MySQL升级后事务异常主因是默认配置变更,需重点排查:1.隔离级别默认值(5.7为RR,8.0.22+可能为RC);2.RC下一致性读更严格(每SELECT用最新快照);3.autocommit下DDL隐式提交更严格;4.sql_mode严格化导致警告变错误。

MySQL 升级后事务行为出现异常,通常不是“bug”,而是版本间默认配置、隔离级别实现或 SQL 模式调整导致的预期变化。重点排查 事务隔离级别默认值、READ-COMMITTED 下的一致性读行为、autocommit 默认状态、SQL_MODE 对事务语义的影响 这四类核心差异。
隔离级别默认值变了(尤其 5.7 → 8.0)
MySQL 5.7 默认隔离级别是 REPEATABLE-READ,而 MySQL 8.0.22+ 在启用 transaction_isolation 系统变量(替代旧的 tx_isolation)后,若未显式配置,可能受启动参数或配置文件影响,部分云厂商镜像甚至默认设为 READ-COMMITTED。
- 执行
SELECT @@transaction_isolation;确认当前会话/全局实际值 - 检查
my.cnf是否有transaction_isolation = READ-COMMITTED或类似配置 - 应用中若依赖 RR 的可重复读特性(如多次 SELECT 同一数据不变化),升级后变 RC 就会看到“幻读”或“不可重复读”现象
READ-COMMITTED 下一致性读逻辑更严格(8.0 改进)
MySQL 8.0 对 RC 隔离级别的 MVCC 实现做了优化:每个 SELECT 语句都基于最新已提交快照,而非事务开始时的快照。这导致同一事务内多次 SELECT 可能返回不同结果——这是标准 SQL 行为,但老版本(尤其 5.6)因实现限制反而“看起来更稳定”。
- 不要在 RC 下假设“事务内 SELECT 结果不变”;需强一致性场景应显式用
SELECT ... FOR UPDATE或升至 RR - 避免在 RC 中写“先查后更”的业务逻辑(如查余额→判断→扣减),改用原子更新:
UPDATE account SET balance = balance - ? WHERE id = ? AND balance >= ?
autocommit 默认开启,但隐式提交规则更明确
所有 MySQL 版本默认 autocommit = 1,但升级后对 DDL 语句触发隐式提交 的处理更严格。例如,在事务中执行 CREATE TABLE 或 ALTER TABLE,8.0 会立即提交当前事务并清空保存点,而 5.6/5.7 可能允许部分 DDL 在事务块内(非标准且已被弃用)。
- 检查代码中是否在 BEGIN … COMMIT 块内混用 DDL —— 升级后会导致事务提前结束、后续 DML 不再受控
- 将 DDL 和 DML 分开执行,DDL 单独成事务,DML 显式控制
- 用
SHOW VARIABLES LIKE 'completion_type';确认是否启用了NO_CHAIN或CHAIN,它会影响 COMMIT 后的行为
SQL_MODE 影响事务边界(尤其 STRICT_TRANS_TABLES / ONLY_FULL_GROUP_BY)
MySQL 8.0 默认启用更严格的 sql_mode(含 STRICT_TRANS_TABLES)。当 INSERT/UPDATE 出现警告(如截断字符串、零日期)时,严格模式会让语句直接报错并回滚当前语句——若在事务中,可能打断预期流程,甚至让应用误判为“整个事务失败”。
- 执行
SELECT @@sql_mode;对比新旧环境差异 - 若应用依赖“警告即成功”,可临时调整为兼容模式(不推荐长期使用):
SET GLOBAL sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,...';去掉 STRICT* - 更稳妥做法:在应用层捕获 SQL 错误码(如 1265、1366),区分警告与错误,避免把警告当致命异常
不复杂但容易忽略。升级前做事务路径回归测试,重点覆盖“查-判-改”、并发更新、DDL 混用、空值/边界值写入这几类典型场景。










