mysql事务提交失败怎么办_mysql异常处理方法

来源:这里教程网 时间:2026-02-28 20:44:16 作者:

事务提交时抛出
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」当作「业务成功」——而真正的确认,永远需要一次独立的、带一致读的验证查询。

相关推荐