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

第 40 章 面试官追问链TOP20精讲

内容摘要

真实面试不是一问一答,而是"追问链"——你答了一层,面试官顺着往下钻三层,直到你答不上来。本篇精选 20 条最高频的追问链,每条按"题干 → 初答 → 追问1 → 追问2 → 追问3"链式拆解,帮你提前把每一层都准备好。前置知识:全系列前 39 章;本系列示例基于 MySQL 8.0。

📖 本篇简介:真实面试不是一问一答,而是"追问链"——你答了一层,面试官顺着往下钻三层,直到你答不上来。本篇精选 20 条最高频的追问链,每条按"题干 → 初答 → 追问1 → 追问2 → 追问3"链式拆解,帮你提前把每一层都准备好。前置知识:全系列前 39 章;本系列示例基于 MySQL 8.0。

目录

  • 1 怎么应对追问链
  • 题目 1:索引追问链
  • 题目 2:事务追问链
  • 题目 3:锁追问链
  • 题目 4:主从复制追问链
  • 题目 5:分库分表追问链
  • 题目 6:慢SQL追问链
  • 题目 7:读写分离追问链
  • 题目 8:分布式ID追问链
  • 题目 9:扣库存追问链
  • 题目 10:缓存一致性追问链
  • 题目 11:大事务追问链
  • 题目 12:Join追问链
  • 题目 13:自增主键追问链
  • 题目 14:redo log追问链
  • 题目 15:count追问链
  • 题目 16:索引失效追问链
  • 题目 17:死锁追问链
  • 题目 18:在线DDL追问链
  • 题目 19:连接池追问链
  • 题目 20:扩容迁移追问链
  • 速查速记
  • 常见坑与注意事项
  • 复习清单
  • 练习题

1 怎么应对追问链

面试官追问本质是验证你是真懂还是背了。应对三原则:

1. 第一层给结论,别绕;

2. 被追问就往下讲机制,讲不清就诚实说"这块我理解到 X 层,再深没实践过";

3. 别硬撑——不知道比瞎编强。

| 层次 | 面试官意图 | 你的答法 |

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

| 第1层 | 会不会 | 结论 + 关键词 |

| 第2层 | 懂不懂机制 | 原理 + 流程 |

| 第3层 | 有没有实践 | 场景 + 权衡 |

下面 20 条追问链覆盖了面试最高频的"三连问"主题。建议复习方式:先遮住答案自己说一遍,再对照补漏;尤其注意每条链的第 3 层——那是区分"背过"和"做过"的分水岭。答到第 2 层合格,答到第 3 层加分。

| 信号 | 说明 |

|---|---|

| 面试官开始追问 | 你第一层答到点了,他在往下挖 |

| 连续两问"为什么" | 在验证你是真懂还是背 |

| 问"线上怎么处理" | 考你的工程实践,别只讲原理 |

| 你答不上 | 大方说"这块没深入实践",别硬编 |

题目 1:索引追问链

考点

  • 索引结构;聚簇/二级;页分裂。

参考答案

题干:MySQL 为什么用 B+ 树做索引?

初答:B+ 树矮胖(3~4 层存千万行)、叶子双向链表适合范围查询、按页读写 IO 少。

追问

  • 追问1:聚簇索引和二级索引有什么区别?答:聚簇索引叶子存整行,二级索引叶子存主键,查二级要回表。
  • 追问2:回表什么代价?怎么避免?答:多一次 B+ 树查找+随机 IO;覆盖索引把查询列包进索引免回表。
  • 追问3:那主键为什么推荐自增?答:自增顺序插尾部不页分裂;UUID 随机插入频繁页分裂、碎片。

题目 2:事务追问链

考点

  • ACID;隔离级别;MVCC。

参考答案

题干:事务的 ACID 怎么实现?

初答:原子性靠 undo、持久性靠 redo、隔离性靠锁+MVCC、一致性是目标。

