B+ 树索引中的前缀压缩是什么?它为什么能提高页扇出?
简化版
前缀压缩是利用相邻索引 key 往往有共同前缀,只存差异部分或缩短分隔 key,从而减少索引项大小。索引项更小,一个页能放更多 key,B+ 树扇出更大,高度和 I/O 压力可能下降。
详细版
B+ 树叶子页或内部页中的 key 是有序的。相邻 key 常共享前缀,例如 URL、邮箱、字符串编码、联合索引的前几列。前缀压缩可以:
- 在叶子页中对连续 key 共享公共前缀;
- 在内部页中只保存足以区分相邻子树的最短分隔 key;
- 减少单个索引项字节数,提高页内容纳量。
代价是查找和比较时需要解码或结合上下文,CPU 成本和实现复杂度增加。它适合 key 较长且共同前缀明显的场景,对随机短整数 key 收益有限。
完整版教学
一、为什么索引 key 的大小会影响 B+ 树性能
B+ 树的核心优势是高扇出:一个页里放很多 key 和指针,每层能分出很多孩子。若 key 很长,一个页能放下的索引项就变少,扇出下降,树可能变高,缓存命中也变差。前缀压缩就是为了让每页装下更多有效索引项。
例如 16KB 页中,平均索引项 64B 时大约能放 256 项;如果压缩后平均 32B,大约能放 512 项。扇出翻倍后,内部层覆盖的数据量会显著增加。
记忆钩子:B+ 树页像货架,key 越短,同一层能摆的路标越多,树就越矮。
二、为什么有序 key 容易压缩
B+ 树页内 key 按顺序排列,相邻 key 往往相似。字符串尤其明显,比如:
user:100001:profile
user:100002:profile
user:100003:profile
它们共享 user:10000 等前缀。如果每条都完整存储,会重复浪费空间。前缀压缩可以记录公共前缀长度和差异后缀,或在页级别保存基准前缀,让每条记录只保存变化部分。
三、内部页分隔 key 为什么可以更短
内部页的 key 主要用于决定搜索走哪个子页,它不一定需要保存完整业务 key。只要某个分隔 key 能区分左子树最大范围和右子树最小范围,就足够完成导航。因此内部页可以保存最短可区分前缀。
举例:
左页最大: apple_999
右页最小: apricot_001
分隔时可能只需要 "apr" 附近的最短区分信息
真实数据库实现会非常谨慎,确保压缩后的分隔 key 不改变查找方向。这个优化能让内部页放更多分隔项,提高上层扇出。
四、前缀压缩带来的收益和代价
收益主要是空间和 I/O:页内放更多 key,索引文件更小,缓存能容纳更多索引页,树高可能降低。代价主要是 CPU 和复杂度:比较 key 时可能需要解码,插入删除时要维护压缩格式,页分裂时还要重新计算压缩边界。
| 维度 | 收益 | 代价 |
|---|---|---|
| 磁盘空间 | 索引更小 | 需要压缩元数据 |
| I/O | 页更少、缓存命中更好 | 解码增加 CPU |
| 扇出 | 每页 key 更多 | 分裂维护更复杂 |
| 查询 | 可能减少层数 | 比较逻辑更复杂 |
如果 key 很短、随机且共享前缀少,压缩收益就会很有限。
五、前缀索引和前缀压缩有什么区别
前缀索引是用户建索引时只取列的前 N 个字符,例如对 name(10) 建索引。它可能影响区分度,甚至不能完全覆盖排序或唯一性。前缀压缩是存储引擎内部为了节省页空间做的编码优化,逻辑 key 仍然是完整 key。
前缀索引: 逻辑上只索引 key 的一部分
前缀压缩: 逻辑上索引完整 key,物理上压缩存储
这两个名字像,但层次完全不同。面试里被问到时要明确区分。
六、它对写入和页分裂有什么影响
插入新 key 后,相邻 key 的公共前缀关系可能变化。页分裂时,左右页的公共前缀也会重新分布,内部页分隔 key 可能需要调整。压缩让页更省空间,但也让修改路径的维护成本增加。
例如原页里都是 user:100xxx,插入一个 user:999999 仍共享 user:,压缩收益还在;如果插入大量完全不同前缀的 key,页内压缩率下降,页可容纳项数也会变化。
七、常见误区与追问
- 误区:前缀压缩会丢失索引 key 的真实值。 它是物理存储压缩,逻辑上仍能还原或比较完整 key。
- 追问:为什么压缩能提高扇出? 单个索引项变小,同样页大小能容纳更多 key 和指针。
- 误区:前缀索引和前缀压缩是一回事。 前缀索引改变逻辑索引内容,前缀压缩只是存储编码。
- 追问:什么场景收益最大? 长字符串、共同前缀明显、联合索引前列重复度高的场景。
- 误区:压缩一定提升所有查询。 解码和维护有 CPU 成本,短随机 key 收益可能很小。
八、加强记忆
前缀压缩的核心是「有序相邻 key 常常长得像」。B+ 树页内 key 有序,重复前缀多时,把公共部分压掉能让页装更多项,提高扇出和缓存效率。但它是物理优化,不等于只索引前几个字符。记住收益在空间和 I/O,代价在 CPU 解码和页维护复杂度。