RSS
菜单
全部文章快讯开发科技深度热点

第 32 章 大厂MySQL面试:原理深挖

内容摘要

本篇是面试系列的收官深挖篇,专门应对大厂"往底层问三层"的风格——B+Tree 选型、redo/undo、Buffer Pool、change buffer、核心参数调优。这一篇不考你怎么用,而是考你 InnoDB 内部到底怎么运转。学完你应该能手画一棵 B+Tree、说清 WAL 与两阶段提交的关系、讲出 Buffer Pool 改良 LRU 的动机。所有示例基于 MySQL 8.0 语法书写,本示例基于 MySQL 8.0 环境。

📖 本篇简介:本篇是面试系列的收官深挖篇,专门应对大厂"往底层问三层"的风格——B+Tree 选型、redo/undo、Buffer Pool、change buffer、核心参数调优。这一篇不考你怎么用,而是考你 InnoDB 内部到底怎么运转。学完你应该能手画一棵 B+Tree、说清 WAL 与两阶段提交的关系、讲出 Buffer Pool 改良 LRU 的动机。所有示例基于 MySQL 8.0 语法书写,本示例基于 MySQL 8.0 环境。

目录

  • 本篇导读与答题思路
  • 题目 1:InnoDB 为什么选择 B 加树做索引
  • 题目 2:redo log 的 WAL 和循环写是怎么保证 crash safe 的
  • 题目 3:undo log 的作用和版本链,purge 线程干什么
  • 题目 4:Buffer Pool 的 LRU 为什么要改良
  • 题目 5:change buffer 是什么,什么场景下能加速
  • 题目 6:几个核心内存参数怎么调优思路
  • 题目 7:聚簇索引和二级索引的叶子节点各存什么
  • 题目 8:什么是 double write 双写,它解决什么问题
  • 速查表:原理深挖速查
  • 注意事项:原理题的答题深度要求
  • 练习题:动手自测与参考
  • 复习清单

本篇导读与答题思路

大厂原理题的评分标准很明确:能不能讲到"机制"和"权衡"。

  • 讲机制:不是说"redo log 是日志",而是说"它固定大小循环写、先写日志再写数据页(WAL)、checkpoint 推进旧页"。
  • 讲权衡:不是说"Buffer Pool 很大",而是说"为什么不能无限大、为什么要年轻代老年代分区、预读为什么会把冷数据挤出去"。

答题时主动说"这样设计是为了权衡 A 和 B",立刻和背八股的人拉开差距。下面每道题都带一个"为什么这么设计"的权衡点。

原理题还有一个隐藏考点:各组件是怎么协作的。别把 redo、undo、Buffer Pool、change buffer、double write 答成五件孤立的事。比如一次 UPDATE:Buffer Pool 改页 → redo 记日志 → change buffer 记二级索引 → double write 兜底页安全 → undo 留旧版本。能串成一条链路讲出来,就是 P7+ 的水平。

最后提醒:大厂面试官听到你答得太"顺"会故意戳一下,比如"你说的这个在 5.7 和 8.0 有差别吗"。遇到这种问题,坦诚说"细节版本差异我记得不太准,但机制是……",别硬编版本号。

题目 1:InnoDB 为什么选择 B 加树做索引

考点

  • B+Tree 与 B-Tree、哈希、二叉树、红黑树的对比。
  • 磁盘 IO 这个物理约束是选型核心。

参考答案

一句话结论:因为数据库是按磁盘页(16KB)读取的,B+Tree 矮胖、扇出大、叶子链表,正好匹配磁盘 IO 和范围查询这两个硬需求。

逐个对比着答:

  • 为什么不是二叉树/红黑树:树太高,千万级数据高度可能到几十层,每次查找几十次磁盘 IO,太慢。
  • 为什么不是 B-Tree:B-Tree 非叶子节点也存数据,单页能放的键少,树更高;而且范围查询要中序遍历整棵树,不如 B+Tree 叶子链表顺。
  • 为什么不是哈希:哈希等值 O(1) 很快,但不支持范围查询、不支持排序、哈希冲突时退化。
  • 为什么是 B+Tree:

