mysql锁等待超时后事务不自动回滚,是因为innodb仅中断当前阻塞语句而非整个事务,事务仍处于active状态(select @@in_transaction返回1),需应用层显式rollback或commit;innodb_lock_wait_timeout仅控制行锁等待最长时间,不终止事务本身,也不释放已持锁。

MySQL锁等待超时后事务不自动回滚,是因为InnoDB只中断当前阻塞语句,而非整个事务。这是设计使然,不是bug。
innodb_lock_wait_timeout 作用范围仅限单条语句
该参数控制的是「等待行锁释放」的最长时间,不是事务生命周期限制。一旦超时,MySQL会立即终止当前UPDATE、DELETE或SELECT FOR UPDATE等需要加锁的语句,并抛出ERROR 1205 (40001): Lock wait timeout exceeded。但事务本身仍处于ACTIVE状态——SELECT @@in_transaction返回1,后续语句仍可执行(直到你显式提交或回滚)。
- 它不触发
ROLLBACK,也不释放已持有的锁(比如前面INSERT拿到的间隙锁) - 它和
wait_timeout(连接空闲超时)、max_execution_time(查询执行时间限制)完全无关 - 死锁检测(
innodb_deadlock_detect=ON)是另一套机制,触发时会主动回滚其中一个事务,错误码为1213,与1205不同
应用层忽略错误继续执行会出什么问题
如果代码捕获到Lock wait timeout exceeded但没调用conn.rollback(),而是直接执行下一条SQL,后果取决于驱动行为:
- JDBC默认不自动回滚,继续执行
INSERT可能报ERROR 1305 (42000): SAVEPOINT does not exist - PyMySQL在未rollback时发新语句,部分版本会静默失败,数据状态不可知
- 更危险的是:事务里已有部分写入未提交,此时再
COMMIT,会导致部分更新被提交,破坏业务原子性 - 连接池中该连接可能被复用,残留事务状态污染后续请求
如何安全地处理锁超时异常
关键不是延长innodb_lock_wait_timeout,而是让应用能识别并清理现场:
- 必须同时检查错误码
1205和错误消息是否含Lock wait timeout exceeded(MySQL 8.0+统一为1205,但旧版本可能有变体) - 在catch块里第一时间调用
conn.rollback(),不要尝试“重试当前语句”而不先清理 - 避免在事务中动态改
SET SESSION innodb_lock_wait_timeout——会报错ERROR 1305 - 若需差异化超时控制,应在开启事务前设置:
SET SESSION innodb_lock_wait_timeout = 10,再BEGIN
真正容易被忽略的点是:事务状态不会因锁超时而重置,它只是“卡住了一条语句”。你得亲手把它拽出来,而不是指望数据库替你善后。











