第三章 索引基本概念:B树、哈希索引、位图索引
聊到索引,很多刚入行的同学第一反应就是“查得快”。但到底怎么个快法?每种索引的脾气秉性又是什么?今天我得给你掰扯清楚。
我个人习惯把索引比作书的目录。但实际工程里,索引的玩法可比目录复杂多了。B树、哈希、位图,这三兄弟各有绝活,也各有软肋。
3.1 B树索引:最通用的老朋友
B树,不是二叉树,是平衡多路查找树。我刚开始接触B树时也犯过糊涂,以为它就是个带平衡的二叉树变种。其实完全不是一回事。
B树的核心思想:每个节点可以存多个键值,有多个分支。这样就大大降低了树的高度。你想想看,一个3层的B树就能轻松管理几千万条数据。
B树的几个关键特性:
- 有序性:数据按索引列排序存储。所以B树天然支持
ORDER BY和范围查询。 - 自平衡:插入删除时会自动调整结构,保证查询稳定。
- 多路分支:每个节点可以分很多岔,树矮,IO次数少。
我在项目中遇到过一个问题:审计日志表数据量大概5亿行,按时间戳查询某个范围,没加索引时跑了8分钟。我建了一个B树索引,查询直接降到了200毫秒。效果立竿见影。
核心优势: 最平衡,适用范围最广。大部分关系型数据库的默认索引就是B树(或其变种B+树)。
小提示: 建B树索引注意 列的区分度。我用性别列(男女)建B树索引,几乎没有效果。因为区分度低,查出来的数据量太大了。
3.2 哈希索引:等值查询的闪电侠
哈希索引,说白了就是把键值通过哈希函数算出一个位置,然后直接去这个位置找数据。你给它一个值,它立马算出位置,一步到位。
哈希索引的优点:
- 等值查询极快,常数级别。比B树还快。
- 非常适合主键或唯一键的精确匹配。
但它的致命弱点也很明显:
- 不支持范围查询:比如
WHERE create_time > '2024-01-01',哈希索引没法用。 - 不支持排序:因为数据是乱序存的。
- 哈希冲突:多个值映射到同一位置,需要链表处理,影响性能。
我记得在优化一个日志详情查询接口时,接口接收一个 trace_id 做精确搜索。当时用了几百万条数据压测,B树索引走了几百毫秒,换成哈希索引后,直接降到了 10 毫秒以内。这就是哈希索引的威力。
避坑指南: 我曾经在MySQL的InnoDB存储引擎下,看文档说用 UNIQUE 和 = 查询会自动用哈希索引。其实InnoDB不支持真正的哈希索引,它用的是自适应哈希索引(内部自动管理的 AHI),你没法手动创建。所以请确认你使用的数据库是否真的支持哈希索引(比如 MEMORY 引擎、Redis)。
| 对比维度 | 哈希索引 | B树索引 |
|---|---|---|
| 等值查询 | 极快 (O(1)) | 较快 (O(log n)) |
| 范围查询 | 不支持 | 支持 (天然有序) |
| 排序 | 不支持 | 支持 |
| 哈希冲突 | 有影响 | 无此问题 |
| 适用场景 | 主键/唯一键等值搜索 | 范围查询、排序、通配 |
3.3 位图索引:大数据分析的利器
位图索引,你可能在OLAP系统中见得比较多。它用位数组表示数据,每个位代表一行,1表示该行有该值。我最早接触它是在Oracle的数据仓库实践中。
核心思想: 把每个不同的值建一个位图。比如性别列(男、女),男对应一个位图(100101...),女对应另一个位图(011010...)。
-- 假设10行数据,性别为男的位图
行号: 1 2 3 4 5 6 7 8 9 10
男: 1 0 1 0 0 1 1 0 0 0
女: 0 1 0 1 1 0 0 1 1 0
优点:
- 存储空间极小,尤其是低基数(唯一值少的列)。
- 位运算(AND/OR)极快,适合多条件组合查询。
缺点:
- 不适合高基数列(比如用户ID、手机号)。一个值建一个位图,基数高时位图数量巨大。
- 不适合频繁更新。每一次更新都可能需要长事务或重建位图。
最佳实践: 位图索引在审计日志分析的 标签统计 场景下特别好用。比如分析“操作类型=删除 AND 状态=失败”的日志数量,位运算只需几毫秒。
3.4 如何选择?
你可能会问,我到底该用哪个?我整理了一个简单的选择指南:
- 第一步:确认你的查询类型。
- 等值查询多 → 优先考虑哈希索引(如果数据库支持)。
- 范围查询、排序多 → 必须用B树索引。
- 组合条件统计、分析和低基数列 → 位图索引。
- 第二步:确认列的数据分布。
- 高区分度(如日志ID、时间戳)→ B树、哈希。
- 低区分度(如状态码、操作类型)→ 位图。
- 第三步:确认写入压力。
- 写入频繁 → 慎用位图索引。
- 读多写少 → 位图非常香。
我的经验: 在审计日志系统中,时间列一般用B树索引(因为要查范围)。操作类型列(比如insert/delete/update),如果操作类型数量很少(不超过20种),我建议建位图索引。我曾经用位图索引替换掉B树索引后,查询性能提升了10倍以上,存储空间也节省了90%。
好了,这三种索引的脾性你应该心里有数了。没有银弹,关键是对症下药。下次建索引时,先停下来想一想:你的查询长什么样?你的数据长什么样?别一上来就建个B树完事。