MySQL哈希索引

Hash索引

在MySQL中,索引是存储在引擎层而不是服务器层实现的,所以,并没有统一的索引标准。即使多个存储引擎支持同一种类型的索引,那么他们的实现原理也是不同的。Innodb和MyISAM默认的索引是Btree索引;而Mermory默认的索引是Hash索引。

 

索引 / 存储引擎

MyISAM

InnoDB

Memory

B-Tree索引

支持

支持

支持

HASH索引

不支持

不支持

支持

R-Tree索引

支持

支持

不支持

Full-text索引

支持

支持

不支持

最常用的索引也就是B-tree索引和Hash索引,且只有MemoryNDB两种引擎支持Hash索引
 
哈希索引是基于哈希表实现,只有精确匹配索引所有列的查询才有效,对于每一行数据,存储引擎都会对所有的索引列计算一个哈希码(hash code),哈希码是一个较小的值,大部分情况下不同的键值的行计算出来的哈希码是不同的,但是也会有例外,就是说不同列值计算出来的hash值一样的(即所谓的hash冲突),哈希索引将所有的哈希码存储在索引中,同时在哈希表中保存指向每一个数据行的指针,hash很适合做索引,为某一列或几列建立hash索引,就会利用这一列或几列的值通过一定的算法计算出一个hash值,对应一行或几行数据
 
哈希索引的示意图则是这样的:

 

 

(图片源自网络)
针对上图的理解:
keys:代表创建索引的列值;
buckets: 就是计算出来的hash值和对应的数据的物理位置组成的hash表;
entries:就是代表具体的数据行;
创建hash索引后,会为每个键值通过特定的算法计算出一个哈希码(hash code),需要注意的是不同的键值计算出来的hash值可能是相同的,例上图上的 John Smith 和Sandra Dee算出来的hash值都是152,然后找到hash值为152在hash表中的存储数据的物理位置,这个位置对应着两条数据也(就是John Smith 521-1234 和Sandra Dee 521-9655),然后再次遍历这两条数据,找到需要的数据,这就解释了为啥hash冲突严重了,
hash索引效率降低的原因。
 
hash索引检索数据的过程(摘自网络)
当我们为某一列或某几列建立hash索引时(目前就只有MEMORY引擎显式地支持这种索引),会在硬盘上生成类似如下的文件:
hash值 存储地址
1db54bc745a1 77#45b5
4bca452157d4 76#4556,77#45cc…
hash值即为通过特定算法由指定列数据计算出来,存储地址即为所在数据行存储在硬盘上的地址(也有可能是其他存储地址,其实MEMORY会将hash表导入内存)。
这样,当我们进行WHERE age = 18 时,会将18通过相同的算法计算出一个hash值==>在hash表中找到对应的储存地址==>根据存储地址取得数据==>最后一步确定这行数据是否是需要查询的数据。
所以,每次查询时都要遍历hash表,直到找到对应的hash值,数据量大了之后,hash表也会变得庞大起来,性能下降,遍历耗时增加;
hash索引的适用情况:
检索时不需要类似B+树那样从根节点到叶子节点逐级查找,只需一次哈希算法即可立刻定位到相应的位置,速度非常快,但是哈希索引只适合某些特定的场景,而一旦适合哈希索引,则它带来的性能提升非常明显,除了memory引擎外,NDB引擎也支持唯一哈希索引;
 
innodb引擎有一个特殊的功能叫做自适应哈希索引,当innodb注意到某些索引值被使用的非常频繁时,它会在内存中基于btree索引之上再创建一个哈希索引,这样就让btree索引也具有哈希索引的一些优点,比如:快速的哈希查找,这是一个全自动的,内部的行为,用户无法控制或者配置,不过如果有必要,可以选择关闭这个功能(innodb_adaptive_hash_index=OFF,默认为ON)。
我们也可以在模拟innodb的方式在不支持hash索引的存储引擎上创建hash索引
思路很简单,就是在B-Tree索引的基础上创建一个伪哈希索引,这和真正的哈希索引不是一回事,因为还是使用B-Tree进行查找。但它是使用hash值而不是键本身进行索引查找,你需要做的是在查询的WHERE子句中手动指定使用哈希函数

 
 
 
 
如何处理哈希冲突

哈希冲突见另外一篇文章
 
正是因为hash表在处理较小数据量时具有无可比拟的优势,所以hash索引很适合做缓存(内存数据库)。如mysql数据库的内存版本Memsql,使用量很广泛的缓存工具Mencached,NoSql数据库redis等,都使用了hash索引这种形式。当然,不想学习这些东西的话Mysql的MEMORY引擎也是可以满足这种需求的。
 
简单地说,哈希索引就是采用一定的哈希算法,把键值换算成新的哈希值,检索时不需要类似B+树那样从根节点到叶子节点逐级查找,只需一次哈希算法即可立刻定位到相应的位置,速度非常快。
从上面的图来看,B+树索引和哈希索引的明显区别是:
    • 如果是等值查询,那么哈希索引明显有绝对优势,因为只需要经过一次算法即可找到相应的键值;当然了,这个前提是,键值都是唯一的。如果键值不是唯一的,就需要先找到该键所在位置,然后再根据链表往后扫描,直到找到相应的数据;
    • 从示意图中也能看到,如果是范围查询检索,这时候哈希索引就毫无用武之地了,由于 Hash 索引比较的是进行 Hash 运算之后的 Hash 值,所以它只能用于等值的过滤,不能用于基于范围的过滤,因为经过相应的 Hash 算法处理之后的 Hash 值的大小关系,并不能保证和Hash运算前完全一样。 
    • 同理,哈希索引也没办法利用索引完成排序,以及like ‘xxx%’ 这样的部分模糊查询(这种部分模糊查询,其实本质上也是范围查询);
    • 哈希索引也不支持多列联合索引的最左匹配规则;、
      联合索引中,Hash索引不能利用部分索引键查询。
      对于联合索引中的多个列,Hash是要么全部使用,要么全部不使用,并不支持BTree支持的联合索引的最优前缀,也就是联合索引的前面一个或几个索引键进行查询时,Hash索引无法被利用。
    • Hash 索引遇到大量Hash值相等的情况后性能并不一定就会比B-Tree索引高。 在有大量重复键值情况下,哈希索引的效率也是极低的,因为存在所谓的哈希碰撞问题
    • Hash索引任何时候都不能避免表扫描
      Hash索引是将索引键通过Hash运算之后,将Hash运算结果的Hash值和所对应的行指针信息存放于一个Hash表中,由于不同索引键存在相同Hash值,所以即使满足某个Hash键值的数据的记录条数,也无法从Hash索引中直接完成查询,哈希索引只包含哈希值和行指针,而不存储字段值,所以不能使用索引中的值来避免读取行(即不能使用哈希索引来做覆盖索引扫描),还是要通过访问表中的实际数据进行比较,并得到相应的结果。
posted @ 2021-04-26 16:26  juicejuice  阅读(3390)  评论(3编辑  收藏  举报