死锁是多个事务因争夺资源而互相等待无法推进的状态;InnoDB自动检测并回滚代价最小事务,核心原因是加锁顺序不一致与循环等待,需同时满足互斥、持有并请求、不可剥夺、循环等待四条件。
死锁是指两个或多个事务在执行过程中,因争夺资源而互相等待,导致所有相关事务都无法继续推进的状态。MySQL(特别是 InnoDB 引擎)中一旦发生死锁,系统会自动检测并强制回滚其中一个事务(通常选择代价最小的那个),抛出错误:ERROR 1213 (40001): Deadlock found when trying to get lock。
本质是“加锁顺序不一致 + 循环等待”。InnoDB 死锁必须同时满足四个条件:互斥、持有并请求、不可剥夺、循环等待。实际开发中最常见、最可控的诱因有以下几类:
UPDATE ... WHERE name = 'xxx',InnoDB 会先锁 name 索引项,再通过回表锁主键。若另一事务已持有该主键行的 X 锁,并反向尝试锁 name 索引,就可能死锁。WHERE age BETWEEN 20 AND 30)会锁定索引间隙。多个事务对重叠间隙加锁,且顺序错乱时,容易触发死锁,尤其在可重复读(RR)隔离级别下更常见。明确一点:表级锁(如 MyISAM 使用的锁)本身不支持事务,也没有死锁检测机制,所以不会产生死锁。而 InnoDB 的行级锁机制天然具备死锁可能性——这也正是它支持高并发事务的代价之一。
假设有用户表 users(id, name, balance),其中 name 有普通索引:
行:UPDATE users SET balance = balance + 100 WHERE name = 'Alice';
UPDATE users SET balance = balance - 50 WHERE id = 1001;
name='Alice' 索引锁,正准备回表锁主键 id=1001;而事务 B 已持有 id=1001 的 X 锁,又试图加 name 索引上的锁(例如执行了 SELECT ... FOR UPDATE 带 name 条件),此时就会触发死锁。