Lock wait timeout exceeded; try restarting transaction异常排查

报错信息

2025-08-21 18:07:29,347 ERROR [Telecom-Diversion-Test-Result-update-Thread] c.j.j.e.t.TDTTaskResultUpdateExecutor [TDTTaskResultUpdateExecutor.java : 116] [TDT][UPDATE]执行任务时发生异常
org.springframework.dao.CannotAcquireLockException:
### Error updating database.  Cause: com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transaction
### The error may exist in class path resource [mapper/TelecomUserDiversionChannelReportDetailMapper.xml]
### The error may involve defaultParameterMap
### The error occurred while setting parameters
### SQL: update talbe_name         set report_status = ?         where channel_id = ?         and ext = ?         and sign = ?
### Cause: com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transaction
; Lock wait timeout exceeded; try restarting transaction; nested exception is com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transaction
	at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:267)
	at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:70)
	at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:91)
	at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:441)
	at com.sun.proxy.$Proxy71.update(Unknown Source)
	at org.mybatis.spring.SqlSessionTemplate.update(SqlSessionTemplate.java:288)
	at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:67)
	at org.apache.ibatis.binding.MapperProxy$PlainMethodInvoker.invoke(MapperProxy.java:145)
	at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:86)
	at com.sun.proxy.$Proxy148.updateReportStatusByChannelIdAndExtAndSign(Unknown Source)
	at com.jjxt.job.executor.service.impl.TelecomUserDiversionChannelReportDetailServiceImpl.updateReportStatusByChannelIdAndExt(TelecomUserDiversionChannelReportDetailServiceImpl.java:94)
	at com.jjxt.job.executor.thread.TDTTaskResultUpdateExecutor.selectReportStatusAndUpdate(TDTTaskResultUpdateExecutor.java:230)
	at com.jjxt.job.executor.thread.TDTTaskResultUpdateExecutor.run(TDTTaskResultUpdateExecutor.java:103)
Caused by: com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transaction
	at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:123)
	at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122)
	at com.mysql.cj.jdbc.ClientPreparedStatement.executeInternal(ClientPreparedStatement.java:953)
	at com.mysql.cj.jdbc.ClientPreparedStatement.execute(ClientPreparedStatement.java:371)
	at com.alibaba.druid.filter.FilterChainImpl.preparedStatement_execute(FilterChainImpl.java:3461)
	at com.alibaba.druid.filter.FilterEventAdapter.preparedStatement_execute(FilterEventAdapter.java:440)
	at com.alibaba.druid.filter.FilterChainImpl.preparedStatement_execute(FilterChainImpl.java:3459)
	at com.alibaba.druid.proxy.jdbc.PreparedStatementProxyImpl.execute(PreparedStatementProxyImpl.java:167)
	at com.alibaba.druid.pool.DruidPooledPreparedStatement.execute(DruidPooledPreparedStatement.java:497)
	at org.apache.ibatis.executor.statement.PreparedStatementHandler.update(PreparedStatementHandler.java:47)
	at org.apache.ibatis.executor.statement.RoutingStatementHandler.update(RoutingStatementHandler.java:74)
	at org.apache.ibatis.executor.SimpleExecutor.doUpdate(SimpleExecutor.java:50)
	at org.apache.ibatis.executor.BaseExecutor.update(BaseExecutor.java:117)
	at org.apache.ibatis.executor.CachingExecutor.update(CachingExecutor.java:76)
	at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:194)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:427)
	... 9 common frames omitted

问题描述

  1. 可以肯定的是这个报错是MySQL InnoDB 行锁等待超时,发生在 UPDATE 操作上。

排查方向

  1. 表更新是否太快,且表更新where语句没有索引,导致扫描更多行加更多锁,甚至表锁。
  2. 排查是否有其他事务事务没有释放锁导致这里的更新操作获取锁超时。

问题排查

表的问题排查

  1. 查看是否是表索引问题导致。
  2. 发现没有索引,开始加唯一索引,尝试解决问题。
  3. 加索引的时候发现执行很慢,等了5分钟没执行完,这时候怀疑是否是表锁了。

锁问题排查

1.说明索引创建被阻塞了,不是在“慢慢建”,而是在“等锁”。

SELECT * FROM information_schema.INNODB_TRX ORDER BY trx_started DESC\G;