1. 非叶子节点只存键不存数据,单页能放上千个键,扇出大,树高 3~4 层就能撑千万行;

2. 叶子节点存全部数据,且用双向链表串起来,范围扫描和 ORDER BY 直接顺着链表走;

3. 查询路径长度稳定,每次都要走到叶子,性能可预期。

一张典型的 B+Tree(聚簇索引)示意:

        [ 30 | 60 ]                 <- 非叶子节点,只存键,导航用
       /      |      \
 [10|20]  [30|40|50]  [60|70|80]    <- 非叶子节点继续导航
   |        |           |
  数据链   数据链       数据链       <- 叶子节点存整行数据,双向链表串联

权衡点:B+Tree 单点查询要走到叶子(B-Tree 可能在非叶子就命中),但数据库里范围查询和排序极多,这个牺牲值得。

追问

  • 追问:一张千万级表树高大概几层? 一般 3~4 层。假设页大小 16KB、每页放 1000 个键,3 层就是 10 亿量级。
  • 追问:联合索引在 B+Tree 里怎么组织? 按索引列顺序排序,先比第一列、再比第二列,这就是最左前缀的物理基础。

题目 2:redo log 的 WAL 和循环写是怎么保证 crash safe 的

考点

  • WAL(预写日志):先写日志再落数据页。
  • redo log 固定大小循环写、write pos 与 checkpoint。
  • 崩溃恢复时重放 redo。

参考答案

一句话结论:redo log 让"数据页还没落盘"就可以先告诉客户端提交成功,崩溃后靠重放 redo 把丢的修改补回来,这就是 crash-safe。

两个核心机制:

WAL(Write-Ahead Logging):修改数据时,先把"某页改成什么"记到 redo log 并刷盘,再慢慢把数据页刷到磁盘。这样即使数据页还在内存没刷就宕机,重启后重放 redo 就能恢复。

循环写:redo log 是固定大小的一组文件(如 ib_logfile0/1),像一个环形缓冲区:

  • write pos:当前写到哪。
  • checkpoint:刷到磁盘数据页的进度。
  • write pos 追上 checkpoint 说明写满了,必须先推进 checkpoint(刷脏页)才能继续写。
-- 查看 redo log 相关参数(MySQL 8.0)
SHOW VARIABLES LIKE 'innodb_log_file_size';     -- 单个日志文件大小
SHOW VARIABLES LIKE 'innodb_log_files_in_group';-- 组内文件个数
SHOW VARIABLES LIKE 'innodb_flush_log_at_trx_commit'; -- 1=每次提交刷盘,最安全

崩溃恢复流程:重启时扫描 redo log,把所有"已提交但数据页没落盘"的修改重放一遍,把"未提交"的事务用 undo 回滚。

权衡点:innodb_flush_log_at_trx_commit=1 最安全(每次提交刷盘),性能略低;设成 2 性能好但宕机丢一秒数据。金融业务必须 1。

把循环写画成环形更直观:

      checkpoint ───→  还没刷成数据页,可被覆盖的旧区
  |------------------------------------------|
  |  已刷盘可复用  |  write pos 正在写  |  空闲  |
  |------------------------------------------|
                  write pos ──→ 追上 checkpoint 时必须先推进 checkpoint

write pos 追不上 checkpoint,写入就被卡住;所以 redo log 不能设太小,否则频繁刷脏页反而拖累性能。

-- 查看 checkpoint 与日志相关状态(MySQL 8.0)
SHOW ENGINE INNODB STATUS\G   -- 找 LOG 段看 log sequence number、checkpoint

追问

  • 追问:redo log 为什么不用追加写而要循环写? 追加写会无限膨胀;循环写固定大小、覆盖旧日志,崩溃恢复只需要从 checkpoint 开始重放,成本可控。
  • 追问:redo log 是物理日志还是逻辑日志? 物理日志,记录"第 X 页第 Y 偏移改成 Z",重放时直接按页重做。

