01.mysql中的锁
1.1 锁分类
-
按使用方式划分
:乐观锁、悲观锁 -
按锁级别划分
:共享锁、排他锁 -
按锁的粒度划分
:表级锁、行级锁、页级锁 -
按操作划分:DML锁,DDL锁
-
按加锁方式划分:自动锁、显示锁
1.2 共享锁和排它锁
-
共享锁(
读锁
)- 共享锁又叫读锁,如果事务T对A加上共享锁,则其它事务只能对A再加共享锁,不能加其它锁。
- 获准共享锁的事务只能读数据,不能写数据。
- 用法: SELECT … LOCK IN SHARE MODE;
-
排它锁(
写锁
)- 排它锁又叫写锁,如果事务T对A加上排它锁,则其它事务都不能对A加任何类型的锁。获准排它锁的事务既能读数据,又能写数据。
- 用法 : SELECT … FOR UPDATE
1.3 乐观锁和悲观锁
2.3.1 悲观锁:写少读多
悲观锁(Pessimistic Lock)
-
定义:
悲观锁假设会发生冲突,因此在读取数据时就加锁,直到操作完成才释放锁,确保只有一个事务能够对数据进行操作。悲观锁会直接阻塞其他事务的访问,直到当前事务完成。 -
实现方式:
1.使用数据库的锁机制,例如 SELECT … FOR UPDATE 来锁定数据行。
2.在事务开始时,锁定目标记录,其他事务必须等待当前事务提交或回滚后才能操作被锁定的数据。 -
使用场景:
1.高并发系统: 当并发较高时,使用悲观锁可以避免数据冲突,通过加锁保证事务的顺序性。
2.关键性的数据更新: 例如库存扣减等操作,确保数据的一致性和准确性。
3.长时间操作的事务: 当事务需要进行复杂的操作且无法快速完成时,使用悲观锁可以防止其他事务对数据的更改,避免脏读或丢失更新。
1 | #### 悲观锁实现加一操作代码 |
2.3.2 乐观锁:读多写少
乐观锁(Optimistic Lock)
- 定义:
乐观锁假设不会发生冲突,因此在读取数据时不加锁,只有在提交数据时才会进行冲突检查。通常通过版本号或时间戳来检测是否发生了并发修改。
实现方式:
1.在数据表中添加一个版本号(version)或时间戳字段。
2.在读取数据时,客户端读取数据的同时也会读取这个版本号或时间戳。
3.在更新数据时,客户端会将数据提交到数据库,并同时检查提交时的版本号或时间戳是否和读取时一致。如果一致,表示没有其他事务修改该数据,更新成功;否则,更新失败,需要重新读取数据并再次提交。
使用场景:
- 低并发系统: 在高并发情况下,乐观锁的性能较差,因为冲突的概率增加,但在低并发下非常适合使用。
- 操作不频繁的资源: 当数据更新的频率不高时,乐观锁能够有效减少数据库的锁竞争。
- 非关键性的数据: 数据冲突后可以容忍重试的场景,如某些临时数据的处理。
1 | #### 乐观锁实现加一操作代码 |
1.4 行级锁 & 表级锁
- 行级锁(写多读少)开销大,加锁慢,锁定粒度最小,发生锁冲突概率最低,并发度最高
- 表级锁(适合大量的读操作)开销小,加锁快,锁定粒度大、发生锁冲突最高,并发度最低
__END__