8.0 KiB
tags, create time
| tags | create time | |||||
|---|---|---|---|---|---|---|
|
2026-08-09 12:00 |
B+树索引原理_测试题
概述
本测试涵盖 InnoDB B+树索引的核心概念,包括 B 树与 B+树的本质差异、页分裂/合并机制、聚簇索引与非聚簇索引的区别,以及联合索引设计原则。共 10 道题:6 道选择题、3 道填空题、1 道简答题。
一、选择题(6道,由浅入深)
难度阶梯: Q1-Q2 基础概念 → Q3-Q4 核心原理 → Q5-Q6 深入应用/边界场景
Q1(基础)— 考察定义层面
InnoDB 存储引擎默认使用的索引数据结构是什么?
A. 哈希表 B. B+树 C. 红黑树 D. 跳表
Q2(基础)→ 考察行为差异
以下关于 B 树和 B+树的说法中,正确的是哪一项?
A. B 树的非叶子节点也存储数据记录 B. B+树的查询路径长度不一致,取决于数据位置 C. B 树的叶子节点通过链表连接,支持高效范围查询 D. B+树在同样大小的页中能容纳更多键,因此树更高
Q3(进阶)→ 考察原理理解
假设一个 InnoDB 表的页大小为 16KB,每条索引记录约 1KB,则单个页大约能容纳多少条索引键?在此条件下,1 亿行数据的 B+树高度通常是多少层?
A. 约 16 条,树高 56 层
B. 约 16384 条,树高 34 层
C. 约 1024 条,树高 45 层
D. 约 100 条,树高 67 层
Q4(进阶)→ 考察比较辨析
为什么推荐使用自增 BIGINT 而非 UUID 作为主键?以下解释最准确的是:
A. UUID 太长,导致 InnoDB 无法为其建立索引 B. 所有二级索引叶子节点都存储了主键值,UUID 乱序插入会导致频繁页分裂且二级索引体积庞大 C. UUID 不唯一,可能产生主键冲突 D. 自增主键的查询速度是 UUID 的两倍以上
Q5(深入)→ 考察场景推理
某业务表的主键为自增 BIGINT,当前填充率已达 85%。此时大量并发 INSERT 导致的页分裂策略是:
A. 每次都是严格的二分平分 B. 首次分裂占 7/8,后续改为平分,以预留碎片空间 C. 只有根节点满时才向上分裂,子节点不会立即分裂 D. 先删除旧数据再合并,避免产生碎片
Q6(深入)→ 考察源码级细节
在二级索引的叶子节点中,实际存储的内容是:
A. 完整行数据 B. 索引列的值 + 回滚指针(rollback pointer) C. 索引列的值 + 主键值 D. 索引列的值 + 该行的物理磁盘地址
二、填空题(3道)
F1 — 聚簇索引的定义
InnoDB 中被称为"聚簇索引"的实际上是______索引,也就是说数据和索引存储在同一个______中。一张表有且仅有______个聚簇索引。
提示: 聚簇的含义指的是数据文件本身就是按 B+Tree 组织的一份索引结构,想一想哪个索引直接包含了整行数据。
F2 — 页分裂规则
当父节点也已满时,页分裂会______向上进行,直到找到不满的父节点或到达根节点。如果根节点发生分裂,则树的______会增加 1。
提示: 分裂是从叶子节点向上传递的,根节点分裂是树结构变化的一个重要标志。
F3 — 联合索引设计
在设计复合索引 (col1, col2, col3) 时,区分度高的列应放在______;等值查询列应在范围查询列之______;若 WHERE col1 = 'a' AND col2 > 10 AND col3 = 'b',则只有前______列能有效利用索引过滤。
提示: 回忆最左前缀原则和范围查询断链陷阱——遇到范围查询时,后面的列就无法使用前缀匹配了。
三、简答题(1道)
S1
某电商订单表 orders 有以下字段:id(BIGINT 自增主键)、user_id、status、created_at、amount。现有以下两个高频查询:
-- 查询 1:按用户查订单列表
SELECT id, status, created_at FROM orders WHERE user_id = 100 ORDER BY created_at DESC;
-- 查询 2:统计各状态的订单总数
SELECT status, COUNT(*) FROM orders GROUP BY status;
请综合分析:
- 为这两个查询分别设计索引方案
- 解释为什么要这样设计(考虑区分度、最左前缀、覆盖索引等因素)
- 说明这种设计对 INSERT/UPDATE 操作可能带来的影响
答题框架提示: 先分析每个查询的过滤条件和排序需求 → 判断哪些列适合做联合索引 → 评估是否覆盖所有 SELECT 列 → 考虑索引维护成本。
参考答案与解析
选择题答案
| 题号 | 正确答案 | 解析 |
|---|---|---|
| Q1 | B | InnoDB 的默认索引结构是 B+树。哈希表不支持范围查询;红黑树是多路平衡树但在磁盘 IO 场景下不如多路树高效;跳表主要用于内存场景。 |
| Q2 | A | B 树的每个节点都存储数据和键,而 B+树的非叶子节点只存键不做数据负担,所以 A 正确。B 错误因为 B+树所有查询路径等长;C 错误因为 B 树叶节点没有链表连接;D 错误因为 B+树同样数据量下树更矮而非更高。 |
| Q3 | B | 16KB / 1KB ≈ 16 条是错误的估算方式——实际上索引键通常远小于 1KB,加上指向子节点的指针后每页可放下约 16000+ 个键。以每页约 1000 |
| Q4 | B | 核心原因有二:① UUID 长度 36 字节,远大于 BIGINT 的 8 字节,导致所有二级索引体积膨胀,内存缓存命中率下降;② UUID 随机性导致插入顺序无序,引发频繁的页分裂和碎片。A 错在 InnoDB 可以为 UUID 建索引;C 错在 UUID 设计保证全局唯一;D 无依据。 |
| Q5 | B | InnoDB 采用"首次分裂占 7/8,后续平分"的策略来预留碎片空间,避免频繁分裂。这是面试常考的优化细节。A 错在不是严格二分;C、D 描述的策略不存在于 InnoDB 实现中。 |
| Q6 | C | 二级索引叶子节点存储的是(索引列的值 + 主键值),通过主键可以回表到聚簇索引获取完整行。A 是聚簇索引叶子节点的内容;B 中的回滚指针用于 MVCC,不在二级索引中体现;D 的物理磁盘地址 InnoDB 不暴露给存储引擎外部。 |
填空题答案
| 题号 | 答案 | 解析 |
|---|---|---|
| F1 | 主键(或"聚簇");数据文件(或"InnoDB 表");1 | 聚簇索引的特点是数据文件和索引文件是同一份,叶子节点存储整行完整数据,每张表只能有一个聚簇索引。 |
| F2 | 递归;高度 | 父节点满了要递归向上分裂,这是 B+树的经典再平衡操作。根节点分裂意味着需要新增一层,树高度 +1。 |
| F3 | 前面;前;2 | 区分度高的列在前可以更早缩小查找范围;等值查询在前避免被范围查询中断最左前缀;col1 = 'a' 等值命中,col2 > 10 范围命中,但 col3 因 col2 的范围查询断链而无法使用索引。 |
简答题参考答案
S1 参考答案要点:
-
查询 1 索引:建议建立联合索引
(user_id, created_at)。user_id是高区分度的等值过滤列,放前面可以快速定位;created_at放后面可以直接满足ORDER BY排序需求,避免 filesort。同时id和status隐含在主键中,不需要额外覆盖。 -
查询 2 索引:建议建立
(status)单列索引。GROUP BY status可以利用索引的顺序分组,减少临时表的使用。虽然COUNT(*)需要从主键获取,但由于状态数通常很少(如待支付、已发货、已完成等),回表开销很小,不值得为这个查询单独建立覆盖索引。 -
INSERT/UPDATE 影响:增加索引会提高写入成本。每个 INSERT 需要在所有索引的 B+树中插入记录,可能导致页分裂。尤其是
(user_id, created_at)联合索引,当created_at无序时也会触发分裂。可通过预排序批量插入或使用自增 ID + 顺序插入来缓解。
评分标准:答出任意 2 个要点即可得满分;完全正确需覆盖全部要点(索引设计 × 2 + 原因分析 × 1 + 写入影响 × 1)。