READ COMMITTED 比 REPEATABLE READ 快15%–40%的前提是锁冲突密集、历史版本未堆积且purge跟得上;其优势源于每次SELECT新建read view(缩短MVCC版本链遍历)和仅持record lock(间隙锁更少),但MySQL 8.0+对非唯一二级索引范围查询和显式锁仍保留gap lock;切换需权衡业务是否允许不可重复读,且长事务或purge滞后仍会导致History list暴涨。

READ COMMITTED 在多数 OLTP 场景下比默认的 REPEATABLE READ 快 15%–40%,但这个数字只在锁冲突密集、历史版本未堆积、purge 跟得上的前提下才成立。盲目切换隔离级别不等于自动提速,真正卡住性能的往往是间隙锁和 MVCC 版本链遍历。
为什么 RC 比 RR 快?关键不在“读”,而在“锁和快照生命周期”
很多人以为换隔离级别是改个配置就提速,其实慢的从来不是 SQL 本身,而是它触发的底层行为:
-
REPEATABLE READ下,事务一启动就建一个 read view,后续所有SELECT都复用它——这意味着要遍历更长的 undo log 版本链,尤其当History list length> 10000 时,CPU 和内存压力明显上升 -
READ COMMITTED每次SELECT都建新 read view,只检查到“最近已提交版本”就停,版本链遍历更短 -
REPEATABLE READ默认用 next-key lock(行锁 + 间隙锁),锁持有时长 = 整个事务从BEGIN到COMMIT;而READ COMMITTED只加 record lock,且语句执行完立刻释放
RC 下 INSERT/UPDATE 被卡住?先查是不是 MySQL 8.0 的 gap lock 例外
别以为设成 READ COMMITTED 就彻底禁用了间隙锁——MySQL 8.0+ 引入了两个例外:
- 非唯一二级索引的范围查询(如
WHERE idx_col BETWEEN 10 AND 20)仍会加 gap lock,只为避免回表时幻读 - 显式用了
SELECT ... FOR UPDATE或LOCK IN SHARE MODE,gap lock 行为与REPEATABLE READ完全一致 - 用
SELECT * FROM performance_schema.data_locks查LOCK_MODE字段,含GAP就说明真锁了间隙
压测时 RR 看起来不慢?盯这三个指标才暴露真实瓶颈
平均响应时间(RT)可能差不多,但并发吞吐和稳定性早已被悄悄拖垮:
-
Innodb_row_lock_waits:每秒超过 5 次,基本确认是间隙锁互相卡死 -
Threads_running:持续高于线程池设置值(比如 >64),说明事务卡在锁上没释放 -
SHOW ENGINE INNODB STATUS中的---TRANSACTION段里,如果看到大量lock struct(s)且状态为WAITING FOR THIS LOCK TO BE GRANTED,就是长事务 + RR 的典型症状
临时切 RC 很简单,但业务语义是否允许才是关键
别一上来就全局改 transaction_isolation,先确认你的业务能不能接受“两次查结果不同”:
- 监控统计、日志聚合、报表预计算这类场景,完全可以加
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED控制单个事务 - 库存预占、金融扣款、订单状态机流转等强一致逻辑,必须留在
REPEATABLE READ - ORM 框架(如 MyBatis、Django ORM)通常不支持运行时动态设隔离级别,硬切得直连 MySQL 执行
SET命令 -
SERIALIZABLE不是“更安全的 RR”,而是把所有SELECT自动转成SELECT ... LOCK IN SHARE MODE,QPS 断崖下跌是常态
真正容易被忽略的点是:READ COMMITTED 不会自动清理旧版本,它只是不依赖它们;如果 innodb_purge_threads 跟不上,或者存在运行超 10 分钟的长事务,History list length 依然会暴涨,最终拖垮所有 DML。



















