绝大多数web业务应选用read committed而非默认repeatable read,因其能有效防止脏读、并发性能更高、锁粒度更轻;仅当明确需要事务内多次读取结果严格一致且可承受间隙锁开销时,才考虑使用repeatable read。

绝大多数 Web 业务应该用 READ COMMITTED,而不是默认的 REPEATABLE READ;只有明确需要“事务内多次读结果一致”且能承担锁代价时,才考虑切回 REPEATABLE READ。
脏读是必须拦住的红线,READ COMMITTED 刚好够用
脏读意味着一个事务读到了另一个事务还没 COMMIT 的中间状态——比如库存扣减了一半就崩了,你却按已扣完来发券。这种数据根本不存在,业务逻辑会直接错乱。READ COMMITTED 通过 MVCC 快照读 + 行级锁,在不加间隙锁的前提下,天然挡住脏读。
- 电商下单查库存 → 扣减 → 更新订单,只要不读到别人未提交的扣减数,就满足一致性要求
-
SELECT是快照读(无锁),UPDATE/DELETE只锁实际命中的行,不锁间隙,死锁概率低 - MySQL 8.0+ 配合
binlog_format = ROW时,主从延迟和数据不一致风险更小
REPEATABLE READ 不是“更强”,而是“更重”,容易被误用
InnoDB 的 REPEATABLE READ 不只是靠 MVCC,它还默认启用间隙锁(Gap Lock)和 next-key 锁。这会让看似简单的查询意外锁住一大片索引范围,尤其在没走索引或范围条件模糊时。
- 执行
SELECT * FROM orders WHERE status = 'pending' FOR UPDATE,可能锁住所有status值为'pending'的间隙,新订单插入被阻塞 - 没有合适索引时,InnoDB 可能升级为全表扫描 + 全表间隙锁,INSERT 直接卡住
- 幻读虽被 next-key 锁压制,但代价是并发写吞吐下降、死锁增多、监控里频繁出现
Waiting for table metadata lock
别把“避免幻读”当默认需求,先问业务是否真依赖它
幻读本质是:同一事务中,两次 SELECT ... WHERE x = ? 返回行数不同,因为别的事务插入了匹配的新行。但多数业务并不需要这个保证。
- 用户列表分页加载:第一次查第1页,第二次查第2页,中间插入一条记录,不影响分页逻辑
- 订单状态轮询:查
status = 'processing'的订单做处理,新订单插入后下一轮再扫,完全合理 - 真需要幻读控制的场景极少,比如财务对账生成报表前要冻结某类数据的全部快照,且该操作是低频、离线、可接受锁表的
真正难处理的不是“选哪个级别”,而是切换隔离级别后,原有 SQL 的锁行为会发生隐式变化——比如 REPEATABLE READ 下安全的范围 FOR UPDATE,在 READ COMMITTED 下可能只锁行不锁间隙,导致并发插入引发业务逻辑冲突。务必在测试环境用真实流量压测锁表现,而不是只看 SELECT 结果是否一致。











