MySQL(InnoDB剖析):28---哈希算法与自适应哈希索引
原创
©著作权归作者所有:来自51CTO博客作者董哥的黑板报的原创作品,请联系作者获取转载授权,否则将追究法律责任
一、哈希算法
- 哈希算法是一种常见算法,时间复杂度为O(1),且不只存在于索引中,每个数据库应用中都存在该数据库结构
二、哈希表
- 哈希表也称散列表,由直接寻址表改进而来.先看直接寻址表 当关键字的全域U比较小时,直接寻址是一种简单而有效的技术。加入某应用要用到一个动态集合,其中每个元素都有一个取自全域U={0,1,...,m-1}的关键字。同时假设没有两个元素具有相同的关键字
- 用一个数组(即直接寻址表)T[0...m-1]表示动态集合,其中每个位置(或称槽或桶)对应全域U中的一个关键字。图5-38说明了这个问题。槽K指向集合的一个关键字为k的元素。如果该集合没有关键字k的元素,则T【k】=NULL
- 直接寻址存在一个很明显的问题。如果域U很大,在一台典型计算机的可用容量的限制下,要在机器中存储大小为U的一张表T就有点不太实际,甚至不太可能。如果实际要存储的关键字集合K相对U来说很小,那么分配给T的大部分空间都要浪费掉
- 因此,哈希表出现了,在哈希方式下,该元素处于h(k)中,即利用哈希函数h,根据关键字k计算出槽的位置,函数h将关键字域U映射到哈希表T[0...m-1]的槽位中,如下图所示
- 哈希表技术很好的解决了直接寻址遇到的问题。但是这样还是有个小问题。如上图所示两个关键字可能映射到同一个槽上。一般将这种情况称之为发生了碰撞。在数据库总一般采用最简单的碰撞解决技术,这种技术被称为链接法
- 在链接法中,把散列到同一槽中的所有元素都放在一个链表中,如下图所示,槽j中有一个指针,它指向所有散列到j的元素构成链表的头。如果不存在这样的元素,那么j为NULL
- 最后要考虑的是哈希函数,哈希函数h必须可以很好的散列,最好的情况是能避免碰撞发的发生。即使不能避免,也应该使碰撞在最小的成都下产生。一般来说,都将关键字转换成自然数,然后通过除法散列、乘法散列或全域散列来实现。数据库中一般采用触发散列的方法
- 在哈希函数的除法散列算法中,通过取k除于m的余数,将关键字k映射到m个槽的某一个去,即哈希函数为
三、InnoDB存储引擎中的哈希算法
- InnoDB使用哈希算法来对字典进行查找,其冲突机制采用链表方式,哈希函数采用除法散列方式
以InnoDB中的缓冲页为例
- 对于缓冲页的哈希表来说,在缓冲池中Page页都有一个chain指针,它指向相同哈希函数值的页。而对于除法散列,m的取值为略大于2倍的缓冲池页数量的质数
- 例如,当前参数innodb_buffer_pool_size的大小为10M,则共有640个16KB的页:
- 对于缓冲池页内存的哈希表来说,需要分配640*2=1280个槽,但是由于1280不是质数,需要取比1280略大的一个质数,应该是1399,所以在启动时会分配1399个槽的哈希表,用来哈希查询所在缓冲池中的页
- 那么InnoDB的缓冲池对于其中的页是怎么进行查找的呢?上面只是给出了一般的算法,怎么讲要查找的页转换为自然数呢?
- 其实很简单,InnoDB的表空间都有一个space_id,用户所要查询的应该是某个表空间的某个连续16KB的页,即偏移量offset。InnoDB将space_id左移20位,然后加上这个space_id和offset,即关键字K=space_id<<2+space_id+offset,然后通过除法散列到各个槽中去
四、自适应哈希索引
- 自适应哈希索引采用之前讨论的哈希表的实现方式。不同的是,这仅是数据库自身创建并使用的,DBA本身并不能对其进行干预
- 自使用哈希索引经哈希函数映射到一个哈希表中,因此对于字典类型的查找非常快速
- 通过“SHOW ENGINE INNODB STATUS”命令可以查看当前自适应哈希索引的使用状况,例如:
- 可以看到自适应哈希索引的大小、使用情况、每秒使用自适应哈希索引搜索的情况
- hash searched/s与non-hash searches/s:代表哈希索引后的使用情况与效率
- 自适应哈希索引是由InnoDB存储引擎自己控制的,因此这里的信息只供参考
- 备注:哈希索引只能用来搜索等值查询。对于范围查找就不能使用哈希索引了
-- 下面的查询语句可以使用自适应哈希索引
select * from table where index_col='xxx';
innodb_adaptive_hash_index参数