MySQL默认隔离级别是REPEATABLE-READ,最稳妥;查当前会话级需用SELECT @@session.transaction_isolation(8.0+/5.7)或@@session.tx_isolation(5.6及更早),SET SESSION必须在START TRANSACTION前执行,全局修改需改my.cnf并重启。

REPEATABLE-READ 是 MySQL 默认且最稳妥的选择,绝大多数业务无需改动;盲目调低到 READ-COMMITTED 或调高到 SERIALIZABLE 往往引入新问题,而不是解决问题。
查当前隔离级别时别被变量名绕晕
MySQL 5.7 和 8.0+ 统一用 transaction_isolation,但 5.6 及更早版本只认 tx_isolation。直接执行 SELECT @@transaction_isolation 在老版本会报错。
安全写法是同时查两个:
SELECT @@session.transaction_isolation, @@session.tx_isolation;
注意返回值格式是带连字符的字符串,例如 'REPEATABLE-READ',不是 'REPEATABLE READ'(空格会导致后续 SET 失败)。
还要分清作用域:@@global.transaction_isolation 是新连接默认值,@@session.transaction_isolation 才是你当前会话实际生效的级别——后者可能已被 ORM 或连接池覆盖。
SET SESSION 必须在 START TRANSACTION 之前执行
这是线上最常踩的坑:事务一旦启动,再执行 SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED 就会报错 Can’t change transaction isolation level when a transaction is active。
- ORM 框架(如 Spring 的
@Transactional)通常在方法入口就隐式开启事务,此时再 set 已经晚了 - 可行方案包括:在连接池的
initSql中预设,或在业务代码中显式START TRANSACTION前调用SET TRANSACTION ISOLATION LEVEL ... - 临时设置只影响后续事务,当前事务仍沿用旧级别
全局修改不等于立即生效,重启和持久化是两回事
SET GLOBAL transaction_isolation = 'READ-COMMITTED' 只改变新建立连接的默认值,所有已存在的活跃会话完全不受影响。
该命令也不会写入配置文件,MySQL 重启后即失效。
要永久生效,必须编辑 my.cnf(Linux)或 my.ini(Windows),在 [mysqld] 段落下添加:
transaction-isolation = READ-COMMITTED
注意:transaction-isolation 中的连字符不能写成下划线,也不能加引号或空格;改完必须重启 MySQL 进程,FLUSH PRIVILEGES 或 SIGHUP 无效。
云数据库(如阿里云 RDS)通常禁用 SET GLOBAL,只能通过控制台修改参数组并重启实例。
REPEATABLE-READ 下的幻读没你想得那么危险
InnoDB 的 REPEATABLE-READ 实际通过 Next-Key Lock(行锁 + 间隙锁)拦截了大部分幻读场景,但仅对 WHERE 条件命中索引有效。
例如:SELECT * FROM orders WHERE status = 'pending' 会锁住 status = 'pending' 对应的索引范围及间隙;但如果 status 列没索引,或查询是全表扫描,就无法阻止其他事务插入新行。
真需要杜绝幻读时,优先考虑 SELECT ... FOR UPDATE 显式加锁,而不是直接升到 SERIALIZABLE——后者会强制锁表,性能代价太大。
容易被忽略的一点是:很多所谓“幻读”其实是业务逻辑缺陷,比如没做唯一约束导致重复插入,跟隔离级别无关。











