深入理解MySQL——哈希索引

哈希索引

哈希索引(hash index)基于哈希表实现,只有精确匹配索引所有列的查询才有效。对于每一行数据,存储引擎都会对所有的索引列计算一个哈希码(hash code),哈希码是一个较小的值,并且不同键值的行计算出来的哈希码也不一样。哈希素引将所有的哈希码存储在索引中,同时在哈希表中保存指向每个数据行的指针。

在 MySQL中,只有 Memory 引擎显式支持哈希索引。这也是 Memory 引擎表的默认索引类型,Memory 引擎同时也支持 B-Tree 索引。值得一提的是,Memory 引擎是支持非唯一哈希索引的,这在数据库世界里面是比较与众不同的。如果多个列的哈希值相同,素引会以链表的方式存放多个记录指针到同一个哈希条目中。

下面来看一个例子。假设有如下表∶

CREATE TABLE testhash(
	fname VARCHAR(50) NOT NULL,  
	lname VARCHAR(50) NOT NULL,  
KEY USING HASH(fname)) 
ENGINE=MEMORY;

表中包含如下数据∶

sywf@localhost:[test]>select *  from testhash;
+-------+-----------+
| fname | lname     |
+-------+-----------+
| Arjen | Lentz     |
| Baron | Schwartz  |
| Peter | Zaitsev   |
| Vadim | Tkachenko |
+-------+-----------+
4 rows in set (0.00 sec)

假设索引使用假想的哈希函数f(),它返回下面的值(都是示例数据,非真实数据);

f('Arjen') = 2323 
f('Baron') = 7437 
f('Peter') = 8784 
f('Vadim') = 2458

则哈希索引的数据结构如下∶

槽(Slot) 值(Value)
指向第1行的指针 2323
指向第4行的指针 2458
指向第2行的指针 7437
指向第3行的指针 8784

注意每个槽的编号是顺序的,但是数据行不是。现在,来看如下查询∶

ljc@localhost:[test]>SELECT lname FROM testhash WHERE fname='Peter';

MySQL先计算’Peter’的哈希值,并使用该值寻找对应的记录指针。因为f(‘Peter’)=8784,所以MySQL在索引中查找8784,可以找到指向第3行的指针,最后一步是比较第三行的值是否为’Peter’,以确保就是要查找的行。

因为索引自身只需存储对应的哈希值,所以索引的结构十分紧凑,这也让哈希索引查找的速度非常快。然而,哈希索引也有它的限制∶

  • 哈希索引只包含哈希值和行指针,而不存储字段值,所以不能使用索引中的值来避免读取行。不过,访问内存中的行的速度很快,所以大部分情况下这一点对性能的影响并不明显。

  • 哈希索引数据并不是按照索引值顺序存储的,所以也就无法用于排序。

  • 哈希索引也不支持部分索引列匹配查找,因为哈希索引始终是使用索引列的全部内容来计算哈希值的。例如,在数据列(A,B)上建立哈希索引,如果查询只有数据列A,则无法使用该索引。

  • 哈希索引只支持等值比较查询,包括=、IN()、<=>(注意<>和<=>是不同的操作)。
    也不支持任何范围查询,例如 WHERE price > 100。

  • 访问哈希索引的数据非常快,除非有很多哈希冲突(不同的索引列值却有相同的哈希值)。当出现哈希冲突的时候,存储引擎必须遍历链表中所有的行指针,逐行进行比较,直到找到所有符合条件的行。

  • 如果哈希冲突很多的话,一些索引维护操作的代价也会很高。例如,如果在某个选择性很低(哈希冲突很多)的列上建立哈希索引,那么当从表中删除一行时,存储引擎需要遍历对应哈希值的链表中的每一行,找到并删除对应行的引用、冲突越多,代价越大。

因为这些限制,哈希索引只适用于某些特定的场合。而一旦适合哈希索引,则它带来的性能提升将非常显著。举个例子,在数据仓库应用中有一种经典的"星型"schema,需要关联很多查找表,哈希索引就非常适合查找表的需求。

你可能感兴趣的:(深入理解MySQL,MySQL,哈希索引,hash,index,MySQL索引,MySQL引擎)