题目 3:undo log 的作用和版本链,purge 线程干什么

考点

  • undo log 三个作用:回滚、MVCC 版本链、read view 可见性。
  • 版本链结构与 purge 线程清理无用版本。

参考答案

一句话结论:undo log 是"反向操作日志",既负责事务回滚,又给 MVCC 提供历史版本,最后由 purge 线程清理不再需要的旧版本。

三个作用:

1. 回滚:事务失败时,按 undo log 反向执行,把数据恢复原状,保证原子性。

2. MVCC 版本链:每次更新都把旧值存进 undo log,通过 roll_pointer 串成版本链,Read View 顺着链找可见版本。

3. 维持隔离性:别的事务要读旧版本时,从版本链取,不用锁当前行。

版本链长这样:

当前行(trx_id=300)
   ↑ roll_pointer
undo: trx_id=200 的旧版本
   ↑ roll_pointer
undo: trx_id=100 的更旧版本

purge 线程:版本链会越来越长,当一个旧版本"没有任何 Read View 还需要它"时,purge 线程把它物理删除。这就是为什么长事务(一直不开新 Read View)会让 undo 暴涨、表空间膨胀——purge 不敢删。

-- 查看当前有多少未清理的 undo(8.0 已把 undo 从系统表空间拆出来)
SHOW STATUS LIKE 'Innodb_undo_log_count';

权衡点:undo 既要留着给 MVCC 读,又不能无限留,purge 就是这个"GC"机制,长事务会拖垮它。

追问

  • 追问:长事务为什么危险? 它持有一个很老的 Read View,purge 不能删它可见的旧版本,undo 越堆越多,磁盘暴涨、复制延迟。
  • 追问:undo log 和 redo log 都会刷盘吗? undo 也会先记 redo(因为 undo 本身也是要持久化的数据页修改),这叫"undo 的 redo"。

题目 4:Buffer Pool 的 LRU 为什么要改良

考点

  • Buffer Pool 是 InnoDB 的内存缓存,缓存数据页和索引页。
  • 普通 LRU 的问题:全表扫描/预读会把热点页挤出去。
  • 改良 LRU:年轻代(young)/老年代(old)分区,midpoint 插入。

参考答案

一句话结论:直接用经典 LRU 会被"一次性大查询"冲垮缓存,所以 InnoDB 把 LRU 分成年轻代和老年代,新读入的页先放老年代头部,停留够久才晋升年轻代。

问题场景:一条 SELECT * 全表扫描千万行,按经典 LRU,这些页会把之前缓存的热点页全挤掉,后续正常查询全部打磁盘。这叫 LRU 污染。

InnoDB 的改良:

  • LRU 链表分成两段:前面 young(年轻代,热点),后面 old(老年代)。
  • 新读入的页不插链表头,而是插到 midpoint(老年代头部)。
  • 一个页在老年代停留超过 innodb_old_blocks_time(默认 1 秒)后再次被访问,才晋升到年轻代。
  • 这样全表扫描时读的页只在老年代晃一下,不会冲进年轻代污染热点。
-- 查看 Buffer Pool 大小和 old 区比例
SHOW VARIABLES LIKE 'innodb_buffer_pool_size';      -- 一般设成物理内存 50%~70%
SHOW VARIABLES LIKE 'innodb_old_blocks_pct';        -- old 区占比,默认 37%
SHOW VARIABLES LIKE 'innodb_old_blocks_time';       -- 在 old 区停留多久才晋升,默认 1000ms

权衡点:这个机制牺牲了"刚加载就要用"的页的命中率(要等晋升),但保住了整体热点不被冲刷,对 OLTP 更划算。

实际监控 Buffer Pool 是否够用:

