假设让你为MySQL选择索引结构
在开始正文之前,我先问一个问题:请问MySQL的采用的索引结构是什么?
这时候,可能会有一些小伙伴们毫不犹豫地说:B+树
了(我都背了这么久了,怎么可能回答错?)
其实这种回答是很吃亏的,或者说不应该这么笼统地回答;我们都知道MySQL中拥有多个存储引擎,常见的有InnoDB、MyISAM、Memory。而不同的存储引擎可能会采取不同的索引结构,所以在回答刚刚那个问题的时候,我们需要之前前提条件,如:对于InnoDB存储引擎来说,它的索引结构是B+树!
好了,言归正传,咱们开始今天的索引结构选择。
一、什么是索引结构?
索引结构,其实就是一种数据结构,或者说是一种数据的组织形式。
你想想,假如现在你有一大堆东西,然后你想放好它,那这时候你是不是就会考虑怎么放,放哪里了呢?其实这个怎么放的规则,就是一种索引。到时候你怎么放就怎么取。
常见的索引结构有:
- 数组
- hash
- 链表
- 树(二叉搜索树,平衡树)
- B+树
- 跳表
二、为什么不选数组?
数组是我们最熟悉和最有把握的数据结构了,那我们要不要选它来作为MySQL的索引呢?
当然不合适!
虽然说数组在查询方面如果采用二分法也是可以有比较高的效率的,但是最大的弊端就是数组插入元素比较复杂,需要将后面的所有元素往后移动;在数据比较少的时候,可能没太大问题,但是在数据库中数据量剧增的情况下,需要移动的元素就巨多了。
一个极端的情况:
一个数组组织了 1 G 的数据,现在要在下标为 0 的位置插入一个元素。。。
想想就可怕~
三、为什么不选Hash?
既然单纯的数组不行,那我们可不可以用 Hash 呢?Hash 虽然底层也是数组,但是它采用了不同于数组的实现方式——每个数据都是通过散列映射的。
其实呢,Hash 是肯定能用的,就看用在哪种存储引擎上(Memory 中使用的就是 Hash 作为索引结构)。
Hash列表通过将数据进行hash求出对应的位置index:
一般来说,使用Hash索引的缺点:
- 利用 Hash 存储的话,需要将所有的数据文件都添加到内存中,这样是比较消耗内存空间的。
- 因为里面存储的是经过处理的哈希值,所以并不支持按原数据排序
- 不支持范围查询
MEMORY存储引擎本来就是一种基于内存的存储引擎,所以它适合使用。
四、为什么不选择链表?
链表只支持顺序访问,需要不断遍历才能找到目标值,这样的话,在数据量大的情况下,查询效率很低,并不适合。
五、为什么不选择二叉搜索树?
其实这里的最大的一个问题就是:
二叉树容易造成树一边高一边低,这对整体的影响就是加深了树的深度
树的深度越深,说明加载一个数据结点需要进行的 IO 次数就越多;如上图:为了加载 0005 数据结点,则需要先加载 0001、0002、0003 、 0004;而如果是加载0.45这个数据结点的话,只需要两次 IO :0001 、 0.45
六、为什么不选择平衡二叉树?
其实平衡二叉树和二叉搜索树的两者都有对数据排序的意识,根节点往左的小,往右的大。
而平衡二叉树在这基础上,还增加了平衡的要求(左右子树的高度差不能超过 1)
每在平衡二叉树中插入一个元素,它都是需要检查树是否平衡,不平衡就需要左旋或者右旋。
(具体的旋转规则,这里就不赘述了)
如果选择平衡树做索引,查询效率还是不错的,但是对于插入和删除来说效率就比较差了,因为它需要做平衡操作。
另外,平衡二叉树也是存在树深度大的问题的。
七、为什么不选择跳表?
(因为具体的数据结构是怎样的,并不是本文讨论的重点,所以此处并不赘述;但为了小伙伴们能快速回忆起来,下面给出一张结构图)
跳表:
跳表的最底层是一个单链表,如果存着 2kw
的数据,那么如果要达到二分查找的效率的话,就需要在上面建立 2^24
个层级才能实现,而不同的层级都是分散在不同的数据页中的,这样一来就需要将近 24
次磁盘 IO,非常影响性能。
八、为什么 要 选择B+树?
B+树:
B+树是应文件系统所需而产生的B树的变形树,B+树中只有叶子结点存储数据,其他中间结点都是索引结点,不存储数据。
MySQL中InnoDB存储引擎选用 B+ 树构建索引,主要是因为 B+ 树是多叉结构,按1k一行数据,一页16kb来算,B+树存放2kw
数据也只是需要 3
层左右就可以了,查找一次数据,最多也就 3 次
磁盘 IO 。
这个相比于其他的数据结构都好很多。
主要是因为 B+ 树是多叉结构,按1k一行数据,一页16kb来算,B+树存放2kw
数据也只是需要 3
层左右就可以了,查找一次数据,最多也就 3 次
磁盘 IO 。
这个相比于其他的数据结构都好很多。
但是B+树页也存在页分裂、维护页索引等性能消耗,这也告诉我们在技术选型的时候,没有最好的、只有相对合适的,更合适的。