追问

  • 追问1:RR 怎么实现可重复读?答:第一次 SELECT 生成 ReadView,之后复用,沿 undo 版本链找可见版本。
  • 追问2:那幻读怎么防?答:快照读 MVCC 防;当前读(for update)靠临键锁锁间隙。
  • 追问3:RC 和 RR 在实现上差在哪?答:ReadView 生成时机——RC 每次 SELECT 都新建,RR 只第一次建。

题目 3:锁追问链

考点

  • 行锁;间隙锁;死锁。

参考答案

题干:MySQL 行锁什么时候加?

初答:InnoDB 行锁加在索引记录上,当前读/更新时加,UPDATE 没命中索引会退化为锁全表。

追问

  • 追问1:除了记录锁还有什么锁?答:间隙锁(锁间隙防插入)、临键锁(记录+间隙)。
  • 追问2:间隙锁什么时候触发?答:RR 隔离级别下范围查询/当前读;RC 没有间隙锁。
  • 追问3:怎么排查死锁?答:SHOW ENGINE INNODB STATUS 看死锁日志,找两个事务互相等的锁。
-- 锁等待与死锁排查命令,本示例基于 MySQL 8.0
SHOW ENGINE INNODB STATUS;                          -- 重点看 LATEST DETECTED DEADLOCK 段
SELECT * FROM information_schema.INNODB_TRX;        -- 当前所有事务,找最老事务
SELECT * FROM performance_schema.data_lock_waits;   -- 锁等待关系:谁在等谁

题目 4:主从复制追问链

考点

  • 复制原理;延迟;脑裂。

参考答案

题干:主从复制原理是什么?

初答:主库写 binlog,从库 IO 线程拉到 relay log,SQL 线程重放。

追问

  • 追问1:为什么会主从延迟?答:单线程重放、大事务、从库性能差;8.0 并行复制缓解。
  • 追问2:延迟了业务读到旧数据怎么办?答:写后读走主库,或半同步复制。
  • 追问3:主从切换怎么防脑裂?答:同一时间只允许一个写, fencing/仲裁,老主降级为从。

题目 5:分库分表追问链

考点

  • 为什么拆;怎么拆;扩容。

参考答案

题干:什么时候要分库分表?

初答:单表千万级、单库 CPU/IO 到瓶颈,纵向升配不划算。

追问

  • 追问1:分片键怎么选?答:高离散、高频查询列,如 user_id;避免低基数列和时间片。
  • 追问2:分了之后跨库查询怎么办?答:业务规避,复杂检索走 ES,避免跨库 Join 和深分页。
  • 追问3:后续要从 8 库扩到 16 库怎么做?答:双写迁移 + binlog 追平 + 校验 + 灰度切流。

题目 6:慢SQL追问链

考点

  • 定位;EXPLAIN;改写。

参考答案

题干:线上一条 SQL 很慢怎么排查?

初答:开慢日志,pt-query-digest 找 Top,EXPLAIN 看 type/key/rows。

追问

  • 追问1:EXPLAIN 里 type=ALL 说明什么?答:全表扫描,where 条件没用到索引。
  • 追问2:加了索引为什么还全表扫?答:函数包裹列、隐式转换、OR 无索引列、优化器算过账放弃。
  • 追问3:深分页 LIMIT 100000,20 慢怎么优化?答:游标分页或延迟关联,先取主键再回表。
-- 慢SQL定位:EXPLAIN 重点字段,本示例基于 MySQL 8.0
EXPLAIN SELECT * FROM t_order WHERE user_id = 1001 ORDER BY created_at DESC LIMIT 20;
-- type=ALL  全表扫(差)
-- key=NULL  没走索引
-- rows 越大扫的越多
-- Extra=Using filesort  额外排序,要警惕

题目 7:读写分离追问链

考点

  • 主从延迟;一致性读。

参考答案

题干:读写分离怎么保证一致性读?

初答:写后读强制走主库,关键读(支付/余额)走主。

