read committed 是多数 web 应用更合适的选择,因其避免脏读、锁粒度小(只锁命中行、无间隙锁)、锁释放快、主从一致性更优(配合row格式binlog)、undo log压力低;repeatable read虽提供可重复读,但依赖间隙锁易导致阻塞和死锁,仅适用于财务对账等强一致性且索引良好的场景。

READ COMMITTED 是多数 Web 应用更合适的选择,REPEATABLE READ 不是“更安全”,而是更容易锁住不该锁的东西。
为什么 READ COMMITTED 在大多数业务里更稳
电商下单、支付状态更新、用户资料修改这类操作,核心诉求只是“别读到别人还没 COMMIT 的脏数据”——READ COMMITTED 刚好满足,且不额外加锁。
-
SELECT是快照读,不加锁;UPDATE/DELETE只锁命中的行,语句执行完立刻释放锁 - 没有间隙锁(Gap Lock),
INSERT不会被莫名卡住 - 主从复制用
binlog_format = ROW时,READ COMMITTED下的 binlog 更易重放,一致性风险更低 - 长事务下
Undo Log压力小:每次SELECT都建新Read View,不用保留事务启动以来所有旧版本
REPEATABLE READ 真正起作用的场景其实很窄
它靠事务启动时拍快照 + 间隙锁实现“可重复读”,但代价是锁范围大、持续时间长、容易阻塞。
- 只在真正需要“同一事务内两次
SELECT结果必须完全一致”时才必要,比如财务对账生成报表,且查询条件能走唯一索引或覆盖索引 -
SELECT ... FOR UPDATE配合范围条件(如WHERE status = 'pending')时,REPEATABLE READ会锁整个间隙,哪怕表里还没有这条记录 - 没索引的
WHERE条件下,REPEATABLE READ可能升级为全表间隙锁,直接让所有INSERT排队 -
innodb_row_lock_time_avg在REPEATABLE READ下常达百毫秒级,READ COMMITTED通常压在几毫秒
切换前必须确认的三件事
别只看文档说“MySQL 默认是 REPEATABLE READ”,就默认它更合理。切换前得实测验证。
- 查业务代码里有没有显式依赖“事务内多次读结果不变”的逻辑,比如先查再判断再更新的三步操作,且中间不能容忍新插入记录
- 看慢日志里有没有大量
INSERT等待锁、或SELECT ... FOR UPDATE执行时间突增,这往往是间隙锁在作祟 - 确认 binlog 格式是
ROW(不是MIXED或STATEMENT),否则READ COMMITTED下主从可能不一致
怎么改、改哪里、会不会滚回
改隔离级别本身很简单,但影响面容易被低估。
- 临时改当前会话:
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; - 全局生效(需重启后持久):
SET GLOBAL TRANSACTION ISOLATION = 'READ-COMMITTED';,并写入my.cnf的[mysqld]段 - Spring Boot 项目里如果用了
@Transactional(isolation = Isolation.REPEATABLE_READ),注解会覆盖数据库全局设置,得同步清理 - 改完别急着上线:重点观察
innodb_deadlocks是否下降、Handler_read_rnd_next是否异常升高(说明 MVCC 快照变频繁)











