MySQL 事务没有提交导致 锁等待 Lock wait timeout exceeded

本文介绍了一种解决MySQL中因长时间未提交的只读事务导致的Lockwaittimeoutexceeded问题的方法。通过查询information_schema.innodb_trx表找到未提交的事务,并使用KILL命令终止对应的线程。

转自:http://blog.youkuaiyun.com/tanggao1314/article/details/53097561


Lock wait timeout exceeded

当一个SQL执行完了,但未COMMIT,后

select * from information_schema.innodb_trx 之后找到了一个一直没有提交的只读事务,

kill 到了对应的线程后ok 了。



MySQL> select * from information_schema.innodb_trx;

+--------+-----------+---------------------+-----------------------+------------------+------------+---------------------+-----------+---------------------+-------------------+-------------------+------------------+-----------------------+-----------------+-------------------+-------------------------+---------------------+-------------------+------------------------+----------------------------+---------------------------+---------------------------+
| trx_id | trx_state | trx_started         | trx_requested_lock_id | trx_wait_started | trx_weight | trx_mysql_thread_id | trx_query | trx_operation_state | trx_tables_in_use | trx_tables_locked | trx_lock_structs | trx_lock_memory_bytes | trx_rows_locked | trx_rows_modified | trx_concurrency_tickets | trx_isolation_level | trx_unique_checks | trx_foreign_key_checks | trx_last_foreign_key_error | trx_adaptive_hash_latched | trx_adaptive_hash_timeout |
+--------+-----------+---------------------+-----------------------+------------------+------------+---------------------+-----------+---------------------+-------------------+-------------------+------------------+-----------------------+-----------------+-------------------+-------------------------+---------------------+-------------------+------------------------+----------------------------+---------------------------+---------------------------+
| F663B  | RUNNING   | 2016-11-09 10:15:52 | NULL                  | NULL             |         10 |               14310 | NULL      | NULL                |                 0 |                 0 |                7 |                  1248 |               4 |                 3 |                       0 | REPEATABLE READ     |                 1 |                      1 | NULL                       |                         0 |                      9999 |
+--------+-----------+---------------------+-----------------------+------------------+------------+---------------------+-----------+---------------------+-------------------+-------------------+------------------+-----------------------+-----------------+-------------------+-------------------------+---------------------+-------------------+------------------------+----------------------------+---------------------------+---------------------------+
1 row in set


mysql> kill 14310 ;
Query OK, 0 rows affected


mysql> 
<think>嗯,用户让我分析一段带有@Transactional注解的Spring事务方法导致MySQL等待超时的原因。用户提供的代码片段是@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)修饰的effectiveBasis方法。 首先注意到这个事务传播级别是REQUIRES_NEW,这个很关键。根据之前提供的引用资料,这个级别会挂起当前事务(如果存在)并创建新事务。这意味着每次调用这个方法都会启动一个全新的独立事务等待超时通常发生在多个事务竞争同一资源时。考虑到REQUIRES_NEW的特性,可能的场景是:当外部方法已经开启事务并持有某些时,内部调用effectiveBasis方法会启动新事务,这个新事务尝试获取被外部事务住的资源。由于外部事务提交,新事务只能等待,如果等待时间超过MySQL的innodb_lock_wait_timeout设置(默认50秒),就会抛出这个异常。 用户代码中的rollbackFor = Exception.class说明它对所有异常都回滚,这可能会扩大事务范围。结合方法名effectiveBasis,推测可能是产品基础信息生效业务,这类操作往往涉及核心数据表,容易产生长时间。 具体到实现层面,需要排查几个点:这个方法内部是否操作了被外部事务住的表数据;是否包含耗时操作导致事务时间过长;是否在循环中高频调用。另外MySQL事务隔离级别也很重要,比如REPEATABLE_READ会比READ_COMMITTED产生更多间隙。 特别要注意REQUIRES_NEW可能造成的死链:外部事务T1住记录A→调用effectiveBasis开启T2→T2等待A时,T1又需要T2持有的B。这种嵌套事务竞争比单事务更复杂。 用户附带引用了两个看似无关的内容(Spring传播属性和Boost异常),可能是在其他调试过程中遇到的。不过当前问题聚焦在数据库事务,可以暂不关注Boost相关引用。 最后想到,如果这个方法在集群环境下被并发调用,不同节点的分布式竞争也可能表现为本地超时。不过用户异常类型是MySQL原生错误,应优先排查单机事务问题。 </think>分析代码 `@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)` 引发 `MySQLTransactionRollbackException: Lock wait timeout exceeded` 的常见原因如下: ### 主要原因分析 1. **事务传播行为冲突** - `Propagation.REQUIRES_NEW` 会**挂起当前事务并创建新事务**。若外部事务定资源(如表行、间隙),新事务尝试访问相同资源时会被阻塞。 - 当新事务等待的时间超过 MySQL 的 `innodb_lock_wait_timeout`(默认50秒),触发超时异常[^1]。 *公式示例:事务等待时间 $T_{\text{wait}} > T_{\text{timeout}}$* 2. **嵌套事务竞争** ```java // 外部事务定资源A) @Transactional public void outerMethod() { lockResourceA(); // 持有A effectiveBasis("P123"); // 内部开启独立事务 } // 内部独立事务(尝试访问资源A) @Transactional(propagation = Propagation.REQUIRES_NEW) public void effectiveBasis(String productId) { updateResourceA(); // 需要A → 等待外部事务释放 } ``` - 外部事务提交前,内部事务等待超时而回滚。 3. **长事务阻塞** - `effectiveBasis` 方法内若有复杂操作(如大批量数据处理、循环更新),导致事务执行时间过长。 - 其他并发事务访问相同数据时被阻塞,超过等待阈值。 4. **高频并发调用** - 若多个线程同时调用 `effectiveBasis`,且操作相同数据范围(如相同 `productId`),可能形成竞争链: $$T_1 \xrightarrow{\text{资源R}} T_2 \xrightarrow{\text{等待}} T_3 \xrightarrow{\text{等待}} \cdots \xrightarrow{\text{超时}} \text{Rollback}$$ 5. **优化的 SQL 操作** - 方法内 SQL 缺少索引,导致升级为**表**。 - `SELECT ... FOR UPDATE` 范围过大,定过多行。 --- ### 解决方案建议 1. **调整事务传播级别** - 评估是否必须使用 `REQUIRES_NEW`。若允许加入外部事务,改用 `REQUIRED` 可避免分离。 2. **缩短事务执行时间** - 将非核心操作(如日志记录)移出事务: ```java @Transactional(propagation = Propagation.NOT_SUPPORTED) // 无事务执行 public void logOperation() {...} ``` 3. **优化数据访问逻辑** - 为 WHERE 条件字段添加索引,减少定范围。 - 分解大批量操作:分页更新代替全表更新。 4. **降低粒度** - 使用 `SELECT ... FOR UPDATE SKIP LOCKED`(MySQL 8.0+)跳过已定行。 - 改用乐观(版本号控制)减少阻塞。 5. **调整 MySQL 参数(临时方案)** - 增大 `innodb_lock_wait_timeout`(需评估风险)。 - 监控等待:`SHOW ENGINE INNODB STATUS`。 --- ### 关键排查步骤 1. 检查是否在循环中调用 `effectiveBasis` 2. 使用 MySQL 命令 `SELECT * FROM information_schema.INNODB_TRX` 查看运行中事务 3. 分析慢查询日志,定位 SQL 瓶颈 4. 验证事务是否涉及跨表更新导致
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值