-- 看命中率:从 INNODB STATUS 的 Buffer pool hit rate 读数
SHOW ENGINE INNODB STATUS\G
-- 正常应接近 1000/1000(即 99.9%+),明显偏低说明内存不够

-- 看 young/old 各占多少页、被预读进来的页有没有立刻淘汰
SHOW STATUS LIKE 'Innodb_buffer_pool_read%';

经验值:命中率掉到 99% 以下就要警惕,先排查是不是有全表扫描在污染,再考虑扩容 buffer pool。

追问

  • 追问:Buffer Pool 命中率怎么看? SHOW ENGINE INNODB STATUS 里的 Buffer Pool hit ratio,正常应在 99% 以上。
  • 追问:Buffer Pool 设多大合适? 专机部署一般设为物理内存的 50%~70%,不能占满,要留内存给操作系统和连接。

题目 5:change buffer 是什么,什么场景下能加速

考点

  • change buffer 缓存"二级索引的写",避免立即随机读磁盘。
  • 只对非唯一二级索引有效。
  • 什么时候 merge。

参考答案

一句话结论:change buffer 把"对二级索引页的写"先缓存在内存里,等这个页被读到时再合并,避免每次写都随机读一次磁盘。

为什么需要:更新一条数据可能要改多个二级索引。如果某个二级索引页不在 Buffer Pool 里,按老逻辑就得先从磁盘把这个页读上来(一次随机 IO)再改。change buffer 说:不读了,先把修改记在 change buffer 里,等以后这个页真的被读出来时再 merge。

关键限制(必答):

  • 只对非唯一二级索引有效。唯一索引必须立刻读盘校验唯一性,没法缓存,所以用不上 change buffer。
  • 写多读少的表收益最大(写进来后很久不读)。
-- 查看 change buffer 配置和命中率
SHOW VARIABLES LIKE 'innodb_change_buffer_max_size'; -- 占 Buffer Pool 比例,默认 25%
SHOW STATUS LIKE 'Innodb_buffer_pool_wait_free';

merge 时机:相关索引页被读入 Buffer Pool 时、系统后台定期 merge、正常关闭重启时。

权衡点:change buffer 加速了写,但如果被缓存的修改一直不 merge,会占内存、重启恢复变慢;而且它只帮非唯一索引,唯一索引写性能天生略差。

追问

  • 追问:唯一索引为什么用不了 change buffer? 插入时必须立刻确认有没有重复值,得马上把页读上来校验,没法延迟。
  • 追问:change buffer 和 redo log 区别? redo 记物理修改保证 crash-safe;change buffer 是缓存二级索引的写操作,减少随机读,职责不同。

题目 6:几个核心内存参数怎么调优思路

考点

  • innodb_buffer_pool_size、innodb_log_file_size、innodb_flush_log_at_trx_commit、sync_binlog。
  • 调优不是背数字,是理解每个参数权衡什么。

参考答案

一句话:调参的核心是搞懂每个参数在"性能"和"安全"之间的天平,再按业务类型(OLTP 读多还是写多)定。

四个必问参数:

| 参数 | 作用 | 建议 |

|---|---|---|

| innodb_buffer_pool_size | 数据页/索引页缓存大小 | 专机设物理内存 50%~70% |

| innodb_log_file_size | 单个 redo log 文件大小 | 约能存 1 小时写入,太大恢复慢 |

| innodb_flush_log_at_trx_commit | redo 刷盘时机 | 金融=1(最安全),非核心可 2 |

| sync_binlog | binlog 刷盘时机 | 强一致=1,性能优先可放宽 |

-- 线上常见的"安全优先"组合(两阶段提交双 1)
SET GLOBAL innodb_flush_log_at_trx_commit = 1;  -- redo 每次提交刷盘
SET GLOBAL sync_binlog = 1;                     -- binlog 每次提交刷盘
-- 两个都为 1 时,宕机最多丢未提交事务,已提交的不丢

调优思路(答题框架):

