1. 锁表情景:查询条件没有索引时
库存表中,扣库存时的where条件居然不是id而是奖品类型,而刚好是用事务控制的,微信红包还得调用微信发奖,导致锁表(间隙锁),并且这锁住的时间还是很严重的,每次都得等第三方接口调用成功处理完后续逻辑后,事务才会结束,秒杀场景下就导致了大部分用户超时失败的情况了,以下就是复现了当时的情景
总结起来就是两个严重问题:
- 扣库存时没走索引
- 在事务中,调第三方接口
create table gap(
id int,
age int,
primary key(id)
)
select * from gap;
INSERT INTO `test`.`gap`(`id`, `age`) VALUES (1, 13);
INSERT INTO `test`.`gap`(`id`, `age`) VALUES (2, 24);
show engine innodb status;
show status like '%lock%';
show OPEN TABLES where In_use > 0;-- 查询是否锁表,in_use = 1表示加锁,0表示未加锁
show processlist;-- 查询到相对应的进程
-- 查看正在锁的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;
-- 查看等待锁的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;
-- 事务1
start transaction;
update gap set age = 25 where age = 24;
-- 事务2
start transaction;
update gap set age = 14 where age = 13;
-- 这一行会等待,age没有索引时,会导致锁表
-- 查看正在锁的事务,为啥不是锁表而是record呢,明明是锁表呀
-- 19988193:890:3:2 19988193 X RECORD `test`.`gap` PRIMARY 890 3 2 1
-- 19988194:890:3:2 19988194 X RECORD `test`.`gap` PRIMARY 890 3 2 1
2. 解决
通过redis实现库存扣减,而不是通过db层来控制库存,减少db的压力。
3. 参考:
https://www.cnblogs.com/aspirant/p/9177978.html
Gap Lock在InnoDB的唯一作用就是防止其他事务的插入操作,以此防止幻读的发生。
Innodb自动使用间隙锁的条件:
(1)必须在Repeatable Read级别下
(2)检索条件必须有索引(没有索引的话,mysql会全表扫描,那样会锁定整张表所有的记录,包括不存在的记录,此时其他事务不能修改不能删除不能添加)