追问

  • 追问1:哪些读必须走主?答:写后立刻读、资金强一致读。
  • 追问2:全走主库主库扛得住?答:扛不住,只对必须强一致的读走主,其余走从+缓存。
  • 追问3:从库延迟大了怎么自动切?答:监控 Seconds_Behind_Master,超阈值把读切回主。

题目 8:分布式ID追问链

考点

  • 为什么;方案;时钟。

参考答案

题干:分库分表后全局唯一 ID 怎么做?

初答:号段模式或雪花算法,趋势递增。

追问

  • 追问1:为什么不用 UUID?答:UUID 随机导致 B+ 树页分裂,写入性能差。
  • 追问2:雪花算法有什么问题?答:时钟回拨。
  • 追问3:时钟回拨怎么办?答:记录上次时间戳,回拨超阈值报错或等待,号段兜底。

题目 9:扣库存追问链

考点

  • 超卖;原子;热点。

参考答案

题干:秒杀怎么防超卖?

初答:一条 SQL 原子扣减 WHERE stock>0,别先查后扣。

追问

  • 追问1:为什么先查后扣会超卖?答:两个事务都读到有货再各自扣,写覆盖写。
  • 追问2:热点商品一行扣太慢怎么办?答:库存分桶打散行锁,或前置 Redis 预扣。
  • 追问3:Redis 预扣和 DB 不一致怎么办?答:下单成功异步扣 DB,失败回补 Redis,定时对账。
-- 原子扣库存:一条SQL完成判断+扣减,本示例基于 MySQL 8.0
UPDATE t_stock
SET stock_count = stock_count - 1, update_time = NOW()
WHERE sku_id = 10086 AND stock_count > 0;  -- 受影响0行即售罄,杜绝超卖

题目 10:缓存一致性追问链

考点

  • 先更库还是删缓存;删除失败。

参考答案

题干:缓存和数据库双写怎么保证一致?

初答:先更新数据库,再删除缓存(Cache-Aside)。

追问

  • 追问1:为什么删缓存而不是更新缓存?答:更新可能并发覆盖,删除更简单安全。
  • 追问2:删缓存失败怎么办?答:失败进重试队列,或订阅 binlog 异步删。
  • 追问3:还有不一致窗口吗?答:有,加延迟双删 + TTL 兜底,强一致场景直接读库。

题目 11:大事务追问链

考点

  • 危害;拆分;消息表。

参考答案

题干:大事务有什么危害?

初答:undo 膨胀、锁持有久、主从延迟、回滚慢。

追问

  • 追问1:怎么拆?答:拆多个小事务,非核心操作异步化,批量按批提交。
  • 追问2:拆了跨服务数据怎么一致?答:本地消息表,同事务写业务表+消息表,异步投递。
  • 追问3:消费方重复消费怎么办?答:按业务唯一键幂等。

题目 12:Join追问链

考点

  • 驱动表;N+1;嵌套循环。

参考答案

题干:慢 JOIN 怎么优化?

初答:小表驱动大表,被驱动表 Join 列要有索引。

追问

  • 追问1:为什么小表驱动?答:被驱动表每次都查索引,小表驱动 = 查的次数少。
  • 追问2:什么是 N+1 查询?答:先查 N 条主表,再循环 N 次查关联,应用层组装。
  • 追问3:N+1 怎么解决?答:改成一条 JOIN,或批量 WHERE id IN (...) 一次取。

题目 13:自增主键追问链

考点

  • 为什么;页分裂;UUID。

参考答案

题干:为什么推荐自增主键?

初答:顺序插入尾部不页分裂,BIGINT 短二级索引省空间。

追问

  • 追问1:页分裂怎么影响性能?答:插入要拆页搬数据,写入抖动、磁盘碎片。
  • 追问2:UUID 做主键到底差在哪?答:随机插入位置导致频繁页分裂 + 二级索引存更长主键。
  • 追问3:分库后自增还能用吗?答:不能全局唯一,要换趋势递增的分布式 ID。