1. 先监控再动手:看 QPS、Buffer Pool 命中率、慢日志、IO 等待,别拍脑袋调。

2. 一次只改一个参数:改完观察几天,归因才清晰。

3. OLTP 写多:buffer pool 调大、redo log 适中、doublewrite 开着;OLAP 读多:主要靠 buffer pool 命中率。

4. 默认值往往不差:MySQL 8.0 默认参数已经比较合理,别为了"优化"而优化。

追问

  • 追问:双 1(两个参数都为 1)为什么最安全? redo 和 binlog 都每次提交刷盘,崩溃后两份日志一致,不丢已提交事务。代价是每次提交多两次刷盘 IO。
  • 追问:buffer pool 命中率 90% 说明什么? 说明内存不够或缓存策略差,热点页大量打磁盘,优先扩 buffer pool 或排查全表扫描。

题目 7:聚簇索引和二级索引的叶子节点各存什么

考点

  • 聚簇索引叶子存整行,二级索引叶子存主键值。
  • 为什么二级索引要回表,以及覆盖索引如何绕过。

参考答案

一句话结论:聚簇索引的叶子节点就是整行数据,二级索引的叶子节点存的是主键值——这就是回表的根。

画个对照:

聚簇索引(主键 id)的 B+Tree 叶子:
   [ id=10 | name='张三' | city='北京' | age=28 | ...整行... ]

二级索引 idx_city 的 B+Tree 叶子:
   [ city='北京' | 主键 id=10 ]   <- 只存索引列 + 主键,不存整行

所以二级索引查数据的流程是:

1. 在 idx_city 上找到 city='北京',拿到主键 id 列表;

2. 拿 id 回聚簇索引那棵树查整行。

第 2 步就是回表。

覆盖索引就是把要查的列都放进二级索引叶子,省掉第 2 步:

-- 把 name 一起放进索引,叶子变成 [city | name | id]
ALTER TABLE t_user ADD INDEX idx_city_name (city, name);

-- 这条查询的列 city、name 全在索引叶子里,不回表
SELECT name FROM t_user WHERE city = '北京';
-- EXPLAIN Extra 显示 Using index

权衡点:二级索引叶子存主键而不是行指针,是因为"主键值更稳定"——行可能被移动(页分裂),但主键不变,存主键避免了行移动时所有二级索引都要改指针。

-- 对比两条查询的回表行为(MySQL 8.0)
EXPLAIN SELECT * FROM t_user WHERE city = '北京';          -- 要整行,必回表
EXPLAIN SELECT city, name FROM t_user WHERE city = '北京';  -- 命中 (city,name) 覆盖索引,不回表
-- 前者 key=idx_city,Extra=Using where(回表)
-- 后者 key=idx_city_name,Extra=Using index(覆盖)

追问

  • 追问:一张表没有主键会怎样? InnoDB 会偷偷选一个非空唯一索引当聚簇索引,都没有就生成一个隐藏的 6 字节 RowID,性能和可维护性都差,所以一定要显式建主键。
  • 追问:二级索引叶子为什么不直接存整行? 那样每个二级索引都存一份整行,空间翻倍,写时还要维护多份整行,划不来。

题目 8:什么是 double write 双写,它解决什么问题

考点

  • 页损坏(partial page write)问题:磁盘页 16KB 写一半宕机。
  • double write 先写共享表空间、再写真实位置。
  • 和 redo log 的关系:redo 是页级逻辑重放,页坏了就没法重放。

参考答案

一句话结论:double write(双写缓冲)解决的是"数据页写到一半宕机,页本身损坏"的问题,是 InnoDB crash-safe 的最后一道防线。

先讲它防的是什么:InnoDB 数据页 16KB,而操作系统/磁盘一般按 4KB 原子写。如果一个 16KB 页写到一半(比如写了 12KB)机器宕机,这个页就是坏的(partial page write,部分写失效)。此时 redo log 虽然在,但 redo 是"基于正确页"的增量重放,页本身都坏了,重放无从谈起。