执行结果:

                    trx_id: 10648548704
                 trx_state: RUNNING
               trx_started: 2025-08-21 15:14:29
     trx_requested_lock_id: NULL
          trx_wait_started: NULL
                trx_weight: 50187
       trx_mysql_thread_id: 1484756
                 trx_query: NULL
       trx_operation_state: NULL
         trx_tables_in_use: 0
         trx_tables_locked: 1
          trx_lock_structs: 2
     trx_lock_memory_bytes: 1136
           trx_rows_locked: 1
         trx_rows_modified: 50185
   trx_concurrency_tickets: 0
       trx_isolation_level: REPEATABLE READ
         trx_unique_checks: 1
    trx_foreign_key_checks: 1
trx_last_foreign_key_error: NULL
 trx_adaptive_hash_latched: 0
 trx_adaptive_hash_timeout: 0
          trx_is_read_only: 0
trx_autocommit_non_locking: 0
  1. 查看结果可知确实有事务没有提交,且锁了将近3个小时,查看trx_query=null说明这个事务 已经执行完 SQL,但没有提交(COMMIT)。
  2. kill掉这个事务
kill trx_mysql_thread_id
  1. 这时候再去加索引很快执行完成。

问题总结

1.主要原因是有事务没有提交导致表锁。
2. 增加唯一索引后,加快sql执行速度。防止lock time out的出现。

在 MySQL 中,`Lock wait timeout exceeded; try restarting transaction` 错误表示一个事务在等待行锁释放时超过了允许的最大等待时间(默认为 50 秒),从而导致事务回滚。该问题通常出现在高并发场景或事务处理不当的情况下,影响系统稳定性和性能。 ### 事务等待锁超时的原因 - **事务未及时提交或回滚**:长事务持有锁资源时间过长,导致其他事务无法获取锁。 - **锁竞争激烈**:多个事务同时请求相同资源,形成锁等待队列。 - **死锁未被及时检测**:虽然 MySQL 有死锁检测机制,但在某些情况下仍可能导致锁等待超时。 - **SQL 执行效率低下**:慢查询导致事务执行时间过长,进而延长锁的持有时间[^1]。 ### 解决方案 #### 1. 重启事务 当发生锁等待超时时,建议按照提示重启事务。这可以释放当前事务的锁请求,避免系统陷入长时间等待。重启后,事务会重新尝试获取所需锁资源[^1]。 #### 2. 优化事务管理 - **缩短事务执行时间**:将事务中不必要的操作移出事务范围,减少锁的持有时间。 - **使用合适的事务隔离级别**:避免使用过高的隔离级别(如可重复读)导致不必要的锁范围扩大。 - **避免跨事务操作**:确保事务逻辑清晰,不将多个业务逻辑混杂在一个事务中[^1]。 #### 3. 调整锁等待超时设置 可以通过修改 `innodb_lock_wait_timeout` 参数调整等待锁的最大时间,默认为 50 秒。例如: ```sql SET innodb_lock_wait_timeout = 120; ``` 此设置可临时缓解问题,但不应作为长期解决方案,因为可能掩盖了设计上的缺陷。 #### 4. 分析并优化锁竞争 使用以下语句查看当前的锁等待情况: ```sql SELECT * FROM information_schema.INNODB_LOCKS; SELECT * FROM information_schema.INNODB_LOCK_WAITS; ``` 这些查询可以帮助识别哪些事务正在等待锁,以及哪些事务持有锁,从而进行针对性优化。 #### 5. 查找并终止持有锁的操作 通过以下语句查找正在运行的事务: ```sql SELECT * FROM information_schema.PROCESSLIST WHERE Command != 'Sleep'; ``` 找到持有锁的线程 ID 后,可以使用 `KILL` 命令终止该线程: ```sql KILL thread_id; ``` 此操作需谨慎,避免影响其他正常业务[^1]。 #### 6. 优化 SQL 语句 - **添加合适的索引**:确保查询能够命中索引,减少全表扫描带来的锁范围。 - **避免不必要的更新**:仅更新真正需要变更的字段和行。 - **使用 `FOR UPDATE` 或 `LOCK IN SHARE MODE` 时谨慎**:避免在事务中长时间持有锁。 ### 预防措施 - **定期监控锁等待情况**:设置监控指标,及时发现锁等待异常。 - **优化数据库设计**:合理设计表结构和索引,减少锁竞争。 - **使用连接池**:避免连接泄漏,减少因连接未释放导致的锁等待。 - **设置事务超时机制**:在应用层设置事务超时,避免事务长时间挂起。 ### 相关问题 1. 如何查看 MySQL 中当前的锁等待情况? 2. 事务未提交导致锁等待超时,应如何排查? 3. 如何优化 SQL 语句以减少锁竞争? 4. 为什么长事务容易引发锁等待超时问题? 5. `innodb_lock_wait_timeout` 参数的作用是什么?如何合理设置?
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值