事务提交失败主因是死锁、状态异常、DDL隐式提交、网络假成功四类,需重试+幂等校验+索引优化+DDL隔离+连接池健壮配置。

事务提交时抛出 Deadlock found when trying to get lock
这是最典型的提交失败场景,不是代码写错了,而是多个事务同时争抢同一组行锁或间隙锁,MySQL 自动回滚了其中一方。你看到的错误里会明确带这串提示,SHOW ENGINE INNODB STATUS 的输出里也能查到最近死锁详情。
- 应用层必须捕获这个错误并重试(最多 2–3 次),不能直接向上抛异常
- 重试前加
sleep(0.1)避免立即再次冲突 - 检查事务是否过长:把非必要查询、日志打印、HTTP 调用等移出事务块
- 确保 UPDATE/DELETE 语句都走索引——全表扫描会锁整张表,极大提高死锁概率
ROLLBACK 后再执行 COMMIT 报错 ERROR 1370 (42000): execute command denied
这不是权限问题,而是事务状态已终结却误操作。MySQL 中一旦发生隐式或显式 ROLLBACK,当前事务就彻底关闭,此时再调 COMMIT 会触发权限校验绕过机制,报这个看似无关的错误。
- 检查是否在异常分支里写了
conn.rollback(); conn.commit();这类重复提交逻辑 - 使用 try/finally 时,避免在
finally块里无条件commit(),应先判断conn.getAutoCommit() == false且事务未结束 - 推荐用连接池的事务模板(如 Spring 的
@Transactional)代替手写 begin/commit/rollback
事务中执行 DDL 导致自动提交(ALTER TABLE 提交了前面的 DML)
MySQL 在事务中遇到 CREATE、DROP、ALTER、TRUNCATE 等 DDL 语句时,会**隐式提交当前事务**,然后执行 DDL,再开启新事务。这意味着你前面的 INSERT 或 UPDATE 已经落地,无法回滚。
- DDL 操作一律放在事务外单独执行;如必须与业务逻辑联动,改用“先建临时表 + INSERT SELECT + RENAME”等可逆方案
- 开发阶段开启
sql_log_bin=OFF和autocommit=0并不改变 DDL 的自动提交行为 - 监控慢查询日志时注意
ALTER是否夹在业务事务中间——这类日志里会出现连续两个Query时间戳紧挨着但事务 ID 不同
连接超时或网络中断后,COMMIT 返回成功但实际未生效
客户端收到 MySQL server has gone away 或连接重置后,仍尝试发送 COMMIT 包,此时 MySQL 可能已关闭该连接上下文,返回虚假的 OK 包(尤其在低版本或启用了 skip-networking 时)。
- 永远不要信任“网络层返回成功”就等于数据持久化——关键业务需加幂等校验(例如插入后立刻
SELECT FOR UPDATE确认) - 设置合理的
wait_timeout和interactive_timeout(建议 300–600 秒),配合连接池的testOnBorrow或validationQuery=SELECT 1 - Java 中用
HikariCP时务必配置connection-test-query=SELECT 1和connection-timeout=3000
-- 示例:安全的事务重试逻辑(Python + PyMySQL)
def safe_update_user_balance(user_id, amount):
for i in range(3):
try:
conn = get_db_connection()
conn.begin()
with conn.cursor() as cur:
cur.execute("UPDATE users SET balance = balance + %s WHERE id = %s", (amount, user_id))
if cur.rowcount == 0:
raise ValueError("user not found")
conn.commit()
return True
except pymysql.err.InternalError as e:
if "Deadlock" in str(e):
time.sleep(0.1 * (2 ** i)) # 指数退避
continue
else:
conn.rollback()
raise
except Exception:
conn.rollback()
raise
finally:
conn.close()
事务提交失败从来不是单点问题,它暴露的是锁设计、SQL 写法、连接管理、甚至网络拓扑的真实约束。最容易被忽略的,是把「数据库返回 OK」当作「业务成功」——而真正的确认,永远需要一次独立的、带一致读的验证查询。










