目录
- 1 Hash 索引
- Hash 索引的五条典型限制
- MySQL 实战:等值查询、范围查询、ORDER BY 排序、组合索引最左前缀
- 2 B+Tree 索引
- HEAP/MEMORY 与 InnoDB 自适应哈希索引
- 3 B+ 树索引和哈希索引的明显区别
- 六个维度对比表与选型结论
- 总结
- 参考资料
- 写在最后
摘要:本文围绕 Hash 索引与 B+Tree 索引的差异展开,先梳理 Hash 索引的五条典型限制,再用 MySQL 实战 SQL 分别验证等值查询、范围查询、ORDER BY 排序和组合索引最左前缀四类场景下 Hash 索引的可用性,随后说明 InnoDB 的自适应哈希索引机制,最后从等值查询、范围查询、排序、最左前缀匹配、哈希碰撞和适用引擎六个维度对比两种索引,并给出选型结论:只有纯等值查询且键值高度离散的场景才考虑 Hash 索引,其余业务优先选择 B+Tree 索引。
Hash 索引结构特殊,检索效率非常高,通常一次索引检索即可完成定位;
B+ 树索引则需要从根节点逐层经过枝节点,最后才能访问到叶子节点,因此需要多次 IO 访问。
那为什么大家不都用 Hash 索引,而还要使用 B+ 树索引呢?
1 Hash 索引
(1)Hash 索引仅仅能满足“=”“IN”和“<=>”查询,不能使用范围查询,因为经过相应的 Hash 算法处理之后的 Hash 值大小关系,并不能保证和 Hash 运算前完全一样;
(2)Hash 索引无法被用来避免数据的排序操作,因为 Hash 值的大小关系并不一定和 Hash 运算前的键值完全一样;
(3)Hash 索引不能利用部分索引键查询,对于组合索引,Hash 索引在计算 Hash 值的时候是组合索引键合并后再一起计算 Hash 值,而不是单独计算 Hash 值,所以通过组合索引的前面一个或几个索引键进行查询的时候,Hash 索引也无法被利用;
(4)Hash 索引在任何时候都不能避免表扫描,由于不同索引键可能存在相同的 Hash 值,所以即使取到满足某个 Hash 键值的数据记录条数,也无法从 Hash 索引中直接完成查询,还是要回表查询数据;
(5)当出现大量 Hash 值相等的情况时,Hash 索引的性能并不一定比 B+ 树索引高。
下面用一组 MySQL 实战 SQL 来验证上述限制。假设有一张用户表 t_user,并在 id 上建立 Hash 索引(MEMORY 引擎),在 name、age 上建立组合索引 idx_name_age(name, age):
-- 建表:MEMORY 引擎显式支持 Hash 索引
CREATE TABLE t_user (
id INT NOT NULL,
name VARCHAR(32),
age INT,
PRIMARY KEY (id) USING HASH, -- Hash 索引
KEY idx_name_age (name, age) USING BTREE
) ENGINE = MEMORY;
场景一:等值查询(Hash 索引可用)
-- 命中 Hash 索引:一次哈希计算即可定位到记录,效率最高
SELECT * FROM t_user WHERE id = 1001;
-- IN 本质是多个等值查询,同样可以走 Hash 索引
SELECT * FROM t_user WHERE id IN (1001, 1002, 1003);
场景二:范围查询(Hash 索引失效)
-- 无法使用 Hash 索引:哈希后键值不再有序,只能全表扫描
SELECT * FROM t_user WHERE id > 1000 AND id < 2000;
-- 同理,BETWEEN 也是范围查询,Hash 索引同样失效
SELECT * FROM t_user WHERE id BETWEEN 1000 AND 2000;
场景三:ORDER BY 排序(Hash 索引失效)
-- 无法利用 Hash 索引避免排序:哈希值大小与原始键值顺序无关,
-- MySQL 只能取出数据后再做 filesort
SELECT * FROM t_user WHERE id > 1000 ORDER BY id;
场景四:组合索引最左前缀查询(Hash 索引失效)
-- 只用到组合索引的第一列 name:Hash 索引需整体计算 (name, age) 的哈希值,
-- 无法只按 name 定位,因此该条件无法利用 Hash 索引
SELECT * FROM t_user WHERE name = 'Tom';
-- 两列都给出等值条件,组合键完整,Hash 索引才能生效
SELECT * FROM t_user WHERE name = 'Tom' AND age = 20;
小结:Hash 索引只在“完整键值的等值查询”场景下有效,一旦涉及范围查询、排序或组合索引的部分前缀,就会退化为全表扫描;实际生产中 InnoDB 默认的 B+Tree 索引能覆盖上述全部场景,这也是它成为默认选择的原因。
2 B+Tree 索引
MySQL 中,只有 HEAP/MEMORY 引擎才显式支持 Hash 索引。
常用的 InnoDB 引擎中默认使用的是 B+ 树索引,它会实时监控表上索引的使用情况,如果认为建立哈希索引可以提高查询效率,则自动在内存中的“自适应哈希索引缓冲区”建立哈希索引(在 InnoDB 中默认开启自适应哈希索引),通过观察搜索模式,MySQL 会利用 index key 的前缀建立哈希索引,如果一个表几乎大部分都在缓冲池中,那么建立一个哈希索引能够加快等值查询。
3 B+ 树索引和哈希索引的明显区别是:
如果是等值查询,那么哈希索引明显有绝对优势,因为只需要经过一次算法即可找到相应的键值;当然了,这个前提是,键值都是唯一的。如果键值不是唯一的,就需要先找到该键所在位置,然后再根据链表往后扫描,直到找到相应的数据;
如果是范围查询检索,这时候哈希索引就毫无用武之地了,因为原先是有序的键值,经过哈希算法后,有可能变成不连续的了,就没办法再利用索引完成范围查询检索;
同理,哈希索引没办法利用索引完成排序,以及 like ‘xxx%’ 这样的部分模糊查询(这种部分模糊查询,其实本质上也是范围查询);
哈希索引也不支持多列联合索引的最左匹配规则;
B+ 树索引的关键字检索效率比较平均,不像 B 树那样波动幅度大,在有大量重复键值情况下,哈希索引的效率也是极低的,因为存在所谓的哈希碰撞问题。
在大多数场景下,都会有范围查询、排序、分组等查询特征,用 B+ 树索引就可以了。
| 对比维度 | Hash 索引 | B+Tree 索引 |
|---|---|---|
| 等值查询 | 一次哈希定位,效率极高(键值唯一时最优) | 从根节点逐层下降到叶子节点,效率稳定 |
| 范围查询 | 不支持,哈希后键值不再有序 | 支持,叶子节点有序且通过链表相连 |
| 排序 | 不支持,无法利用索引避免排序 | 支持,叶子节点天然有序 |
| 最左前缀匹配 | 不支持,组合索引键需整体计算哈希值 | 支持,可只使用联合索引的前缀列 |
| 哈希碰撞 | 存在,大量重复键值时性能急剧下降 | 不存在该问题,检索效率波动小 |
| 适用引擎 | 仅 HEAP/MEMORY 显式支持;InnoDB 仅提供自适应哈希索引 | InnoDB、MyISAM 等主流引擎默认支持 |
选型结论:只有纯等值查询且键值高度离散的场景才考虑 Hash 索引,其余涉及范围查询、排序、分组或联合索引前缀匹配的业务,优先选择 B+Tree 索引。
总结
Hash 索引通过一次哈希计算直接定位记录,在键值唯一且完整的等值查询场景下效率极高,但哈希后键值不再有序,因此无法支撑范围查询、排序和联合索引的最左前缀匹配,遇到大量重复键值时还会因哈希碰撞导致性能急剧下降。
B+Tree 索引从根节点逐层下降到叶子节点,叶子节点天然有序并通过链表相连,等值查询、范围查询、排序和前缀匹配都能覆盖,检索效率稳定,这也是 InnoDB 默认选择 B+Tree 的原因。
选型原则可以概括为一句话:只有纯等值查询且键值高度离散的场景才考虑 Hash 索引,其余涉及范围查询、排序、分组或联合索引前缀匹配的业务,优先使用 B+Tree 索引。
参考资料
- MySQL 官方文档:The MEMORY Storage Engine(说明 MEMORY 引擎默认使用 Hash 索引,以及
USING HASH/USING BTREE的索引定义方式) - MySQL 官方文档:InnoDB Adaptive Hash Index(说明 InnoDB 自适应哈希索引的开启方式、工作原理与适用条件)
- MySQL 官方文档:Clustered and Secondary Indexes(说明 InnoDB 中 B+Tree 聚簇索引与二级索引的结构)
写在最后
如果这篇内容帮你理清了 Hash 索引与 B+Tree 索引的区别,欢迎来一套一键三连:点赞、收藏、评论,你的每一次互动都是持续更新的动力。
也欢迎点个关注,后续会继续分享 MySQL 索引原理、执行计划分析和 SQL 优化实战等系列内容,第一时间收到更新提醒。
- 点赞:让更多正在踩索引坑的同学看到这篇总结;
- 收藏:面试或排查慢查询时随时翻出来对照;
- 评论:有疑问或不同见解,欢迎在评论区一起讨论;
- 关注:不错过后续的数据库与后端实战干货。
感谢阅读,我们下篇见!
回顾
读到这里,可以把全文压缩成下面几条要点,方便快速复盘:
- Hash 索引的本质:对索引键整体做一次哈希计算,直接定位到记录所在位置,等值查询通常一次检索即可完成,效率极高。
- 五条典型限制:只支持“=”“IN”“<=>”等值查询;无法避免排序;不能利用组合索引的部分前缀;无法避免回表;大量重复键值时会因哈希碰撞导致性能下降。
- 实战验证结论:等值查询和 IN 可以走 Hash 索引;范围查询(>、<、BETWEEN)、ORDER BY 排序、组合索引只给最左列条件时,Hash 索引都会失效,退化为全表扫描。
- 引擎支持情况:只有 HEAP/MEMORY 引擎显式支持 Hash 索引;InnoDB 默认使用 B+Tree,并通过自适应哈希索引在内存中为热点等值查询加速。
- 选型原则:只有纯等值查询且键值高度离散的场景才考虑 Hash 索引,其余涉及范围查询、排序、分组或联合索引前缀匹配的业务,优先选择 B+Tree 索引。
一句话记住:Hash 索引赢在“点查”,B+Tree 索引赢在“通用”。理解了这一点,再看执行计划里的 type 和 key 字段,就能判断索引到底有没有被用上。
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/qq_36506963/article/details/169565571



