我们都知道在查询数据时为了避免全表扫描我们会加上索引,这样会极大的减少遍历次数,从而增加查询的效率。而索引本质是一种优化查询的数据结构。MySQL默认的索引数据结构为B+树。
有的张三可能就会问了,MySQL为什么要用B+树来作为默认的索引数据结构呢?为什么不能是hash,况且有那么多树结构,为什么偏偏选择B+树?
这个疑问我当然也有,在查阅一番资料后,且听我一一道来。
一、hash结构
加速查询速度的数据结构通常见有两种:
- hash:hash是非常常见的数据类型,如hashMap就是通过对key做hash运算后定位到key在数组上的位置,hash的查询/插入/修改/删除的平均时间复杂度都是O(1)。
- tree:如平衡二叉搜索树,它的查询/插入/修改/删除的平均时间复杂度都是O(log2(n))。
不管是读请求,还是写请求,hash都要比树型索引更快一些,那为什么索引还要用树型结构呢?
举个栗子:
SELECT * FROM student WHERE age BETWEEN 18 AND 24
当hash用于范围/排序等条件时,hash的查找的时间复杂度会退化到O(n),而树具有有序的特性,依然能保证O(log2(n))的高效率。
二、tree结构
1.二叉搜索树
1.二叉树特点
- 一个节点只能有两个子节点
- 左子节点小于本节点,右子节点大于等于本节点
2.检索过程
深度为1的节点查找次数为1
深度为2的节点查找次数为2
深度为n的节点查找次数为n
总结:因此如果树高为3,平均查找次数为(1+2+2+3+3+3)/6=2.3次
3.问题
- 如果一直插入大于父节点的子节点,那么树的右倾会很严重,导致树会变高
- 更糟糕的是如果往下层的节点插入值得话,还会造成树的分差,这无疑会让检索速度更加慢
所以我们就需要另一种树来避免这种树一直往一边倾的情况,也就是AVL平衡树。
2.AVL树
1.AVL树特点
- 不会发生一直向一边倾斜的情况,因为AVL树会自己进行旋转保持平衡
- 平衡条件:每个结点的左右子树的高度之差的绝对值(平衡因子)最多为1
2.检索过程
比如要检索的数据为7
- 比较根节点4和7的大小,7>4,继续向右查找
- 比较子节点6和7的大小,7>6,继续向右查找
- 比较子节点8和7的大小,7<8,继续向左查找
- 比较子节点7和7的大小,7=7,找到目标节点
2.问题
- AVL树保证了不会发生一直向一边倾斜的情况,当数据量小时,查询性能非常优秀,比如要查找上图的7,也只需要查询4次就可以得到结果,但是当数据量变得非常大是,AVL树会变得很高,查询次数变多,查询速度明显下降
这时,我们需要考虑的问题就是如何让这个随着数据量也来越大时让树不那么的“瘦高”,让他变得“矮胖”一点
3.B树
1.B树特点
- B树和平衡二叉树稍有不同的是B树属于多叉树又名平衡多路查找树(查找路径不只两个)
- 每个节点不仅包含数据key值,还有data值
2.B树查找过程
比如要检索的数据为7
- 比较根节点4和7的大小,7>4,继续向右查找
- 比较子节点6.8和7的大小,8>7>6,继续向下查找
- 比较子节点7和7的大小,7=7,找到目标节点
可以看到,同样是查找7,B树结构比AVL树锁查询的次数要少1次
3.疑问
优化到这里,查找性能已经非常优秀了,为什么还要引出B+树呢,请往下看。
4.B+树
1.B+树特点
- 与B树不同的是,B树的数据存储在各个节点中,而B+树将所有数据都移动到叶子节点中,而且子节点和子节点之间会有指针指向,这样可以大大提升范围查询效率,也方便遍历整个树
- 叶子之间增加了链表,获取所有节点不再需要中序遍历
- B+树的非叶子节点只存key,这样可以加大每个节点存储key值的数量,降低B+树的高度
- 通过非叶子节点查询叶子节点获取对应的数据,所有相邻的叶子节点包含非叶子节点使用链表进行组合,叶子节点是顺序排序并且相邻节点有顺序引用的关系
这里只是简单的介绍一下查询过程,具体查询/添加/删除过程可以通过数据结构模拟的过程自己动手实践一下,可以更加深入理解。
数据结构模拟地址:https://www.cs.usfca.edu/~galles/visualization/Algorithms.html