哈希表

MySQL创建索引的时候为什么不选择Hash索引?

作者:Java野生程序猿 / 关注公众号:The-Retired-King  发布:2019-11-01

微信公众号:Java野生程序猿如有问题或建议,请公众号留言
我们都知道InnoDB存储引擎支持以下几种常见的索引:
B+树索引
哈希索引
全文索引
我们在mysql中常用两种索引算法BTree和Hash,两种算法检索方式不一样,对查询的作用也不一样。
B+树的理解看《MySQL深入浅出索引》进行了解。
Hash索引结构的特殊性,其检索效率非常高,索引的检索可以一次定位,不像B-Tree索引需要从根节点到叶子节点,最后才能访问到页节点这样多次的IO访问,所以 Hash 索引的查询效率要远高于 B-Tree 索引。
哈希索引(hash index)基于哈希表实现,只有精确匹配索引所有列的查询才有效。对于每一行数据,存储引擎都会对所有的索引列计算一个哈希码(hash code),哈希码是一个较小的值,并且不同键值的行计算出来的哈希码也不一样。哈希索引将所有的哈希码存储在索引中,同时在哈希表中保存指向每个数据行的指针。
既然 Hash 索引的效率要比 B-Tree 高很多,为什么大家不都用 Hash 索引而还要使用 B-Tree索引呢?
Hash 索引仅仅能满足"=","IN"和"<=>"查询,不能使用范围查询
由于 Hash 索引比较的是进行 Hash 运算之后的 Hash 值,所以它只能用于等值的过滤,不能用于基于范围的过滤,因为经过相应的 Hash 算法处理之后的 Hash 值的大小关系,并不能保证和Hash运算前完全一样。
Hash 索引无法被用来避免数据的排序操作
哈希索引数据并不是按照索引值顺序存储的,所以也就无法用于排序。
Hash 索引中存放的是经过 Hash 计算之后的 Hash 值,而且Hash值的大小关系并不一定和 Hash 运算前的键值完全一样,所以数据库无法利用索引的数据来避免任何排序运算。
Hash 索引不能利用部分索引键查询
哈希索引不支持部分索引匹配查找,因为哈希索引始终是使用索引的全部内容来计算哈希值的。
例如,在数据列(A,B)上建立哈希索引,如果查询只有数据列A,则无法使用该索引。
Hash 索引在任何时候都不能避免表扫描
Hash 索引是将索引键通过 Hash 运算之后,将 Hash运算结果的 Hash 值和所对应的行指针信息存放于一个 Hash表中,由于不同索引键存在相同 Hash 值,所以即使取满足某个 Hash 键值的数据的记录条数,也无法从 Hash索引中直接完成查询,还是要通过访问表中的实际数据进行相应的比较,并得到相应的结果。
Hash 索引遇到大量Hash值相等的情况后性能下降
有很多哈希冲突(不同的索引列值却有相同的哈希值)。当出现哈希冲突的时候,存储引擎必须遍历链表中所有的行指针,逐行进行比较,直到找到所有符合条件的行。
如果哈希冲突很多的话,一些索引维护操作的代价也会很高。
例如,如果在某个选择性很低(哈希冲突很多)的列上建立哈希索引,那么当从表中删除一行时,存储引擎需要遍历对应哈希值的链表中的每一行,找到并删除对应的引用,冲突越多,代价越大。
因为这些限制,哈希索引只适用于某些特定的场合。
值得一提的是InnoDB 引擎有一个特殊的功能叫做“自适应哈希索引”,当 InnoDB注意到某些索引值被使用得非常频繁时,它会在内存中基于B-Tree索引之上再创建一个哈希索引,这样就让B-Tree索引页具有哈希索引的一些优点,比如快速的哈希查找。
这仅是数据库自身创建并使用的,DBA本身并不能对其进行干预。自适应哈希索引索引经哈希函数映射到一个哈希表中,因此对于字典类型的查找非常快速。
通过命令 SHOW ENGINE INNODB STATUS 可以看到当前自适应哈希索引的使用状况,如图
现在可以看到自适应哈希索引的使用信息了,包括自适应哈希索引的大小、使用情况、每秒使用自适应哈希索引索引的情况。
需要注意的是,哈希索引只能用来搜索等值的查询。而对于其他查询类型,如范围查询,是不能使用哈希索引的。
因此,这里出现了non-hash searches/s的情况。
通过hash searches:non-hash searches可以大概了解使用哈希索引后的效率。
由于自适应哈希索引是由InnoDB存储引擎自己控制的,可以通过参数innodb_adaptive_hash_index来禁用或者启动此特性,默认为开启。
如果您喜欢,分享一下让更多的人学习
请点击标题下方的“Java野生程序猿”添加关注
或者长按下方的二维码添加关注


本文作者 :Java野生程序猿

关注Ta的微信公众号获取更多图文精彩内容...