1. MySQL死锁处理机制解析
在数据库并发控制中,死锁是一个经典问题。当两个或多个事务相互持有对方需要的锁资源时,就会形成死锁循环。MySQL的InnoDB引擎采用了一种独特的死锁处理方式,这与Oracle、PostgreSQL等主流数据库有明显区别。
1.1 死锁检测机制
InnoDB使用等待图(wait-for graph)算法来检测死锁。这个算法会周期性地检查事务间的锁等待关系,当发现循环等待时即判定为死锁。检测频率由参数innodb_deadlock_detect控制,默认为开启状态。
死锁检测的具体过程:
- 构建等待图,节点表示事务,边表示锁等待关系
- 深度优先搜索(DFS)遍历等待图
- 发现环时即判定为死锁
- 选择代价最小的事务作为牺牲品(victim)
1.2 事务牺牲品选择策略
InnoDB选择牺牲品时会考虑以下因素:
- 事务已修改的行数(UNDO日志大小)
- 事务的年龄(已执行时间)
- 事务的隔离级别
- 是否显式使用
FOR UPDATE等加锁语句
可以通过设置innodb_deadlock_detect_algorithm来选择不同的检测算法,但通常保持默认值即可。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. MySQL的隐式事务重启机制
2.1 与其他数据库的对比
传统数据库如Oracle在死锁处理上的典型流程:
- 检测到死锁
- 选择牺牲品事务
- 完全回滚该事务
- 向客户端返回错误
- 终止事务连接,后续操作必须显式回滚
而MySQL InnoDB的处理流程:
- 检测到死锁
- 选择牺牲品事务
- 回滚该事务
- 自动开启新事务
- 返回错误信息
这个自动开启新事务的行为是MySQL特有的,也是导致Java开发者困惑的根源。
2.3 事务状态验证实验
我们可以通过以下实验验证MySQL的行为:
会话1:
sql复制BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
-- 持有id=1的行锁
会话2:
sql复制BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE i
