跳到主要内容

悲观锁介绍

悲观锁

在关系数据库管理系统里,悲观并发控制(又名“悲观锁”,Pessimistic Concurrency Control,缩写PCC)是一种并发控制的方法。它可以阻止一个事务以影响其他用户的方式来修改数据。如果一个事务执行的操作读某行数据应用了锁,那只有当这个事务把锁释放,其他事务才能够执行与该锁冲突的操作。

使用场景

在商品购买场景中,当有多个用户对某个库存有限的商品同时进行下单操作。若采用先查询库存,后减库存的方式进行库存数量的变更,将会导致超卖的产生。

info

若使用悲观锁,当B用户获取到某个商品的库存数据时,用户A则会阻塞,直到B用户完成减库存的整个事务时,A用户才可以获取到商品的库存数据。则可以避免商品被超卖。

如何使用悲观锁

获取锁的前提:结果集中的数据==没有使用排他锁或共享锁时==,才能获取锁,否则将会阻塞。

SELECT ... FOR UPDATE;
SELECT * FROM tb_user WHERE id=1 FOR UPDATE;

需要注意的是,FOR UPDATE 需要同时满足两个条件时才生效

tip
  1. 数据库的存储引擎为InnoDB
  2. 操作位于事务块中(BEGIN/COMMIT)

行锁与表锁

tip

当执行FOR UPDATE时,将会把数据锁住,因此,需要注意锁的级别。InnoDB默认为行级锁。当查询语句指定了主键时,MySQL会执行[行级锁],否则会执行[表锁]

常见情况如下:

caution
  1. 若明确指明主键,且结果集有数据,行锁;
  2. 若明确指明主键,结果集无数据,则无锁;
  3. 若无主键,且非主键字段无索引,则表锁;
  4. 若使用主键但主键不明确,则使用表锁;

小结:

InnoDB的行锁是通过给索引上的索引项加锁实现的,因此只有通过索引检索数据,才会采用行锁,否则使用的是表锁。

总结

悲观锁采用的是「先获取锁再访问」的策略,来保障数据的安全。但是加锁策略,依赖数据库实现,会增加数据库的负担,且会增加死锁的发生几率。此外,对于不会发生变化的只读数据,加锁只会增加额外不必要的负担。在实际的实践中,对于并发很高的场景并不会使用悲观锁,因为当一个事务锁住了数据,那么其他事务都会发生阻塞,会导致大量的事务发生积压拖垮整个系统。

参考文献