题目 14:redo log追问链

考点

  • WAL;checkpoint;两阶段。

参考答案

题干:redo log 是干嘛的?

初答:WAL,先写日志再刷数据页,crash 后重放保证已提交不丢。

追问

  • 追问1:和 binlog 什么关系?答:两阶段提交,redo prepare → 写 binlog → redo commit。
  • 追问2:为什么要两阶段?答:保证 redo 和 binlog 一致,否则主从数据脱节。
  • 追问3:checkpoint 干嘛?答:把脏页刷盘推进 redo 可复用点,缩短 crash 恢复时间。
-- 两阶段提交时序,本示例基于 MySQL 8.0
1. 写 redo log 置 prepare
2. 写 binlog
3. redo log 置 commit
-- 崩溃恢复:prepare 态看 binlog 是否完整,完整则提交,否则回滚

题目 15:count追问链

考点

  • InnoDB count 原理;优化。

参考答案

题干:count(*) 为什么慢?

初答:InnoDB 要数可见行(MVCC),不能像 MyISAM 直接读行数。

追问

  • 追问1:为什么 InnoDB 不能存行数?答:多事务下每个会话可见的行不同,不能存一个固定值。
  • 追问2:要精确总数怎么办?答:维护计数表,增删时同步更新;大数放 Redis。
  • 追问3:只要大概数呢?答:SHOW TABLE STATUS 估算,或走最小索引。

题目 16:索引失效追问链

考点

  • 函数包裹;隐式转换;左模糊。

参考答案

题干:索引为什么会失效?

初答:函数包裹列、隐式类型转换、左模糊、OR 无索引列、跳过前导列。

追问

  • 追问1:隐式转换怎么导致失效?答:列是 VARCHAR 用数字比,转换作用在列上,索引列值被改无法定位。
  • 追问2:LIKE '%x%' 怎么办?答:走覆盖索引或全文索引,否则全表扫。
  • 追问3:怎么系统排查失效?答:EXPLAIN 看 type=ALL、key=NULL,对照 WHERE 写法逐条查。

题目 17:死锁追问链

考点

  • 场景;排查;预防。

参考答案

题干:死锁怎么产生的?

初答:两个事务互相持有对方要的锁,循环等待。

追问

  • 追问1:常见死锁场景?答:两个事务以不同顺序更新多行(A 先锁1再锁2,B 先锁2再锁1)。
  • 追问2:怎么排查?答:SHOW ENGINE INNODB STATUS 看 LATEST DETECTED DEADLOCK。
  • 追问3:怎么预防?答:固定访问顺序、短事务、降低锁粒度、合理索引避免锁全表。
-- 典型死锁:两事务以相反顺序更新同一两行,本示例基于 MySQL 8.0
-- 事务A: UPDATE acc SET bal=bal-1 WHERE id=1; UPDATE acc SET bal=bal-1 WHERE id=2;
-- 事务B: UPDATE acc SET bal=bal-1 WHERE id=2; UPDATE acc SET bal=bal-1 WHERE id=1;
-- A等B持有的2,B等A持有的1,循环等待 => 死锁

题目 18:在线DDL追问链

考点

  • INPLACE;pt-osc;锁。

参考答案

题干:大表加索引怎么不锁表?

初答:ALGORITHM=INPLACE, LOCK=NONE,大表用 gh-ost/pt-osc。

追问

  • 追问1:INPLACE 完全不锁写?答:写不锁,但元数据锁窗口仍在,别在高峰。
  • 追问2:pt-osc 原理?答:建影子表 + 触发器同步增量 + 分批拷贝 + 原子换表。
  • 追问3:8.0 有什么改进?答:原子 DDL,崩溃自动回滚不留残表。

题目 19:连接池追问链

考点

  • 大小;为什么不是越大。

参考答案

题干:数据库连接池设多大合适?

