ID · 数据库面试题
DB 域主体(DB01~DB10)尚在计划中,本篇以内联为主(只写前端/全栈该懂的部分), 已有文章的地方链过去:事务一致性见 C04、数据访问见 C10、 缓存见 C13、向量检索见 DB11、SQL 写法见 速查 12。
📌 服务对象是前端 / 转全栈:目标是把"索引为什么快、事务为什么一致、缓存为什么可能脏"答清楚, 而不是背建表语法。章节顺序:索引 → 事务 → SQL 优化 → 缓存 → 分布式 → 向量检索。
高频 Top 10(不会的话优先补这些)
| # | 题目 | 去哪补 |
|---|---|---|
| 1 | 索引为什么快?B+ 树 vs 哈希索引 | 内联(本章) |
| 2 | 最左匹配原则是什么,联合索引怎么建 | 内联(本章) |
| 3 | 回表 / 覆盖索引 / 索引下推 | 内联(本章) |
| 4 | 事务 ACID,隔离级别与脏读/不可重复读/幻读 | C04 |
| 5 | MVCC 怎么实现可重复读 | 内联(本章) |
| 6 | 慢 SQL 怎么排查:EXPLAIN 看什么 | 内联(本章)+ 速查 12 |
| 7 | N+1 查询是什么,为什么 ORM 常踩 | 内联(本章)+ C10 |
| 8 | Redis 三种缓存模式,一致性怎么保证 | C13 |
| 9 | 缓存穿透 / 击穿 / 雪崩,分别怎么防 | C13 |
| 10 | 什么时候需要分库分表,代价是什么 | 内联(本章) |
一、索引:为什么快
Q:为什么 B+ 树索引比全表扫描快?
A:全表扫描要读整张表;B+ 树按有序结构组织,查询路径只要读树高那么几层(千万级数据也就 3~4 层), 每层读一个节点。B+ 树的叶子节点有序且有链表,所以范围查询(BETWEEN/ORDER BY)也很高效—— 这是它优于哈希索引的地方(哈希只能做等值查询)。
Q:最左匹配原则是什么?联合索引 (a, b, c) 哪些查询能用?
A:联合索引按"从左到右"的顺序组织,查询必须从最左列开始连续使用:
| 查询条件 | 能用索引吗 |
|---|---|
WHERE a = 1 | ✅ |
WHERE a = 1 AND b = 2 | ✅ |
WHERE a = 1 AND b = 2 AND c = 3 | ✅ |
WHERE b = 2(跳过了 a) | ❌ 用不上 |
WHERE a = 1 AND c = 3(跳过 b) | 只用 a 走索引,c 不行 |
建索引的直觉:把"等值条件"放前面,"范围/排序"放后面,高频组合优先。
Q:回表、覆盖索引、索引下推分别是什么?
A:
| 概念 | 含义 |
|---|---|
| 回表 | 索引里只有索引列,查到位置后再回主表取其它字段 |
| 覆盖索引 | 要查的字段全在索引里,不用回表(Index Only Scan),最快 |
| 索引下推 | 把 WHERE 里的条件尽量在索引层过滤,减少回表行数(如 LIKE '张%' AND age>18 提前过滤 age) |
Q:什么时候索引反而有害?
A:写操作要维护索引、索引占空间;选择性差的列(如布尔值、性别)索引收益低;对列做函数包裹会失效索引。 别"每个字段都建索引",按真实查询组合建。
二、事务与隔离
Q:ACID 分别解决什么问题?
A:原子性(要么全做要么全不做)、一致性(从一个合法状态到另一个)、隔离性(并发不互相干扰)、 持久性(提交后不丢)。最容易忽略的是"一致性是应用层要保证的"——数据库只保证约束,业务一致性靠事务范围设计。
→ 详见 C04
Q:脏读 / 不可重复读 / 幻读,和隔离级别怎么对应?
A:
| 隔离级别 | 防脏读 | 防不可重复读 | 防幻读 |
|---|---|---|---|
| 读未提交 | ❌ | ❌ | ❌ |
| 读已提交(默认) | ✅ | ❌ | ❌ |
| 可重复读 | ✅ | ✅ | ❌* |
| 串行化 | ✅ | ✅ | ✅ |
* MySQL 可重复读通过间隙锁基本防住幻读;PostgreSQL 可重复读用快照但允许序列化异常。解释一下三者: 脏读读到未提交数据;不可重复读同一查询两次结果不同(别人改了);幻读范围查询行数变了(别人插了行)。
Q:MVCC 是什么,怎么做到"读不阻塞写、写不阻塞读"?
A:MVCC = 多版本并发控制。每行有多个版本,每个事务读自己可见的快照(基于版本号判断), 写操作生成新版本而不是覆盖旧的。这样读事务不需要锁,写事务之间才需要锁——这是"读已提交/可重复读"高性能的根基。
三、SQL 优化
Q:一条慢 SQL 怎么定位?
A:顺序:先 EXPLAIN ANALYZE 看执行计划,重点看三件事:
1. 是不是 Seq Scan(全表扫)→ 缺索引
2. 有没有走 Index Scan / Index Only Scan
3. 预估行数 vs 实际行数是否偏差大(统计信息过期)然后看是否有:隐式类型转换(列上包函数)、OR/NOT IN 破坏索引、深分页 OFFSET 巨大。
→ 见 速查 12
Q:N+1 查询是什么?为什么 ORM 容易踩?
A:查出 N 条主记录后,循环里又各查一次关联 → 总共 1+N 次查询。ORM 的懒加载会隐藏这个行为。 典型症状:接口慢、数据库连接被占满。修法:JOIN 或一次批量查关联(IN (...)),再在内存里组装。
→ 详见 C10
四、缓存与 Redis
Q:Cache-Aside / Read-Through 是什么,缓存和数据库怎么保持一致?
A:Cache-Aside(旁路缓存) 是主流:读时先查缓存、miss 再查库并回填;写时先更新库、再删缓存(删而不是更新,避免并发写脏)。 Read-Through 把缓存作为入口,由缓存层负责回源。一致性本质是"缓存和库的时效差",无法零延迟,目标是让窗口尽量小。
→ 详见 C13
Q:穿透 / 击穿 / 雪崩分别是什么,怎么防?
A:
| 问题 | 现象 | 防御 |
|---|---|---|
| 穿透 | 查不存在的 key,每次打到库 | 空值缓存 + 布隆过滤器 |
| 击穿 | 热点 key 过期瞬间高并发打到库 | 互斥锁重建 + 逻辑过期 |
| 雪崩 | 大量 key 同时过期,库被打爆 | 过期时间加随机抖动 + 多级缓存 |
→ 详见 C13
五、分布式与分库分表
Q:什么时候才需要分库分表?
A:先确认真的到了瓶颈:单表几千万行且有慢查询、写并发打满单机、单库容量将尽。分库分表是最后手段, 代价很大:跨库 JOIN 没了、事务变分布式、分布式 ID、路由与扩容都麻烦。常见错误:为了"未雨绸缪"提前分, 把简单系统做成分布式复杂度。
Q:分库分表后有哪些必须处理的问题?
A:
| 问题 | 说明 |
|---|---|
| 路由键 | 按什么拆(user_id/order_id),查询必须带上 |
| 分布式 ID | 雪花算法等,保证全局唯一、趋势递增 |
| 跨库事务 | 本地消息表 / SAGA 等最终一致方案 |
| 扩容 | 取模路由会"换库",需一致性哈希或双写迁移 |
→ 详见 C04(分布式事务部分)
六、向量检索与 AI
Q:RAG 里 pgvector 和专用向量库怎么选?
A:数据量不大、已有 PostgreSQL、要和业务数据一起查 → pgvector(不用多维护一套系统); 数据量巨大(亿级)、需要极低延迟/高吞吐 → 专用向量库或分层方案。pgvector 的关键:维度要一致、 索引用 HNSW、过滤与 ANN 冲突要用对查询方式。
→ 详见 DB11
Q:为什么 embedding 维度不一致会直接报错?
A:向量列定义了固定维度(如 vector(1536)),入库/查询向量必须同维度。换 embedding 模型维度变了,就得重建列 + 重算全量数据。 这解释了"模型要定死、升级要迁移"的工程约束。
→ 详见 DB11