由MySQL加锁机制引发的死锁案例分析
1、死锁案例
--建表 CREATE TABLE t1( `id` int(11) NOT NULL, `value` int(11) NOT NULL PRIMARY KEY (`id`), KEY `idx_value` (`value`) ); --插入初始数据 insert into t1 values(1,1); insert into t1 values(2,2); insert into t1 values(3,3); --事务1 select count(*) from t1 where value=1 for update; --事务2 update t1 set value=2 where id=1;
在高并发场景下事务1和事务2会发生死锁。
2、MySQL加锁机制
首先需要知道的是,MySQL的行锁是对索引项加锁,可重复读隔离级别下具体的加锁方式如下:
NextKey锁即行锁+间隙锁,用于锁定一个范围,主要用于解决幻读的问题
3、分析
--建表 CREATE TABLE t1( `id` int(11) NOT NULL, `value` int(11) NOT NULL PRIMARY KEY (`id`), KEY `idx_value` (`value`) ); --插入初始数据 insert into t1 values(1,1); insert into t1 values(2,2); insert into t1 values(3,3); --事务1 select count(*) from t1 where value=1 for update; --事务2 update t1 set value=2 where id=1;
- 如果不了解MySQL具体的加锁机制(加锁顺序及加锁对象),我们可能会错误地认为事务1和事务2都是对 id=1 这行进行了加锁,即只存在对单个资源的竞争,两个事务会发生锁等待,但不会发生死锁。
- 但是如果考虑到MySQL的加锁顺序及加锁对象,我们可以看到: 事务1会先对 value=1 的索引项加锁,再对 id=1 的主键索引项加锁; 事务2会先对 id=1 的主键索引项加锁,之后修改value的索引需要获取value=1的索引项的锁。
- 这样就很容易看出来,在高并发情况下,事务1和事务2对 id=1 的主键索引项、value=1 的索引项这两个资源产生了竞争,可能会产生循环等待,即产生死锁。
- 分析的过程不复杂,但会因为知识储备存在漏洞,而解不出来;并且case不好手动触发,只有高并发场景才能复现。