初答:别贪大,参考"核数*2+磁盘数",通常几十到几百。

追问

  • 追问1:为什么不是越大越好?答:连接多了 CPU 上下文切换、锁竞争反而重。
  • 追问2:连接数打满一般什么原因?答:慢 SQL/长事务占着连接不放,不是真并发高。
  • 追问3:怎么缓解?答:kill 长事务、缩短事务、连接池合理等待超时。

题目 20:扩容迁移追问链

考点

  • 双写;校验;切流。

参考答案

题干:分库分表扩容怎么不停机迁移?

初答:双写 + 存量搬迁 + binlog 追平 + 校验 + 灰度切流。

追问

  • 追问1:双写期间一边失败怎么办?答:以一边为准,异步补偿对账,不让两分支不一致。
  • 追问2:怎么确认数据搬对了?答:count + checksum 抽样/全量比对。
  • 追问3:怎么切最稳?答:先切读流量灰度,观察无误再切写,留回滚开关。

速查速记

| 追问链 | 三层核心 |

|---|---|

| 索引 | B+树 → 回表 → 自增主键 |

| 事务 | ACID → ReadView → 临键锁 |

| 锁 | 行锁 → 间隙锁 → 死锁排查 |

| 主从 | 复制 → 延迟 → 脑裂 |

| 分库分表 | 分片键 → 跨库 → 扩容 |

| 扣库存 | 原子 → 热点分桶 → Redis |

| 缓存 | 先更库 → 删缓存失败 → 延迟双删 |

| 扩容 | 双写 → 校验 → 灰度切流 |

常见坑与注意事项

  • 坑 1:只会第一层不会第二层——背了结论但讲不清机制,一问"为什么"就露馅;
  • 坑 2:被追问时硬编——不懂就说"这块我理解到 X,再深没实践过",别瞎编;
  • 坑 3:把并列问题答成二选一——如"缓存更新还是删除"要讲清为什么删而不是更;
  • 坑 4:忽略权衡——每个方案都有代价,答了方案要带一句"代价是……";
  • 坑 5:答架构题不给兜底——只说主流程不说失败/对账/回滚,工程上不完整;
  • 注意:追问链为公开高频面试套路,答案为通用原理与工程实践。

复习清单

| 追问链 | 第一层 | 第三层(拉开差距) |

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

| 索引 | B+树好 | 自增主键为什么 |

| 事务 | ACID | ReadView 时机 |

| 锁 | 行锁 | 死锁怎么查 |

| 主从 | 三线程 | 脑裂怎么防 |

| 分库分表 | 分片键 | 扩容怎么迁 |

| 慢SQL | EXPLAIN | 深分页优化 |

| 缓存 | 先更库 | 删除失败兜底 |

| count | 为什么慢 | 计数表维护 |

| 死锁 | 循环等待 | 怎么预防 |

| 连接池 | 别贪大 | 打满根因 |

练习题

练 1:面试官问"为什么用 B+ 树",你怎么往下接住他的三连追问?

<details>

<summary>参考答案</summary>

第一层:B+ 树矮胖、叶子链表、IO 少。追问1 聚簇 vs 二级:叶子存整行 vs 存主键,二级要回表。追问2 回表代价:多一次随机 IO,覆盖索引免回表。追问3 主键设计:所以推荐自增(顺序插尾部不页分裂),UUID 随机会页分裂。

</details>

练 2:面试官问"缓存和数据库一致性",接着追问"删缓存失败怎么办""还有窗口吗",你怎么答?

<details>

<summary>参考答案</summary>

初答:先更库再删缓存。追问1 删失败:失败进重试队列,或订阅 binlog(Canal)异步删。追问2 仍有窗口(读旧库回填旧缓存):加延迟双删 + TTL 兜底;强一致场景直接读库。

</details>

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

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

文章推广海报

《第 40 章 面试官追问链TOP20精讲》完整推广海报
DISCUSSION

文章回复

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