InnoDB 所谓“行锁”通常锁的是索引记录或索引范围。SQL 没有合适索引时,扫描范围扩大,锁影响面也可能扩大。死锁无法靠提高超时时间解决,应用必须准备重试。
Table of contents
常见锁术语
| 术语 | 作用 |
|---|---|
| 记录锁 Record Lock | 锁住一条索引记录 |
| 间隙锁 Gap Lock | 锁住两条索引记录之间的范围 |
| 临键锁 Next-Key Lock | 记录锁加前方间隙,用于范围和幻读控制 |
| 意向锁 Intention Lock | 表级标记事务准备在某些记录上加共享/排他锁 |
普通一致性 SELECT 通常不加记录锁;FOR SHARE、FOR UPDATE、UPDATE 和 DELETE 属于锁定读写。锁具体覆盖什么取决于隔离级别、索引唯一性和实际扫描范围。
动手制造一个死锁
打开两个 MySQL 客户端,按照事务实验脚本的 A1、B1、A2、B2 顺序执行。两个事务以相反顺序持有并请求订单 1、2,形成等待环。InnoDB 会选择一个事务回滚,让另一个继续。
随后查询:
SHOW ENGINE INNODB STATUS;
SELECT * FROM performance_schema.data_lock_waits;
死锁日志要读出两个事务、已持有锁、等待锁和相关 SQL,不要只复制最后一条报错。
如何减少死锁
让所有流程按同一顺序锁资源;为条件建立合适索引,缩小扫描与持锁集合;拆掉事务中的远程调用;一次处理适量记录;监控死锁率和事务耗时。即便都做到,数据竞争下仍可能死锁。
应用捕获数据库死锁异常后,应回滚并以有限次数、随机退避重试整个事务。只重试失败的最后一条 SQL,可能丢失前面已回滚的步骤。
参考 InnoDB 锁与事务模型。
下一步
继续阅读06-11 MVCC、Read View 与一致性读,理解读操作为何常常不必阻塞写操作。