MySQL 默认事务隔离级别是 REPEATABLE READ
(可重复读),由 InnoDB 存储引擎强制实现,且自 MySQL 5.1 起成为全局默认值。这不是配置出来的“习惯”,而是底层 MVCC + 间隙锁协同保障主从一致性的硬性设计结果。
怎么查当前隔离级别?别用错变量名
不同 MySQL 版本使用不同系统变量,混用会导致查不到或报错:
MySQL 5.7 及之后(含 8.0):用@@transaction_isolation和
@@GLOBAL.transaction_isolationMySQL 5.6 及更早:用
@@tx_isolation和
@@GLOBAL.tx_isolation
SELECT @@SESSION.transaction_isolation, @@GLOBAL.transaction_isolation;
如果返回类似
'REPEATABLE-READ'(注意是短横线而非下划线),说明生效;若返回空或报错
Unknown system variable,大概率版本不匹配,立刻切回
tx_isolation查。
怎么改?session、global、配置文件三者效果完全不同
改隔离级别不是“设一次就永远生效”,必须分清作用域:
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;:只影响当前连接后续事务,断连即失效
SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;:影响所有新建连接(已有连接不变),需 SUPER 权限,重启后丢失 在
/etc/my.cnf中写
transaction-isolation = READ-COMMITTED:服务重启后对所有连接生效,但无法热加载
⚠️ 注意:
GLOBAL设置不会改变当前已存在的会话,线上调参前务必确认连接池是否复用旧连接——很多 Java 应用用 Druid/Hikari 连接池,改了 global 后新连接才走新级别,老连接仍卡在
REPEATABLE READ。
为什么大厂线上常改成 READ COMMITTED
?不是默认就好吗
MySQL 默认选
REPEATABLE READ是历史包袱:早期 binlog 格式只有
STATEMENT,配合
RC会导致主从不一致(删/插顺序被重排)。但现在普遍用
ROW格式 binlog,这个限制已解除。
改用
READ COMMITTED的真实动因是: 降低锁粒度:
RC不用间隙锁(Gap Lock),高并发 INSERT 场景下死锁概率显著下降 减少长事务阻塞:
RR下一个长事务会 hold 住 MVCC 快照,导致 purge 线程无法清理旧版本,undo 表空间暴涨 语义更贴近直觉:开发者通常预期“只能读到已提交数据”,而不是“同一事务内反复读都一样”
但切换前必须验证:业务 SQL 是否依赖
RR的可重复性?比如报表类任务在事务中多次
SELECT COUNT(*),换
RC后可能因中间有其他事务提交而结果漂移。
真正容易被忽略的点是:隔离级别不是孤立参数,它和
binlog_format、
innodb_lock_wait_timeout、甚至应用层重试逻辑强耦合。调之前先看慢日志里有没有大量
Lock wait timeout exceeded,再决定是调级别,还是优化 SQL 加索引,或者干脆加
SELECT ... FOR UPDATE显式控制。
