数据库死锁排查需结合事务日志、SQL执行路径与JVM线程快照:MySQL启用innodb_print_all_deadlocks可记录事务ID、持有锁及等待锁;Spring需检查@Transactional配置、隔离级别与嵌套事务行为;JDBC层用P6Spy捕获真实SQL,配合information_schema.INNODB_TRX和jstack交叉验证锁竞争。

Java 应用中死锁和回滚异常通常不直接暴露在业务代码里,而是通过数据库层面的事务日志(Transaction Log)和 JDBC/框架行为间接体现。排查关键不在 Java 日志本身,而在于结合数据库事务日志、应用事务配置与执行上下文做交叉分析。
看懂数据库事务日志里的死锁信息
MySQL、PostgreSQL、Oracle 等主流数据库都会在错误日志或系统表中记录死锁详情。以 MySQL 为例,启用 innodb_print_all_deadlocks = ON 后,死锁会被完整写入 error log:
- 日志中会明确标出两个(或多个)事务各自的 事务 ID、持有锁、等待锁、SQL 语句
- 重点关注
WAITING FOR THIS LOCK TO BE GRANTED和HOLDS THE LOCK(S)两段内容,能还原加锁顺序 - 结合 Java 应用的 SQL 执行路径(如 MyBatis 的 mapper 方法名、调用栈),反推哪段代码触发了冲突的行锁或间隙锁
从 Spring 事务传播与隔离级别定位回滚根源
很多“无声回滚”其实源于事务配置与异常类型不匹配:
-
@Transactional(rollbackFor = Exception.class)必须显式声明,否则仅对RuntimeException回滚;检查是否漏配导致业务异常未触发回滚 - 嵌套事务中使用
REQUIRES_NEW时,内层事务回滚不影响外层,但外层若再抛异常,可能掩盖原始问题——需查日志中事务 begin/commit/rollback 的嵌套标记 - 隔离级别设为
READ_COMMITTED以上时,幻读或不可重复读可能引发乐观锁失败(如version字段校验失败),表现为OptimisticLockException,本质是逻辑回滚而非 DB 层死锁
用 JDBC 连接属性和代理工具捕获真实 SQL 与事务边界
应用层看到的 SQL 往往被 ORM 封装,需穿透到 JDBC 层确认实际执行行为:
立即学习“Java免费学习笔记(深入)”;
- 开启
spring.datasource.hikari.data-source-properties.useServerPrepStmts=true&cachePrepStmts=true并配合log4jdbc或P6Spy,可打印带事务 ID 的完整 SQL + 参数 + 执行耗时 - 关注日志中同一
connectionId下多条 SQL 是否跨方法、跨线程复用(如手动获取 Connection 却未关闭),易造成事务意外延长甚至阻塞 - 检查是否在事务方法内调用了非事务性远程服务(如 HTTP 调用),导致事务超时(
timeout触发强制 rollback)
结合数据库锁视图与应用线程快照交叉验证
单看日志不够,要实时抓取“正在发生”的锁竞争:
- MySQL 查
SELECT * FROM information_schema.INNODB_TRX和INNODB_LOCK_WAITS,找出长时间运行事务及其阻塞关系 - PostgreSQL 查
pg_stat_activity和pg_locks,过滤state = 'idle in transaction'的悬挂事务 - JVM 层用
jstack -l <pid>抓线程堆栈,搜索getConnection、execute、rollback等关键词,确认线程是否卡在 DB 操作上


















