MySQL默认隔离级别为REPEATABLE-READ,它通过MVCC和Next-Key Lock在保证强一致性的同时兼顾并发性能,适配大多数OLTP场景;盲目调整隔离级别易引发脏读、幻读或性能下降等问题。

MySQL 默认的 REPEATABLE-READ 已经能覆盖大多数业务场景,盲目调低到 READ-COMMITTED 或调高到 SERIALIZABLE 反而容易引入新问题——关键不是“设成什么”,而是“为什么在这个会话/事务里必须改”。
查看当前隔离级别时注意变量名差异
MySQL 5.7 和 8.0+ 使用 transaction_isolation,但旧版本(如 5.6)只认 tx_isolation。直接查 @@transaction_isolation 在老版本会报错。
- 安全写法是两个都查:
SELECT @@transaction_isolation, @@tx_isolation;,任一有值即可 - 返回值格式为带连字符的字符串,例如
'REPEATABLE-READ',不是REPEATABLE READ(空格会报错) - 全局和会话级变量名不同:
@@global.transaction_isolationvs@@session.transaction_isolation,混用会导致误判
会话级设置必须在 START TRANSACTION 之前执行
一旦事务已启动,再执行 SET SESSION TRANSACTION ISOLATION LEVEL ... 会报错:Can’t change transaction isolation level when a transaction is active。
- 正确顺序:先
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;,再START TRANSACTION; - 如果用 ORM(如 Django、Spring),需确认其是否自动开启事务——有些框架在执行第一条 SQL 时就隐式启事务,此时再 set 就晚了
- 临时切换只对后续事务生效,当前事务仍沿用旧级别
全局设置不立即影响已有连接
SET GLOBAL transaction_isolation = 'READ-COMMITTED'; 只改变新建立的连接默认值,不会重置当前活跃会话的隔离级别。
- 生产环境改全局前,建议先用
SHOW PROCESSLIST;确认没有长事务或监控连接在运行 - 配置文件方式(
my.cnf中加transaction-isolation = READ-COMMITTED)需重启 MySQL 才生效,且重启后所有新连接才继承该值 - 某些云数据库(如阿里云 RDS)不支持动态
SET GLOBAL,只能通过控制台修改参数组并重启实例
REPEATABLE-READ 并非完全免疫幻读
InnoDB 的 REPEATABLE-READ 用 Next-Key Lock 防幻读,但仅对 WHERE 条件命中索引有效;全表扫描或范围查询未覆盖的间隙仍可能插入新行。
- 例如:
SELECT * FROM orders WHERE status = 'pending';不会锁住status = 'shipped'的间隙,其他事务仍可插入新shipped记录 - 若业务真需杜绝幻读,不要直接升
SERIALIZABLE,优先考虑:SELECT ... FOR UPDATE显式加锁,或改用唯一约束+重试逻辑 -
READ-COMMITTED下每次SELECT都生成新快照,看似“更实时”,但反而增加 MVCC 版本链清理压力,可能拖慢 purge 线程
真正难的不是选哪个值,而是判断某条 SQL 是否真的需要脱离默认隔离级别——多数一致性问题其实出在应用层没做幂等、没校验前置状态,而不是隔离级别不够高。



















