查询死锁日志

  1. show engine innodb status;

建表

  1. CREATE TABLE `test` (
  2. `id` int(11) NOT NULL AUTO_INCREMENT,
  3. `c` int(11) DEFAULT NULL,
  4. `d` int(11) DEFAULT NULL,
  5. PRIMARY KEY (`id`),
  6. KEY `idx_c` (`c`) USING BTREE
  7. ) ENGINE=InnoDB AUTO_INCREMENT=26 DEFAULT CHARSET=utf8;
  8. insert into test values(5,5,5),(10,10,10),(15,15,15),(20,20,20),(25,25,25);

事务自动提交取消设置

  1. set autocommit=0;

案例分析

行锁案例

步骤 事务A 事务B
1 begin;


select from test where id=5 for update; | | | 2 | - | insert into test value(12,12,12);
*(no blocked)
| | 3 | commit; | |

间隙锁简单案例

步骤 事务A 事务B
1 begin;


select from test where id=11 for update; | | | 2 | - | insert into test value(12,12,12);
*(blocked)
| | 3 | commit; | 解除阻塞 |

间隙锁死锁问题

步骤 事务A 事务B
1 begin;


select * from test where id=6 for update; | | | 2 | - | begin;


select from test where id=8 for update; | | 3 | | insert into test values(8,8,8);
(blocked) | | 4 | insert into test values(7,7,7);
(Deadlock found) | 解除阻塞,*(affected row 1)
| | | commit;
数据表不会生成(7,7,7)行记录 | commit;
数据表生成 (8,8,8)的行记录 |

等值查询-唯一索引

索引上的等值查询 - 给唯一索引(已存在行记录)(比如:id=5id=10)加锁的时候,next-key lock升级为行锁。

索引上的等值查询 - next-key lock范围锁(前开后闭:(5,10])向右遍历时最后一个值不满足查询条件时,next-key lock 退化为间隙锁。

步骤 事务A 事务B 事务C
1 begin;


select * from test where id=9 for update;
|
|
| | 2 |
| begin;


insert into test values(8,8,8);
(blocked) |
| | 2 |
|
| begin;


select * from test where id = 10 for update; |

  • 1:加锁的范围是 (5,10]的范围锁;
  • 2:由于是索引的等值查询,并且表中最后的数据 id=10不满足id=9的查询要求,故id=10的行级锁退化为间隙锁:(5,10)
  • 所以事务B中id=8会被锁住,而id=10的时候不会被锁住。

等值查询-普通索引

步骤 事务A 事务B 事务C
1 begin;


select * from test where c=5 lock in share mode;

或者:

begin;


select id from test where c=5 for update;
|
|
| | 2 |
| update test set d=d+1 where id = 5;
(blocked) |
| | 2 |
|
| insert into value(7,7,7);
(blocked) |

步骤 事务A 事务B 事务C
1 begin;


select id from test where c=5 lock in share mode;
|
|
| | 2 |
| update test set d=d+1 where id = 5; |
| | 2 |
|
| insert into value(7,7,7);
(blocked) |

  • 1:加锁范围是 (0,5],(5,10]的范围锁;
  • 2:由于c是普通索引,根据原则4,搜到到的5后继续向后遍历直到搜索到10才放弃,故加锁范围为(5,10];
  • 3:由于查询时等值查询,并且最后一个值不满足查询要求id=5,故间隙锁退化为(5, 10)
  • 4:因为加锁是普通索引c加锁,而且因为索引覆盖(select id),没有对主键进行加锁,所以事务B执行正常;
  • 5:因为加锁范围为(5,10), 故事务C执行阻塞;
  • 6:第二个表格中, lock in share mode因为覆盖索引所以没有锁主键索引,如果使用for update 程序会觉得在之后执行更新操作时,会将主键索引一同锁住。