select @@transaction_isolation仅反映会话默认隔离级别,非当前事务实际生效级别;必须通过并发实验(如观察未提交/已提交数据可见性)验证真实行为。

SELECT @@transaction_isolation 返回的只是会话默认值,不是当前事务实际生效的级别——它可能早已被忽略、覆盖或滞后于真实行为。
为什么 SELECT @@transaction_isolation 不可靠
这个语句查的是“会话初始化时设的默认值”,不是“当前事务正在用的级别”。常见误导场景:
- 你在
BEGIN之后执行了SET TRANSACTION ISOLATION LEVEL READ COMMITTED→ MySQL 静默忽略,不报错,但下个事务才生效 - ORM(如 Django)在连接建立后自动执行
SET SESSION TRANSACTION ISOLATION LEVEL ...→ 你手动查到的值是框架设的,不是你预期的 - 连接池复用连接,上一个请求改过隔离级别,新请求没重置 →
@@transaction_isolation显示旧值,但事务已按旧设置运行 - 配置文件里写了
transaction-isolation = READ-COMMITTED,但 MySQL 启动时没加载该配置 → 全局值仍是默认REPEATABLE-READ,而你只查会话级,发现不了
真正生效的验证必须靠并发行为
隔离级别最终体现在“能否读到别人未提交/已提交/已修改的数据”上。验证必须构造两个并发连接,并控制事务生命周期:
- 两个连接都先执行
SET autocommit = OFF(禁用自动提交) - 连接 A:执行
START TRANSACTION,查一次某行(记为 V1) - 连接 B:执行
START TRANSACTION,更新同一行但不COMMIT - 连接 A 再查同一行:
- 若看到 B 的未提交值 → 实际是
READ UNCOMMITTED - 若看不到 → 至少是
READ COMMITTED起
- 若看到 B 的未提交值 → 实际是
- 连接 B 执行
COMMIT,连接 A 再查:- 若仍看不到新值 → 当前事务级别是
REPEATABLE READ - 若立刻看到 → 是
READ COMMITTED
- 若仍看不到新值 → 当前事务级别是
SET TRANSACTION 和 SET SESSION 的生效时机差异
这两条命令看似相似,但作用范围和触发点完全不同:
-
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED:立即修改会话默认值,影响后续所有新事务;可在事务内外执行 -
SET TRANSACTION ISOLATION LEVEL READ COMMITTED:只对“下一个BEGIN开启的事务”生效,且**必须在事务外执行**;若已在事务中执行,MySQL 报错ERROR 1568 (25001): Transaction characteristics can't be changed while a transaction is in progress - 写错格式(如
'read-committed'少了LEVEL,或用了空格而非短横线)不会报错,MySQL 会静默 fallback 到REPEATABLE READ
线上环境最容易被绕过的干扰点
你以为改了隔离级别,其实根本没生效——往往卡在这些地方:
- Django 默认每个请求开头执行
SET SESSION TRANSACTION ISOLATION LEVEL ...,你手动 SET 的值在中间就被覆盖 - 连接池(如 HikariCP、Druid)可能在归还连接时重置会话变量,也可能不重置,行为取决于配置
- MySQL 8.0.28+ 引入了
transaction_isolation系统变量的只读代理模式,某些托管服务(如 AWS RDS)会拦截并重写你的 SET 语句 - 使用
mysql -e "..."一次性执行命令时,连接立即断开,SET没机会生效到后续查询
真正难的不是语法,而是确认「那个正在跑的事务」到底按什么规则读数据——它不看你设了什么,只看你 BEGIN 的那一刻,系统给它分配了哪个 Read View。











