MySQLTransactionRollbackException

问题描述

mysql部署1主3从,昨天发现主库有大量报警错误:

Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction

; Deadlock found when trying to get lock; try restarting transaction; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction

定位根因

MySQLTransactionRollbackException是MySQL 处理并发事务时的典型错误。一般有死锁Deadlock和锁等待超时innodb_lock_wait_timeout两种;

当两个或多个事务相互等待对方持有的锁资源时,MySQL 会主动终止其中一个事务以打破僵局,从而抛出该异常,这一过程由 InnoDB 的死锁检测机制自动完成,无需人工干预。

分析死锁日志,定位冲突点:MySQL 会记录死锁详情到错误日志中,可通过以下命令查看最近一次死锁信息:

sql 复制代码
SHOW ENGINE INNODB STATUS;

在输出的 LATEST DETECTED DEADLOCK 部分,能看到:

  • 参与死锁的事务 ID 和 SQL 语句。
  • 事务持有和等待的锁类型(如 X-lock 排他锁)。
  • 涉及的表和行记录(通过 space idpage no 定位)。
sql 复制代码
            LATEST DETECTED DEADLOCK
------------------------
2025-09-11 17:19:22 0x7ee2dbbdf700
*** (1) TRANSACTION:
TRANSACTION 922499728, ACTIVE 0 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 1136, 1 row lock(s)
MySQL thread id 24349657, OS thread handle 139512913655552, query id 4391620199 10.199.99.99 kms_mpmanage_st_pjab updating
/*id:3b7f6356*//*ip=10.199.99.99*/update srunning
    set report_time = 1757582355865,
      is_del = 0,
      graph_version = null,
      service_item = 'aaaaa',
      build_id = 9652,
      report_version = 2,
      config_version = 'config-202508112023',
      scene_sdk_version = '1.3.20',
      table_sdk_version = '2.4.31'
    where pid = 179529542 and report_time < 1757582355865
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 828 page no 8 n bits 200 index uniq_ip_app_scene_s of table `camp`.`srunning` trx id 922499728 lock_mode X locks rec but not gap waiting
*** (2) TRANSACTION:
TRANSACTION 922499723, ACTIVE 0 sec fetching rows
mysql tables in use 1, locked 1
27 lock struct(s), heap size 3520, 35 row lock(s), undo log entries 12
MySQL thread id 24348682, OS thread handle 139512814368512, query id 4391620202 10.199.99.99 kms_mpmanage_st_pjab updating
/*id:389d6f54*//*ip=10.199.99.99*/update srunning
     SET report_time = 1757582353855, is_del = 1 WHERE ( pid = 179529542 and report_time < 1757582353855 )
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 828 page no 8 n bits 200 index uniq_ip_app_scene_s of table `camp`.`srunning` trx id 922499723 lock_mode X locks rec but not gap
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 828 page no 8 n bits 200 index uniq_ip_app_scene_s of table `camp`.`srunning` trx id 922499723 lock_mode X waiting
*** WE ROLL BACK TRANSACTION (1)

两个update更新同一表死锁是两个事务在该表中以不同顺序获取行锁,形成循环等待。

解决和避免

业务涉及事务的4个表都达到了500W+,且事务内同时查询插入或更新4个表,所以多次死锁

统一事务操作顺序

多个事务更新同一批行时,按相反或不同的顺序执行 UPDATE,是单表死锁的最主要原因。

降低锁粒度或使用合理的索引

使用非主键索引、范围条件(>, <, BETWEEN)等,导致 InnoDB 加锁范围扩大(如间隙锁、临键锁),增加交叉等待概率。

缩小事务范围减少锁持有时间

事务执行时间过长,长时间占用锁资源,提高了与其他事务形成循环等待的可能性。

数据表量级对性能有显著影响

  • 小型表(10 万行以内)SELECT、INSERT、UPDATE基本无瓶颈,操作耗时主要受 SQL 语句写法影响
  • 中型表(10 万~1000 万行):SELECT 无索引的全表扫描开始变慢,有索引但索引设计不合理(如低基数索引)时,性能下降明显,复杂查询(多表关联、子查询)耗时增加,可能出现临时表或文件排序。INSERT :单条插入影响不大,但批量插入可能因索引维护(如 B+ 树分裂)耗时增加。若表有多个索引,写入性能下降更明显(每个索引都需更新)。UPDATE:基于非索引字段的更新需要全表扫描,耗时显著增加。高频更新可能导致行锁 / 表锁竞争,出现等待延迟。
  • 大型表(1000 万~1 亿行):需要分库分表、数据归档等拆分

代码层面捕获异常并重试

死锁是临时性异常,重试通常可解决。在代码中捕获 MySQLTransactionRollbackException,并设置合理的重试机制

锁类型

  • lock_type:锁类型,常见值:
    • RECORD:行级锁(针对具体行记录)。
    • TABLE:表级锁(针对整个表)。
  • lock_mode:锁模式,常见值:
    • S:共享锁(读锁,允许其他事务读,不允许写)。
    • X:排他锁(写锁,禁止其他事务读和写)。
    • GAP:间隙锁(锁定索引间隙,防止插入数据)。
    • Next-Key:临键锁(行锁 + 间隙锁的组合,默认的行锁模式)。

行级排他锁,UPDATE/DELETE/INSERT 时自动加锁,阻止其他事务修改该行。

行级共享锁,SELECT ... FOR SHARE 显式加锁,允许其他事务读,但阻止写。

排他间隙锁,锁定索引间隙,防止其他事务插入数据(如 UPDATE ... WHERE id > 10 可能触发)。

临键锁(默认行锁模式),锁定行及前面的间隙,防止幻读(REPEATABLE READ 隔离级别下默认)。

相关推荐
快乐觉主吖7 小时前
adb的常用命令
adb
好名字更能让你们记住我7 小时前
MYSQL数据库初阶 之 MYSQL用户管理
linux·数据库·sql·mysql·adb·数据库开发·数据库架构
哥哥还在IT中7 小时前
mysql分库分表数据量核查问题
数据库·mysql·adb
paid槮7 小时前
MySql基础:数据类型
android·mysql·adb
独行soc17 小时前
2025年渗透测试面试题总结-66(题目+回答)
java·网络·python·安全·web安全·adb·渗透测试
残影飞雪1 天前
如何在 Debian 12 上安装 MySQL
mysql·adb·debian
纤瘦的鲸鱼3 天前
MySQL慢查询
android·adb
Anthony_2313 天前
MySQL的常用命令
运维·数据库·mysql·adb·docker