SELECT @@transaction_isolation仅反映会话默认隔离级别,不保证当前事务实际生效级别;需结合执行时机(必须在事务开始前)、ORM/连接池行为及并发现象(如幻读、不可重复读)综合验证。

直接查 SELECT @@transaction_isolation 只能告诉你“设了什么”,不能证明“正在用什么”——真正在事务中生效的隔离级别,必须结合执行时机和上下文验证。
为什么 SELECT @@transaction_isolation 有时不准
这个变量返回的是会话初始化时继承或显式设置的默认值,但不反映当前活跃事务的实际行为。比如你执行了 SET TRANSACTION ISOLATION LEVEL READ COMMITTED,但如果是在 BEGIN 之后、第一条 DML 之前执行的,它只对“下一个事务”生效;如果已经处于事务中,MySQL 会静默忽略该语句,且不报错。
- 刚连上就查:返回的是会话级默认值(通常继承自
@@global.transaction_isolation) - 在事务中间查:值没变,但实际事务仍按开启时的级别运行
- ORM 或连接池初始化后查:可能已被框架覆盖,
SELECT @@transaction_isolation看到的是框架设的值,未必是你手动 SET 的
怎么确认当前事务真的用了指定隔离级别
最可靠的方式是构造可观察的并发行为,而不是依赖变量查询。重点看“是否读到未提交数据”“同一事务内两次 SELECT 是否一致”这类现象。
- 先确保两个连接都禁用自动提交:
SET autocommit = OFF - 连接 A 执行
START TRANSACTION,查一次某行数据 - 连接 B 同样
START TRANSACTION,修改该行但不COMMIT - 连接 A 再查一次:若看到 B 的未提交结果 → 实际是
READ-UNCOMMITTED;若看不到 → 至少是READ-COMMITTED起 - 连接 B
COMMIT后,连接 A 再查:若仍看不到新值 → 当前事务级别是REPEATABLE-READ;若立刻看到 → 是READ-COMMITTED
SET SESSION 和 SET TRANSACTION 的生效边界
这两者常被混用,但行为完全不同:
-
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'小写或带空格)不会报错,但 MySQL 会静默 fallback 到默认值REPEATABLE-READ
容易被忽略的环境干扰点
线上验证时,真正影响结果的往往不是 SQL 本身,而是外围机制:
- Django 默认在每次请求开始时执行
SET SESSION TRANSACTION ISOLATION LEVEL ...,你手动 SET 会被覆盖 - HikariCP 连接池复用连接,
SET SESSION的效果随连接生命周期结束而丢失,无法跨请求保持 - MySQL 8.0+ 不再支持
@@tx_isolation,查到Unknown system variable错误说明你用了旧变量名 - 配置文件里写了
transaction-isolation = READ-COMMITTED,但没重启 MySQL,@@global.transaction_isolation仍显示旧值
验证隔离级别本质是验证行为,不是读变量。哪怕 SELECT @@transaction_isolation 返回 'READ-COMMITTED',只要在事务中两次 SELECT 同一行得到不同结果,就说明它真正在起作用;反之,值对了但行为不对,一定是 ORM、连接池或执行时机出了问题。


