double write 的做法:

1. 脏页先写到共享表空间里的 double write 区域(顺序写);

2. 再从 double write 区域拷贝到真正的数据文件位置;

3. 崩溃恢复时,如果发现某个数据页坏了,就从 double write 区域拷一份完好的页出来,再用 redo 重放。

-- 查看 double write 是否开启(8.0 默认开启)
SHOW VARIABLES LIKE 'innodb_doublewrite';
SHOW STATUS LIKE 'Innodb_dblwr_writes';   -- double write 写了多少次

权衡点:double write 多了一次写,有一点性能损耗(约 5%~10%),但换来页级别安全,值得。极端对性能极敏感、底层文件系统保证原子写的场景才考虑关。

一句话总结这层防线:先写 double write 备份区,再写数据文件,崩溃恢复时先靠 double write 把页修对,再靠 redo 把增量补全,两者缺一不可。

面试时如果被追问"为什么不直接靠 fsync 保证原子写",答:普通磁盘扇区是 4KB,InnoDB 页是 16KB,单次 write 系统调用不是原子的,写一半掉电就会留下坏页,所以才需要 double write 这个软件层兜底。

追问

  • 追问:double write 和 redo log 是重复设计吗? 不重复。redo 负责"页正确时的增量恢复",double write 负责"页本身损坏时先把页修好",两者配合才完整。
  • 追问:mysql 8.0 还有没有必要? 有,默认开启。除非用了保证 16KB 原子写的存储(如某些文件系统+电池缓存),否则别关。

速查表:原理深挖速查

| 考点 | 一句话结论 |

|---|---|

| B+Tree 选型 | 矮胖扇出大、叶子链表,匹配磁盘 IO 与范围查询 |

| WAL | 先写 redo 再刷数据页,崩溃后重放恢复 |

| redo 循环写 | write pos 追 checkpoint,写满先刷脏页 |

| undo | 回滚 + MVCC 版本链,purge 清理无用旧版本 |

| Buffer Pool | 改良 LRU 分 young/old 区,midpoint 插入防污染 |

| change buffer | 缓存非唯一二级索引的写,merge 时落盘 |

| 双 1 | redo 与 binlog 都每次提交刷盘,最安全 |

| 聚簇 vs 二级 | 聚簇叶子存整行,二级叶子存主键,故需回表 |

注意事项:原理题的答题深度要求

1. 只说"是日志"不说机制:redo 要讲到循环写、write pos/checkpoint、WAL,否则算没答到点。

2. 漏说权衡:原理题高级答案都带"这样设计是为了权衡什么",比如改良 LRU 牺牲刚加载页命中率换整体热点。

3. change buffer 说成所有索引都能用:一定要强调只对非唯一二级索引有效。

4. buffer pool 调参背数字:要讲"命中率不够才扩、专机设 50%~70%"的思路,而不是死记 128G。

5. 二级索引叶子存什么答错:必须是"主键值",不是整行、不是行物理地址。

6. 把 double write 和 change buffer 搞混:double write 防页损坏,change buffer 缓存二级索引写,名字像但完全两码事。

7. 讲组件讲成孤岛:一定要把 redo/undo/Buffer Pool/double write 串成一次 UPDATE 的完整流程,体现系统观。

练习题:动手自测与参考

练习 1:为什么 InnoDB 表一定要显式建自增主键?不建会怎样?

<details>

<summary>参考答案</summary>

聚簇索引按主键物理组织。不建主键时 InnoDB 选第一个非空唯一索引,都没有就生成隐藏 RowID(不可控、不可见),二级索引叶子还要存这个隐藏列,又大又难维护。自增主键还带来顺序插入、少页分裂的好处。

</details>

练习 2:innodb_flush_log_at_trx_commit=2 比 =1 快,快在哪?有什么风险?

<details>

<summary>参考答案</summary>

