MySQL的B+树索引和Hash索引

我们在学习MySQL的时候有没有想过索引的目的和他的本质是什么呢?本篇文章在讲解B+树和Hash索引之前先开始谈一下我对这两个问题的看法吧^-^


索引的目的毋庸置疑就是提高查询效率了,很多书籍都是把他类比为字典前面的拼音索引,比如我们在查找'lanco'这个单词,是不是先去查找到'l'这个字母,然后再往下找到'a'这个字母,依次再找剩下的字母;试想如果没与索引,我们是不是需要从头开始翻这本词典呢?多么麻烦的一件事^-^


那索引的本质是什么呢?不妨去阅读以下官方文档,那里说了他就是一种帮助获取数据的数据结构,

Most MySQL indexes (PRIMARY KEY, UNIQUE, INDEX, and FULLTEXT) are stored in B-trees. 
Exceptions: Indexes on spatial data types use R-trees; MEMORY tables also support hash indexes; 

这是官方文档一个片段,意思是大部分索引都存储在B树种,空间数据类型还使用R树,MEMORY还支持Hash索引,emmmm这里注意官方文档说的B树其实应该是B+树,这篇文章也主要学习一下B+树和Hash索引

emmm在谈B+树之前还是先说一下磁盘IO的读写吧,要不然我觉得会对B+树只知道其一不知道其二;


*图来源于算法导论一书

如图为一个典型的磁盘驱动器,他有一个或者多个盘片组成,这几个盘片以一个固定速度绕着共同主轴旋转,驱动器通过磁臂末尾的磁头读/写盘片,磁臂可以将磁头向主轴移近或者移远,当一个给定磁头处于静止时候,下面经过的磁盘表面称为一个磁道。

而每次读取数据花费的时间可以分为寻道时间、旋转延迟、传输时间三个部分,寻道时间指的是磁臂移动到指定磁道所需要的时间;旋转延迟就是我们经常听说的磁盘转速,比如一个磁盘7200转,表示每分钟能转7200次,也就是说1秒钟能转120次,旋转延迟就是1/120/2 = 4.17ms;传输时间指的是从磁盘读出或将数据写入磁盘的时间,一般在零点几毫秒,相对于前两个时间可以忽略不计。

emm磁盘IO是非常昂贵的,所以磁盘一次IO会存取多个数据项,信息被分为一系列相等大小的在柱面内连续出现的页面并且每个磁盘读或者写一个或者多个完整的页面,页面具体大小跟操作系统有关,一般4K或8K

以上看到操作系统都在尽量减少磁盘IO,所以我们每次查找数据时能不能把磁盘IO控制在很小的数量级呢?所以此时一种多路搜索树B+树出现了(那些说数据结构没啥用的emmm^-^)


想要搞懂先看看B树,B+树是B树的变种,B树他又叫平衡多路查找树,一颗M阶的B树特性如下:

1、树中每个节点最多含有m个孩子(m>=2);

2、除了根节点和叶子结点外,其他每个节点至少有ceil(m/2)个孩子 (ceil是向上取整)

3、根如果不是叶子结点,则至少有两个孩子

4、所有叶子结点出现在同一层(即具有相同深度,树的高度)

5、每个内部节点x还包含x.length+1个指向其孩子的指针,叶节点则没有

6、每个非终端节点中包含有n个关键字信息(n,p0,k1,p1,k2,p2……,kn,pn)其中

    a. ki(i=1……n)关键字,按照升序排序

    b. pi为指向子树根节点,且指针pi指向子树中所有的节点关键字都小于ki,但都大于k(i-1)

    c. 关键字个数n必须满足[ceil(m/2)-1]<=n<=m-1


*该图片来源于https://www.cnblogs.com/vianzhang/p/7922426.html


B+树是在B树基础上的优化变种,相对B树不同点主要有下:

1、有n颗子树的节点中包含n个关键字(B树如上图是有n颗子树含n-1关键字)

2、非叶子结点只存储键值信息,所有叶子结点包含全部关键字信息(B树任何和关键字相联系的数据都和关键字放在同一节点中)

3、所有的叶子结点之间都有一个链指针


如上大概讲解清楚B树和B+树了,接下来让我们看看B+树查找过程--假设查找数据30

1、首先将磁盘块1加载到内存中,发生一次IO,二分查找确定30在28和65之间,锁定磁盘块1的p2指针

2、通过磁盘块1的p2指针将磁盘块3加载到内存,产生第二次IO,同样经过二分查找锁定磁盘块3的P1指针

3、通过2步骤将磁盘块8加载进内存,产生第3次IO,查找到所需要的数据项30,结束


如上,一半真实情况下3层B+树可以表示上百万数据,而查找只需要三次IO,性能提高了多少啊!


好吧终于到了Hash索引了,这其实是很简单的,一句话概括搞定:基于哈希表实现,对于每一行数据,存储引擎都会对所有的索引列计算出一个哈希码,将所有的哈希码存储在索引中,同时在哈希表中保存指向每个数据行指针。

emmm所以是怎么实现的啧,很简单,hash表就是一个数组和链表组成,这个数组里,我们称直接寻址表,也称为slot(槽),我们通过hash函数映射到对应槽的位置,这时候另个关键字也有可能会映射到同一个槽中,所以我们用链表链起映射到同一个槽位置的元素。


*图片来源算法导论一书


那这两者区别是什么呢?

B+树索引可用于=,>,>=,<,<=或者between运算符表达式的列比较;

如果like的参数是一个不以通配符开头的常量字符串,name该索引也可以用于like比较


Hash索引仅仅能满足"="和"<=>"查询,不能够使用范围查询(很简单吧,范围你如何hash)

Hash索引不能用于加速order by操作(emmm^-^这是hash计算后的值,和原始值没有大小之间关系)

MySQL不能确定在两个值之间大约有多少行,所以我们将MyISAM或者InnoDB表改为Hash的MEMORY表会影响某些查询

对于组合索引必须是完整的才能搜索一行(也就是说我们如果通过组合索引前面一个或者几个索引键进行查询时候,hash索引也是无法被利用)


^-^以上就是我个人学习过程的总结,如有异议欢迎提出交流,共同进步呐!


猜你喜欢

转载自blog.csdn.net/m0_37752084/article/details/79846956