mysql中B+树索引和 Hash 索引有什么区别?
在 MySQL 中,B+树索引和 Hash 索引是两种完全不同的索引结构,它们各有优缺点,适用于不同的查询场景。主要区别如下:
特性 | B+树索引 | Hash 索引 |
---|---|---|
数据结构 | 多路平衡搜索树(有序结构) | 哈希表(无序结构) |
查询类型支持 | 等值查询 范围查询( > 、< 、BETWEEN )排序( ORDER BY )模糊查询(前缀匹配 LIKE 'abc%' ) | 仅支持等值查询(= 、IN() )不支持范围查询 不支持排序 不支持模糊查询 |
查询效率 | 等值查询:O(logₙN)(稳定) 范围查询:高效(叶子节点链表) | 等值查询:O(1)(理想情况下,极快) 范围查询:不支持 |
排序支持 | 天然有序(叶子节点构成链表) | 无序(需额外排序操作) |
磁盘 I/O 优化 | 适合磁盘存储(减少 I/O 次数,顺序访问友好) | 随机访问多,可能引发更多 I/O |
空间利用率 | 较高(节点填充因子高) | 较低(哈希冲突、扩容开销) |
索引列重复值 | 高效处理 | 哈希冲突影响性能 |
使用场景 | OLTP 主流选择、范围查询、排序、高并发 | 内存表、精确点查场景、临时表、自适应哈希索引 |
存储引擎支持 | InnoDB(默认)、MyISAM | 仅 Memory 引擎显式支持 (InnoDB 有自适应哈希索引) |
数据结构与基本原理
B+树索引: 基于 多路平衡搜索树。数据存储在有序的叶子节点中(形成链表),非叶子节点只存储键值和指向子节点的指针。查询从根节点开始,逐层比较键值,最终定位到叶子节点。
Hash 索引: 基于 哈希表。对索引列的值计算一个哈希码(Hash Code),然后将指向数据行的指针存储在哈希表中对应的“桶”(Bucket)里。查询时直接计算键值的哈希码,找到对应的桶(理想情况下时间复杂度 O(1)),再在桶内精确匹配(解决哈希冲突)。
支持的查询类型
B+树索引:
等值查询:
WHERE column = value
范围查询:
WHERE column > value
,WHERE column BETWEEN value1 AND value2
等。因为数据在叶子节点是有序存储的。排序:
ORDER BY column
。利用叶子节点的链表结构可以高效地按顺序获取数据。前缀匹配模糊查询:
WHERE column LIKE 'prefix%'
。可以利用索引的最左前缀特性。Hash 索引:
仅支持等值查询:
WHERE column = value
或WHERE column IN (value1, value2, ...)
。不支持范围查询: 因为哈希表本身是无序的,无法高效定位一个范围。
不支持排序: 无序性导致无法利用索引进行排序。
不支持模糊查询: 即使是最左前缀
LIKE 'abc%'
也不行,因为哈希函数是对整个值计算的。查询效率
B+树索引: 等值查询效率是
O(logₙN)
(N 是记录数,n 是节点扇出),非常高效且稳定。范围查询效率也很高,因为只需要定位到范围的起始点,然后顺序扫描叶子节点链表即可。Hash 索引: 在没有哈希冲突的理想情况下,等值查询效率是
O(1)
,理论上比 B+树更快。但是:哈希冲突会降低效率(需要遍历冲突链)。
范围查询或排序需要全表扫描,效率极低。
磁盘 I/O 友好性
B+树索引: 设计时充分考虑了磁盘存储特性。树的高度较低(通常 3-4 层就能存储海量数据),每次查询只需几次磁盘 I/O。顺序访问(范围扫描)效率高。
Hash 索引: 哈希查找本质上是随机 I/O。桶的位置由哈希函数决定,没有局部性原理。当索引无法完全放入内存时,大量的随机磁盘 I/O 会严重拖慢速度。这是 Hash 索引在磁盘数据库中应用受限的关键原因。
空间利用率
B+树索引: 空间利用率较高。节点通常填充较满(InnoDB 页默认 16KB)。
Hash 索引: 为了减少冲突,哈希表通常需要更大的空间(负载因子小于 1)。桶的数量需要精心管理,扩容(Rehashing)操作开销大。
对索引列重复值的处理
B+树索引: 可以高效处理重复键值。相同键值的记录会存储在相邻的叶子节点位置(具体实现如 InnoDB 会附加主键保证唯一性)。
Hash 索引: 大量重复键值会导致严重的哈希冲突,显著降低查询效率。
MySQL 中的实际应用
显式 Hash 索引: 只有
MEMORY
存储引擎显式支持创建 Hash 索引。因为它将数据完全存储在内存中,规避了 Hash 索引随机 I/O 的致命缺点。InnoDB 的 Adaptive Hash Index: InnoDB 存储引擎有一个自适应哈希索引特性。它是一个内置的、自动的、透明的优化机制。
原理: InnoDB 监控对表上 B+树索引的查询模式。如果发现某些索引值(或其前缀)被非常频繁地以等值查询方式访问,它会在内存中的 Buffer Pool 里为这些热点值自动创建一个 Hash 索引指向 B+树叶子节点上的真实记录。
目的: 加速这些特定热点值的查询速度,使其接近 O(1)。
用户控制: DBA 可以开启或关闭该特性(
innodb_adaptive_hash_index
),或调整其分区数(innodb_adaptive_hash_index_parts
)以降低锁争用。关键点:
它是对 B+树索引的补充,不是替代。
它只针对频繁访问的特定值。
它完全由 InnoDB 内部管理,用户无法手动创建或指定哪些列使用 AHI。
它利用的是内存 Buffer Pool,不解决磁盘 I/O 问题。
NDB Cluster 引擎: 也使用了 Hash 索引作为其主要的索引类型。