一张表先说清四个级别(MySQL InnoDB):
| 级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| 读未提交 | 会 | 会 | 会 |
| 读已提交 RC | 不会 | 会 | 会 |
| 可重复读 RR(默认) | 不会 | 不会 | 基本不会 |
| 串行化 | 不会 | 不会 | 不会 |
三个概念
- 脏读:读到别人没提交的数据
- 不可重复读:同一行两次读,值变了(别人改了并提交)
- 幻读:同样的条件两次查,行数变了(别人插入了新行)
区别在于:不可重复读针对已存在的行被修改,幻读针对新增的行。
RR 为什么能防住大部分幻读
两个机制配合:
- MVCC 快照读:普通
SELECT读的是事务开始时的快照,别人插入的新行对你不可见; - 间隙锁(Gap Lock):
SELECT ... FOR UPDATE这类当前读会锁住索引记录之间的间隙,别人插不进来。
但有一个经典例外
快照读和当前读混用会出问题:
-- 事务 A
BEGIN;
SELECT * FROM t WHERE id > 100; -- 快照读,看到 3 行
-- 此时事务 B 插入 id=200 并提交
SELECT * FROM t WHERE id > 100 FOR UPDATE; -- 当前读,看到 4 行!
COMMIT;
同一个事务里两次查询行数不一致——这就是 RR 下依然可能出现的幻读。原因是当前读会读到最新提交版本,绕过快照。
规避:事务里一旦要用 FOR UPDATE / UPDATE,从头就用当前读,别混。
RC 和 RR 怎么选
| RC | RR | |
|---|---|---|
| 锁范围 | 小,无间隙锁 | 大,有间隙锁 |
| 并发度 | 高 | 低 |
| 死锁概率 | 低 | 高 |
| 主从复制 | 需 binlog 用 ROW 格式 | 都行 |
互联网高并发业务(尤其有大量 UPDATE ... WHERE 的)我倾向改 RC,死锁少很多,靠业务侧乐观锁解决一致性。
查看当前级别:
SELECT @@transaction_isolation;
楼主 · 2026-09-28 13:21 · 浏览 2

