在商品购买场景中,当有多个用户对某个库存有限的商品同时进行下单操作。若采用先查询库存,后减库存的方式进行库存数量的变更,将会导致超卖的产生
悲观锁要满足才能生效
- 数据库的引擎为 innoDB
- 操作位于事务块中(BEGIN/COMMIT)
如何使用悲观锁
用法:SELECT … FOR UPDATE;
如:
// 开启事务
BEGIN;
//查询SQL
SELECT * FROM t_city for update;
//提交事务
COMMIT;
// 开启事务
BEGIN;
//查询sql
SELECT * FROM t_city where id=222 for update;
//提交事务
COMMIT;
执行完后,窗口2并没有像窗口1一样,立刻返回结果,而是发生了阻塞。
若超时间未获取锁,将会得到一个锁超时错误提示。如上图所示:[Err] 1205 - Lock wait timeout exceeded; try restarting transaction
行锁与表锁
当执行 select ... for update
时,将会把数据锁住,因此,我们需要注意一下锁的级别。MySQL InnoDB 默认为行级锁。当查询语句指定了主键时,MySQL会执行「行级锁」,否则MySQL会执行「表锁」。
常见情况如下:
- 若明确指明主键,且结果集有数据,行锁;
- 若明确指明主键,结果集无数据,则无锁;
- 若无主键,且非主键字段无索引,则表锁;
- 若使用主键但主键不明确,则使用表锁;
- 小结: innoDB的行锁是通过给索引上的索引项加锁实现的,因此,只有通过索引检索数据,才会采用行锁,否则使用的是表锁。