MySQL中的死锁通常指两个或多个事务互相持有对方需要的锁并等待对方释放,导致相关事务都无法继续。InnoDB存储引擎会自动检测死锁,并回滚其中一个代价较小的事务。理解锁机制和事务行为是避免死锁的第一步。

一、死锁常见原因
大多数死锁来自以下情况:
- 多个事务以不同顺序更新同一批数据行
- 事务过大、持有锁的时间过长
- 查询未走索引,导致锁升级为表级或大量行锁
- 并发插入热点数据引发间隙锁冲突
二、如何避免死锁
1. 固定加锁顺序
所有业务代码对多张表或多行数据的更新,都应约定相同的顺序。例如总是先更新用户表再更新订单表,可显著降低交叉等待。
2. 缩短事务范围
把非数据库操作如远程调用、文件处理移出事务,减少锁持有时间。下面的示例展示了将提交提前以减少锁占用:
-- 不推荐:事务中包含额外逻辑 START TRANSACTION; UPDATE account SET balance = balance - 100 WHERE id = 1; -- 模拟其他耗时操作 UPDATE account SET balance = balance + 100 WHERE id = 2; COMMIT; -- 推荐:尽快提交 START TRANSACTION; UPDATE account SET balance = balance - 100 WHERE id = 1; UPDATE account SET balance = balance + 100 WHERE id = 2; COMMIT;
3. 建立合适索引
使用EXPLAIN检查更新语句是否命中索引。没有索引的更新会锁住更多行,增加死锁概率。
三、监控与调试工具推荐
1. 自带命令
通过SHOW ENGINE INNODB STATUS可以查看最近一次死锁的详细信息,包括等待的锁和执行的SQL。
SHOW ENGINE INNODB STATUS;
也可以查询系统表观察当前锁情况:
SELECT * FROM information_schema.INNODB_TRX; SELECT * FROM information_schema.INNODB_LOCKS; SELECT * FROM information_schema.INNODB_LOCK_WAITS;
2. 错误日志配置
在my.cnf中开启死锁日志,便于事后分析:
[mysqld] innodb_print_all_deadlocks = 1
3. 第三方工具
Percona Toolkit中的pt-deadlock-logger可定时抓取死锁记录到表或文件,适合长期监控。使用方式如下:
pt-deadlock-logger h=127.0.0.1,u=root,p=password
| 工具 | 类型 | 用途 |
|---|---|---|
| SHOW ENGINE INNODB STATUS | 内置命令 | 查看最近死锁详情 |
| INNODB_TRX等系统表 | 内置表 | 实时观察事务与锁等待 |
| pt-deadlock-logger | 第三方 | 持续记录死锁信息 |
四、小结
避免MySQL死锁的核心在于规范事务写法、控制并发冲突,并配合监控工具快速定位。日常开发中养成查看执行计划和锁状态的习惯,可以让系统更加稳定。