MySQL多连接事务锁异常:connB绕过connA行锁提交更新
MySQL SELECT ... FOR UPDATE 行锁未阻塞跨连接更新的问题排查与解决
问题核心
InnoDB表中,connA通过SELECT ... FOR UPDATE锁定目标行后,connB的UPDATE操作未按预期被阻塞,反而直接执行成功;命令行测试锁机制正常,但NodeJS/Python代码中复现该问题,同时伴随连接关闭错误。
可能原因分析
MySQL连接超时自动断开
MySQL的wait_timeout或interactive_timeout参数设置过小,当connA持有锁但长时间无后续操作时,MySQL会主动断开该连接,行锁随之释放,导致connB的UPDATE可以直接执行。实际输出中的got packets out of order警告,正是连接被MySQL关闭后客户端仍尝试操作的典型表现。客户端驱动超时设置
部分数据库驱动(如mysql2/promise、Python的pymysql)默认带有连接超时配置,若超时时间短于事务持有时间,客户端会主动关闭连接,同样导致锁提前释放。
解决方案
1. 调整MySQL服务器超时参数
- 查看当前超时设置:
重点关注SHOW VARIABLES LIKE '%timeout%';wait_timeout和interactive_timeout,若值小于事务持有时间(如10秒以内),就会触发问题。 - 修改为合适时长(例如300秒):
若要持久化配置,需在SET GLOBAL wait_timeout = 300; SET GLOBAL interactive_timeout = 300;my.cnf(或my.ini)中添加:
修改后重启MySQL服务生效。[mysqld] wait_timeout = 300 interactive_timeout = 300
2. 客户端保持连接活跃
在事务持有期间,定期向MySQL发送心跳查询,避免连接因无操作被断开。例如修改connA的代码,将定时器替换为异步等待并插入心跳:
// 替换原setTimeout部分 console.log("A: Holding transaction (sleeping 15s)"); for (let i = 0; i < 15; i++) { await sleep(1000); // 发送心跳维持连接 await connA.query("SELECT 1"); } console.log("A: Rolling back transaction"); await connA.rollback(); await connA.end();
3. 检查客户端驱动超时配置
确保客户端连接参数中没有设置过短的超时时间。例如mysql2/promise的连接配置中,调整超时参数大于事务持有时间:
let db_details = { host: 'localhost', user: 'root', password: 'password', database: 'dbname', timeout: 300000, // 设置为300秒 connectTimeout: 10000 // 连接超时保持合理值即可 }
验证方法
调整参数后重新运行测试代码,观察connB的UPDATE操作是否会被阻塞,直到connA执行回滚后才继续执行;同时检查是否再出现连接关闭的错误提示。
内容的提问来源于stack exchange,提问作者jmc
相关产品推荐
相关产品推荐

