在数据库系统的存储引擎里,索引直接决定了查询能跑多快。B树索引和哈希索引是两类最基础也最容易被混淆的结构,它们底层组织数据的方式完全不同,适用的业务语句也有明显边界。

一、底层结构原理差异
B树索引是一种平衡多路搜索树,通常以B+树形态落地。所有真实数据记录或行指针挂在最底层的叶子页上,叶子页之间用双向链表串起来。上层非叶子页只存键值和子页指针,用来收窄查找范围。因为键值在页内有序,所以不仅能做等于判断,还能顺着链表做大于、小于、Between这类范围遍历。
哈希索引则完全另辟蹊径。它通过对索引列计算哈希函数,把记录塞进对应哈希桶。桶内部可能是链表或动态数组。查找时必须先算哈希再定位桶,整个过程与数据大小无关,理想情况下是O(1)。但这种结构从根上丢掉了顺序性,也就没法支撑任何依赖排序的检索。
// 简化的哈希桶定位逻辑
int bucket = Math.abs(key.hashCode()) % bucketCount;
List<Row> list = buckets[bucket];
for (Row r : list) {
if (r.key.equals(key)) { // 桶内再依次比对
return r;
}
}
二、查询路径与性能表现
对于单行等值查询,例如 where user_id = 123,哈希索引往往更短平快。它不需要从树根一层层下钻,算完哈希直接进桶。在内存表或热点主键场景下,这种常数级延迟很有吸引力。MySQL的Memory引擎、Redis的字典结构都依赖类似思路。
但当语句变成 where user_id > 100 and user_id < 200,哈希索引就无能为力了,只能全桶扫描。B树索引则可以定位到100所在的叶子页,沿着链表一直读到200之前,天然适配。下面用伪代码展示B树范围扫描的游走方式:
-- B树索引可优化的范围语句 SELECT * FROM orders WHERE amount >= 1000 AND amount <= 5000 ORDER BY amount; -- 哈希索引下,优化器通常只能放弃索引走全表
| 对比维度 | B树索引 | 哈希索引 |
|---|---|---|
| 等值查询 | O(log n) | O(1) 理想 |
| 范围查询 | 支持 | 不支持 |
| 排序利用 | 可利用序 | 不可利用 |
| 哈希冲突 | 无 | 有,需桶内比对 |
三、落地选型与避坑建议
实际建表时,InnoDB虽然叫聚簇索引,但本质仍是B+树,并不提供原生哈希索引,只支持自适应哈希加速。如果你用的是Memory引擎,默认就是哈希索引,做范围统计会踩坑。这时应显式声明 using btree,例如 create index idx_amt using btree on t(amt)。
另一个常见误区是拿哈希索引做复合键的前缀查询。因为哈希是对整行键组合算值,单独用前面一列去查,哈希值对不上,索引直接失效。B树则允许最左前缀匹配。所以写业务SQL前,先想清楚主要是点查还是报表区间查,再回头决定索引类型,比事后调慢查询日志要省心得多。
-- Memory引擎改造成B树索引示例
CREATE TABLE session_cache (
sid VARCHAR(64),
uid INT,
payload TEXT
) ENGINE=MEMORY;
CREATE INDEX idx_uid USING BTREE ON session_cache(uid);
四、小结
哈希索引胜在单点极速,弱在失去顺序;B树索引以略高的等值成本为代价,换来了范围、排序与最左前缀的全面能力。理解两者在页分裂、桶定位和链表游走上的区别,才能把索引建在真正的读写热路径上。
B_tree_indexhash_indexdatabase修改时间:2026-08-08 15:00:30