rr隔离级别不能保证高并发下的数据一致性。它仅解决不可重复读,不阻止幻读及更新丢失等问题;需配合select ... for update或条件更新等机制才能保障正确性。

RR 隔离级别真能保证高并发下的数据一致性?
不能直接保证。RR(Repeatable Read)只解决「不可重复读」,但不阻止「幻读」,更不自动解决更新丢失、写偏斜等并发写问题。很多以为设成 RR 就万事大吉的场景,其实只是没压测到临界点。
什么时候必须配合 SELECT ... FOR UPDATE?
当业务逻辑含「读-改-写」三步(比如查余额 → 判断是否足够 → 扣减),且多个事务可能操作同一行时,仅靠 RR 无法避免两个事务同时读到旧值、各自扣减后覆盖对方结果。这时必须显式加锁。
-
SELECT ... FOR UPDATE在 RR 下会为扫描到的索引记录加 next-key lock(间隙锁 + 行锁),阻塞其他事务对这些范围的插入/更新 - 注意:只对 WHERE 条件命中索引的行生效;全表扫描或非索引字段过滤,可能升级为表级锁,拖垮性能
- 若 WHERE 条件无匹配行,InnoDB 仍会在对应间隙加锁,防止幻读插入——这点常被忽略,导致死锁
为什么 UPDATE ... WHERE id = ? 在 RR 下有时还是出错?
因为单条 UPDATE 语句本身是原子的,但它的“正确性”依赖前提条件是否成立。例如:UPDATE accounts SET balance = balance - 100 WHERE id = 123 AND balance >= 100 这种带校验的写法,比先 SELECT 再 UPDATE 更安全,且不需要额外锁。
- 推荐用「条件更新 + 影响行数判断」代替分离的读写:执行后检查
mysql_affected_rows()是否为 1 - RR 下,该语句仍只锁匹配到的行(或间隙),不会锁全表,但要注意 WHERE 中的字段必须有索引,否则退化为全表扫描加锁
- 如果业务需要严格串行(如库存超卖控制),单靠 RR + 条件更新不够,得结合应用层重试或分布式锁
RR 的 gap lock 在什么情况下会引发死锁?
典型场景是两个事务按不同顺序访问相同索引范围。比如事务 A 先锁 id IN (1,5),事务 B 同时锁 id IN (5,1),就可能因加锁顺序不一致触发死锁。
- 死锁检测由 InnoDB 自动完成,但回滚成本高;应尽量让所有业务按相同顺序访问数据(如统一按主键升序)
- 关闭
innodb_locks_unsafe_for_binlog(默认已禁用)或降级为 RC 能消除 gap lock,但会引入幻读——这不是银弹,要权衡 - 用
SHOW ENGINE INNODB STATUS查看最近死锁详情,重点关注TRANSACTIONS和LOCK WAIT部分里的lock_mode X locks rec but not gap等提示
真正难的不是设对隔离级别,而是厘清每一处「读-写依赖」是否被锁覆盖、每一条 WHERE 是否走索引、每一次失败是否该重试而非报错。这些细节藏在慢日志和死锁日志里,不在配置文件中。











