MySQL的Hash索引是基于哈希表数据结构实现的索引类型,主要通过哈希函数将索引键值转换为哈希值,再映射到对应的数据位置,从而实现快速的数据查询。目前MySQL中只有Memory存储引擎显式支持Hash索引,InnoDB存储引擎的自适应哈希索引是引擎内部自动优化的功能,用户无法直接创建。

Hash索引的底层结构
Hash索引的核心结构是哈希表,整体由哈希数组和链表两部分组成:
- 哈希数组:是一个固定长度或者动态扩容的数组,数组的每个元素称为哈希桶,用来存储哈希值对应的数据指针或者链表头节点。
- 冲突链表:当不同的索引键经过哈希函数计算后得到相同的哈希值时,就会产生哈希冲突,这些冲突的键值会以链表的形式挂载到同一个哈希桶下。
以Memory存储引擎的Hash索引为例,其结构可以简化为如下逻辑:
-- 模拟Hash索引的存储逻辑,非实际执行代码 -- 哈希数组长度为8,索引键为user_id,对应数据行指针 哈希数组[0] -> 链表: (user_id=5, 指针=0x123) -> (user_id=13, 指针=0x456) 哈希数组[1] -> 链表: (user_id=1, 指针=0x789) 哈希数组[2] -> NULL 哈希数组[3] -> 链表: (user_id=3, 指针=0xabc) 哈希数组[4] -> NULL 哈希数组[5] -> 链表: (user_id=7, 指针=0xdef) 哈希数组[6] -> NULL 哈希数组[7] -> 链表: (user_id=9, 指针=0x111)
Hash索引的工作流程
Hash索引的查询工作可以分为三个核心步骤:
1. 计算哈希值
当执行带索引的查询时,存储引擎会先取出查询条件中的索引键值,将其传入哈希函数进行计算,得到对应的哈希值。比如查询user_id=5的记录,就会对5进行哈希计算得到哈希值。
2. 定位哈希桶
拿到哈希值后,会对哈希数组的长度取模,得到该哈希值对应的哈希桶位置。假设哈希数组长度为8,计算得到的哈希值是0,那么就会定位到哈希数组的第0个桶。
3. 遍历冲突链表匹配键值
如果哈希桶下挂载了冲突链表,就需要遍历链表中的每个节点,对比节点的索引键值和查询的键值是否完全一致,匹配到之后就通过指针找到对应的数据行。如果哈希桶为空,说明没有对应的索引记录,直接返回空结果。
对应的查询逻辑代码模拟如下:
# 模拟Hash索引查询逻辑
class HashIndex:
def __init__(self, bucket_size):
self.bucket_size = bucket_size
self.buckets = [None] * bucket_size # 哈希数组
def calc_hash(self, key):
# 简单哈希函数,实际MySQL的哈希函数更复杂
return hash(key)
def get_bucket_index(self, hash_val):
# 取模定位哈希桶
return hash_val % self.bucket_size
def search(self, target_key):
hash_val = self.calc_hash(target_key)
bucket_idx = self.get_bucket_index(hash_val)
node = self.buckets[bucket_idx]
# 遍历冲突链表
while node:
if node.key == target_key:
return node.data_ptr
node = node.next
return None
# 节点结构
class HashNode:
def __init__(self, key, data_ptr):
self.key = key
self.data_ptr = data_ptr
self.next = None
Hash索引的特点和局限性
Hash索引有非常鲜明的特性,开发者需要根据业务场景判断是否适用:
| 特性 | 说明 |
|---|---|
| 查询效率 | 等值查询速度极快,时间复杂度接近O(1),只要没有严重的哈希冲突,性能远优于B+树索引 |
| 范围查询支持 | 不支持范围查询,因为哈希函数计算后的哈希值没有顺序性,无法快速定位范围边界 |
| 排序支持 | 无法用于排序,哈希索引的存储顺序和索引键值的原始顺序无关 |
| 哈希冲突影响 | 哈希冲突严重时,冲突链表会变长,查询时间复杂度会退化为O(n),性能下降明显 |
| 联合索引支持 | 联合Hash索引不支持部分索引键查询,必须所有索引键都参与计算才能定位哈希桶 |
Hash索引和B+树索引的对比
MySQL中最常用的索引是B+树索引,和Hash索引的差异如下:
- B+树索引支持等值查询、范围查询、排序、模糊查询等多种查询场景,Hash索引只支持等值查询。
- B+树索引的查询性能稳定,时间复杂度是O(log n),Hash索引的查询性能依赖哈希冲突情况,波动较大。
- Memory存储引擎的Hash索引适合存储临时数据、等值查询多的场景,比如缓存类的临时表,而大部分业务表的索引更适合选择B+树索引。
注意:InnoDB的自适应哈希索引是引擎自动根据查询频率将热点B+树索引页转换为哈希结构,不需要用户手动干预,也不支持用户自定义创建Hash索引。