这里讲解的很清楚. 幻读并不是两次查询不一致, 而是表面上查询一致, 但实际操作却显示不一致.

  • RU: read-uncommitted, 去食堂吃饭, 找到一个空位置, 但打完饭回来看到位置被人坐了(实际上还是空位置, 脏读), 实际上那个人只是在脑海中想象自己坐下来了, 并没有真的去坐(回滚了).
  • RC: read-committed, 去食堂吃饭, 找到一个空位置, 但打完饭回来位置被人坐了.
  • RR: repeatable-read, 去食堂吃饭, 找到一个空位置, 但打完饭回来位置显示为空, 但坐下去时发现有个隐形人已经在坐(幻读).
  • s: serializable, 餐厅一次只能进一个人, 不会有冲突, 效率低.

注意:RR 级别下存在幻读的可能,但也是可以使用对记录手动加 X锁 的方法消除幻读。SERIALIZABLE 正是对所有事务都加 X锁 才杜绝了幻读,但很多场景下我们的业务sql并不会存在幻读的风险。SERIALIZABLE 的一刀切虽然事务绝对安全,但性能会有很多不必要的损失。故可以在 RR 下根据业务需求决定是否加锁,存在幻读风险我们加锁,不存在就不加锁,事务安全与性能兼备,这也是 RR 作为mysql默认隔是个事务离级别的原因,所以需要正确的理解幻读。

解决幻读的方式:

# 这里需要用 X锁  LOCK IN SHARE MODE 拿到 S锁 后我们没办法做 写操作
SELECT `id` FROM `users` WHERE `id` = 1 FOR UPDATE;