=2 时每次提交只写 OS 页缓存,不 fsync 刷盘,每秒刷一次,少了每次提交的磁盘刷写,所以快。风险是操作系统崩溃(不是 MySQL 崩溃)会丢最近 1 秒的事务。金融业务必须 =1。

</details>

练习 3:一条全表扫描的大查询为什么会让后续正常查询突然变慢?

<details>

<summary>参考答案</summary>

经典 LRU 下大查询读的页会占满 Buffer Pool,把之前缓存的热点页挤出去(LRU 污染),后续查询全部打磁盘。InnoDB 用 young/old 改良 LRU 缓解这个问题。

</details>

练习 4:double write 关掉,redo log 不就够用了吗?为什么还要它?

<details>

<summary>参考答案</summary>

不够。redo 是对"完好页"的增量重放;如果一个 16KB 页写到一半宕机、页本身坏了,redo 无从重放。double write 先把页完整备份一份,崩溃时先恢复出好页再重放 redo,两者是互补关系。

</details>

练习 5:为什么非唯一二级索引的写适合用 change buffer,而唯一索引不行?

<details>

<summary>参考答案</summary>

唯一索引插入时必须立刻读盘检查有没有重复值,没法延迟;非唯一二级索引的修改可以先记在 change buffer 里,等页被读到时再 merge,省掉一次随机读,所以它能加速。

</details>

练习 6:一次普通的 UPDATE 语句,会依次经过 InnoDB 的哪些组件?

<details>

<summary>参考答案</summary>

先在 Buffer Pool 里找到数据页(没有就从磁盘读入)→ 修改页(脏页)→ 写 redo log(prepare)→ Server 写 binlog → redo commit → 对二级索引的修改若页不在内存则进 change buffer → undo 记录旧版本供回滚和 MVCC → double write 保证页落盘安全。

</details>

复习清单

| 题目 | 核心结论 | 易错点 |

|---|---|---|

| 1 B+Tree 选型 | 矮胖+叶子链表,匹配磁盘 IO 和范围查询 | 说不出为什么不用哈希/B 树 |

| 2 redo WAL | 先日志后数据页,循环写,崩溃重放 | 漏说 write pos/checkpoint |

| 3 undo 与 purge | 回滚+版本链,purge 清无用版本,长事务会拖垮 | 不知道 purge 作用 |

| 4 Buffer Pool LRU | young/old 分区防全表扫描污染缓存 | 说不出 midpoint 插入 |

| 5 change buffer | 缓存非唯一二级索引写,merge 落盘 | 以为唯一索引也能用 |

| 6 核心参数 | buffer pool/双 1,按性能安全权衡调 | 只会背数字不讲权衡 |

| 7 聚簇与二级叶子 | 聚簇存整行,二级存主键,故回表 | 把二级叶子说成存整行 |

| 8 double write | 先写备份区再写数据文件,防 16KB 页写一半损坏 | 以为和 redo 重复设计 |

🧭 全篇收束:把这 8 题串起来就是 InnoDB 一次写操作的完整链路——Buffer Pool 改页、redo 记日志、change buffer 缓存二级索引、double write 兜底页安全、undo 留版本。面试时能主动画出这条链路并指出每个组件权衡了什么,就达到了大厂原理题的加分标准。建议对着复习清单逐题自测,答不出来的回到对应小节重画一遍图,把抽象的组件变成脑子里的一张图。

>

复习节奏建议:第一遍只看"核心结论"列能否脱口而出;第二遍把每个"易错点"用自己的话反着讲一遍(比如"为什么二级索引叶子不能存整行");第三遍合上书,从头默写一次 UPDATE 的组件链路,能默对就算过关。

下一篇预告:第 33 章 MySQL踩坑:安装与配置篇

— 全文完 —回到顶部 ↑
下载推广海报

文章推广海报

《第 32 章 大厂MySQL面试:原理深挖》完整推广海报
DISCUSSION

文章回复

0 条公开回复
未登录回复需要审核后公开
还没有回复,欢迎参与讨论。