
MySQL InnoDB 四种事务隔离级别实现原理InnoDB 隔离级别基于MVCC (多版本并发控制)锁行锁、Gap 临键锁共同实现 MVCC 主要解决读不加锁快照读锁用来解决当前读的幻读问题。 SQL 标准 4 个隔离级别由低到高READ UNCOMMITTED 读未提交READ COMMITTED 读已提交 RCREPEATABLE READ 可重复读 RRMySQL InnoDB 默认级别SERIALIZABLE 串行化前置基础概念1快照读 vs 当前读快照读 (snapshot read)普通 select读取 undo log 历史版本不加行锁依靠 MVCC。select * from t where id1;当前读 (current read)加锁读取最新版本走行锁 / 临键锁读取磁盘最新数据。select ... for update; select ... lock in share mode; update / delete / insert2MVCC 核心部件undo log read‑view 隐藏列每行记录 3 个隐藏列DB_TRX_ID最后修改该行的事务 IDDB_ROLL_PTR回滚指针指向 undo log 里旧版本形成版本链DB_ROW_ID隐式主键无主键时Read‑View读视图快照读的时候生成的一个视图对象保存活跃事务 ID 集合用来判断版本链上哪个版本对当前事务可见。Read‑View 生成时机是 RC 和 RR 最核心区别1. READ UNCOMMITTED 读未提交原理不使用 MVCC不生成 Read‑View事务可以直接读到别的事务还没 commit 的数据脏读读取直接拿最新行不去 undo 版本链找历史快照几乎生产不用。问题脏读 ✔不可重复读 ✔幻读 ✔2. READ COMMITTEDRC读已提交✅实现原理每次快照读每执行一次普通 select就重新生成一个新 Read‑View用 Read‑View 过滤 undo 版本链只能读取已经提交事务所做的变更。现象同一个事务内前后两次相同select中间别的事务提交修改第二次 select 生成新 read‑view可以读到别人提交后的新值 →出现不可重复读。RC 级别脏读 ❌消除不可重复读 ✔存在幻读 ✔存在RC 下 InnoDB 关闭 Gap 锁没有间隙锁只有记录锁当前读只加行记录锁不加临键锁会出现幻读。3. REPEATABLE‑READRRInnoDB 默认可重复读✅实现原理Read‑View 只在事务内 第一次快照读第一条普通 select的时候生成一次之后整个事务复用这同一个 Read‑View不再重新生成 所以同一个事务多次普通 select始终拿这一套视图读到同一套快照消除了【不可重复读】快照读层面。⚠重点RR 隔离级别快照读 MVCC 不能解决幻读MVCC 只是读旧快照别的事务依然可以真实插入新行只是你快照看不到。RR 消除幻读靠【当前读】时的临键锁 (Next‑Key Lock 记录锁 Gap 间隙锁)当前读 (for update/update/delete) 时会加 Next‑Key 临键锁锁住区间阻止别的事务往这个区间插入新记录从而防止幻读。总结 RR脏读❌不可重复读❌快照读 MVCC幻读快照读依然可以发生只是看不见当前读依靠临键锁防止幻读RR 才开启 Gap 间隙锁RC 没有间隙锁这是 RC/RR 锁层面最大差异。4. SERIALIZABLE 串行化✅实现原理不使用 MVCC 快照读了InnoDB 把所有普通的快照读 select隐式自动转成SELECT ... LOCK IN SHARE MODE加共享行锁所有读操作都是当前读读写互相阻塞事务只能串行执行完全隔离所有并发异常。并发性能最差生产极少使用。脏读❌、不可重复读❌、幻读❌全部消除。RC 与 RR 核心对比项目RC (读已提交)RR (可重复读 默认)Read‑View 生成时机每次 select 都新建事务第一次 select 生成复用到底MVCC 解决不可重复读❌✅快照读解决间隙锁 Gap 锁关闭只有记录锁开启 Next‑Key 临键锁 (记录 间隙)幻读 (当前读)存在幻读临键锁阻止幻读补充面试易错点1.❌错误认知RR 隔离级别 MVCC 直接解决幻读✅正确MVCC 只是快照MVCC 本身不能阻止其他事务物理插入只有临键锁当前读才阻止幻读。2.RC 没有间隙锁所以 update 范围查询的时候别的事务可以插入发生幻读。3.MVCC 只服务快照读所有 DML (update/delete/for update) 永远都是当前读不走快照。一句话速记版RU无 MVCC直接读最新脏读RC每次 select 生成 read‑view消除脏读无间隙锁不可重复读、幻读存在RR事务首次 select 生成 read‑view 复用MVCC 消除快照读不可重复读开启临键锁锁住区间防止当前读幻读SERIALIZABLE全部 select 隐式加共享锁放弃快照串行执行。InnoDB 隔离级别MVCC 锁 分工核心结论MVCC 负责「快照读 (普通 select)」锁行锁、Gap 间隙锁、Next‑Key 临键锁负责「当前读 (for update /update/delete)」。两者各司其职一起实现 4 种隔离级别。1、先分清两类读最重要① 快照读Snapshot Read普通select * from t;✅走MVCC读 undo log 历史快照不加锁MVCC 组件隐藏列 DB_TRX_ID、DB_ROLL_PTR、undo log 版本链、Read‑ViewMVCC 只能解决读的可见性问题脏读、不可重复读MVCC无法物理阻止别的事务修改、插入数据只是自己读到旧快照看不见新数据而已② 当前读Current Readselect ... for update select ... lock in share mode update / delete / insert✅走锁机制行锁 / Gap 间隙锁 / Next‑Key 临键锁读取磁盘上最新的数据靠锁物理阻塞其它事务的写、插入操作用来解决幻读的是锁不是 MVCC2、四种隔离级别下 MVCC 与锁的开启情况1. READ‑UNCOMMITTED 读未提交❌不使用 MVCC、不生成 Read‑View直接读取最新的数据行锁普通 select 不加锁DML 依旧行锁会出现脏读2. READ‑COMMITTED RC 读已提交✅使用 MVCC每一次快照读都新建 Read‑View锁层面关闭 Gap 间隙锁只有记录锁行锁现象快照读会出现不可重复读当前读范围查询会幻读没有间隙锁挡插入3. REPEATABLE‑READ RRMySQL 默认✅使用 MVCC事务第一次快照读生成 Read‑View整个事务复用这一份MVCC快照读层面消除不可重复读但是仅仅是读旧快照别的事务仍然可以物理插入新行。锁层面开启 Next‑Key Lock临键锁 记录锁 Gap 间隙锁当前读的时候临键锁锁住整个区间物理阻止别的事务往区间插入新记录从而解决当前读的幻读⚠易错快照读依旧 “伪幻读”别的事务已经插进去了只是 MVCC 快照看不到。3. SERIALIZABLE 串行化❌关闭 MVCC 快照读普通 select 隐式变成lock in share mode全部变成当前读加共享锁读写互相阻塞事务串行跑。3、一句话区分分工MVCC 解决快照读的可见性锁解决当前读的并发写入阻塞。RC/RR 的 Read‑View 时机控制 MVCC 可见性RR 独有的临键锁负责物理阻挡插入解决当前读幻读。4、误区1.❌错误RR 隔离级别依靠 MVCC 解决幻读✅正确MVCC 只管读快照拦不住别人插入RR 是依靠临键锁 (Next‑Key Lock) 在当前读时防止幻读。2.RC 级别没有间隙锁只有 RR 才开启间隙锁。3.MVCC 只作用于快照读所有 DML 永远是当前读不走 undo 快照版本链。先把三个问题梳理1RC (读已提交) 为什么还要加行锁2RR (可重复读) 为什么要加临键锁 Next‑Key Lock3RR 到底有没有解决幻读重点易错1、RC 读已提交为什么加行锁⚠️记住RC 只是关闭了 Gap 间隙锁但是【记录锁行锁依然存在】MVCC 仅仅作用于快照读普通 select 不加锁update / delete / select … for update属于当前读和 MVCC 无关RC 的锁策略✅快照读MVCC不加锁✅当前读只加记录锁行锁不加间隙锁为什么 RC 还需要行锁行锁的作用是防止同一条记录被多个事务同时修改解决写‑写冲突。举例事务 A update id5事务 B 也 update id5行锁拦住 B避免两条事务同时改写同一行丢失更新。RC 只是不去锁间隙也就是同一行不能并发改但是间隙之间允许别的事务插入新行。所以 RC 范围的当前读就会出现幻读锁住已经存在的行但没锁住空隙别人可以往空隙插入新记录。2、RR 可重复读为什么引入临键锁 Next‑Key Lock记录锁 Gap 间隙锁临键锁 记录锁 间隙锁。RR 的快照读靠 MVCC 解决不可重复读但是 MVCC 拦不住别的事务 INSERTMVCC 只是读旧版本快照做不到物理阻止其他事务在查询的区间插入新数据。当执行范围的当前读例如select * from t where id 10 for update;如果只加普通行锁只能锁住已经存在的那些行id10 的空隙没有锁别的事务可以插入 id1112。 事务 A 再次做这条for update当前读就会读到刚刚新插入的 id11发生幻读。所以 InnoDB 在 RR 隔离级别范围当前读时使用临键锁把整个查询的区间全部锁住包括中间空隙 Gap禁止别的事务往区间里面 INSERT 新行。目的在当前读层面物理阻止幻读的发生。补充小知识点RR 也不是所有情况都一定生成临键锁如果是等值查询命中唯一索引精确匹配一行会降级退化成普通记录锁。3、RR 到底有没有解决幻读这道题坑最多幻读定义同一个事务内前后两次相同查询第二次返回多出之前没有的行。分两种读场景1. 快照读普通 selectMVCCRR 没有消除幻读伪幻读事务 A 全程普通 select复用同一份 read‑view即使 B 事务已经物理插入提交了新行A 读到的还是旧快照看不见新增的数据。⚠注意数据库里面数据其实已经插进去了只是 MVCC 快照看不到。不是阻止了插入只是看不见这叫伪幻读。2. 当前读update /delete/for updateRR 依靠临键锁物理锁住区间禁止插入解决了当前读场景下的幻读 。✅最终标准答案InnoDB 的 RR 隔离级别对于快照读普通 select依靠 MVCC并没有真正解决幻读只是看不到新插入的数据对于当前读DML、for update 等依靠临键锁 (Next‑Key Lock) 锁住查询区间物理阻塞插入解决幻读所以不能笼统说 RR 完全解决幻读也不能笼统说 RR 完全没解决幻读要看是快照读还是当前读。一句话总结RC 保留行 (记录) 锁是为了解写写冲突防止丢失更新去掉间隙锁允许间隙插入并发更好。RR 增加临键锁记录锁 Gap专门用来对付范围当前读时别的事务插入新行造成的幻读。RR 幻读分快照读和当前读两套结果是 MVCC 和锁两套机制分别生效带来的现象。拓展SERIALIZABLE 隔离级别全部读变成当前读加锁所以不管快照读、当前读幻读全部消除。