以下文章来源于小龙coding ,作者小龙coding
如有侵权,联系删除
不少同学私信,问其面试常问的一个经典问题:"MySQL 索引为何选用 B+ 树" 思路远比结论重要,今天简单聊聊索引为何这样设计?
何为索引:以图书馆为例,需借助检索目录,以加快书本查询定位;同理,MySQL索引也即为排好序的一种数据结构,用于提升数据库的查找速度。
哈希(hash)比树(tree)更快,索引结构为什么要设计成树型?
知其然,知其所以然。分析两种数据结构时间复杂度:
(1)哈希(Hash)结构,例如 HashMap
,查询、删除、插入、修改
的平均时间复杂度都是 O(1);
(2)树(tree)结构,例如平衡二叉搜索树,查询、删除、插入、修改
的平均时间复杂度都是 O(logN) ;
从中分析可见,若用 Hash
类型的索引结构,无论是在读请求,还是写请求,都比 树(tree)
类型的索引结构快;、
那,为何 MySQL 索引结构还设计成树型结构呢?
其一:若是简单的读写请求,Hash结构效果当然更佳;Hash 底层采用哈希表实现,等值查询,可以快速定位,一般情况效率很高。但也不稳定,当出现大量键重复哈希冲突,效率下降。
其二:MySQL 需要满足更多场景的 SQL 查询需求。Hash不支持范围查询,无法用于排序分组(grou by),无法模糊查询(like %),多列索引的最左前缀匹配原则。
而在众多树之中,MySQL最后选择了改进后的B树—>B+树
那,MySQL 索引为什么使用 B+ 树呢?而不用 B 树、红黑树?
你事先需要知道的几种树:
二叉搜索树
(1)当数据量大的时候,树的高度会比较高,数据量大的时候,查询会比较慢;
(2)当数据线性增大时,二叉搜索树会呈现单边倒的情况,时间复杂度退化 O(n),效率更低;
(3)每个节点只存储一个记录,可能导致一次查询有很多次磁盘IO;
AVL 树(平衡二叉搜索树)
平衡树虽然可以弥补 "当数据线性增大时,二叉搜索树会呈现单边倒的情况,时间复杂度退化 O(n) 的情况”,但是他还是具有二叉搜索树一样的缺点。
B树
B树,如下图,它的特点是:
(1)不再是二叉搜索,而是 m 叉搜索(可以根据不同度数分裂),而正由于是 m 分叉的,高度能够大大降低;
(2)并且 B树 叶子节点、非叶子节点,都可以存储数据;如果将每个节点大小设置为页大小,那么利用磁盘预读
的特性,可以极大减少磁盘IO;并且,通过中序遍历,可以很快找到节点对应的数据。
思考:那到了 B 树结构,直接用来做索引是否就完美了呢?
虽然 B 树相对其他种树优势很明显,但是在范围查询时,还是很吃力;想要减少磁盘IO,节点最好设置为页大小,节点(页)大小固定后,若每个节点都存数据,每个节点也存不下多少,数据大时树的高度也不低。
于是,B+ 树应运而生,在B树上做小小改动,便是目前完美的索引结构啦。
B+树
B+树,如下图,仍是 m 叉搜索树。在 B树 的基础上,进行了一些改进:
(1)非叶子结点不存data,只存key,查询更稳定,增大了广度;数据只存在叶子节点;
(2)叶子之间,增加了链表。可以很好的支持范围查询,并且获取所有节点,不再需要中序遍历;
相比 B树 具有更优的特性:
(1)范围查找,定位min与max之后,中间叶子节点,就是结果集啦;
(2)叶子节点存储实际记录行,记录行相对比较紧密的存储,适合大数据量磁盘存储;非叶子节点存储记录的PK(KEY数据小,相同内存情况下,节点可以多存KEY,增大了节点广度(B+树出度更大,进而树高更矮,磁盘IO次数更少))用于查询加速,适合内存存储;
总结:
-
索引为排好序的一种数据结构,用于提升数据库的查找速度。
-
Hash索引时间复杂度为O(1),树索引是O(log(n))。Hash 底层是哈希表实现,等值查询,可以快速定位数据。但不支持范围查询,无法用于排序分组,无法模糊查询等操作。
-
B+树作为索引优势:
-
叶子节点存储实际记录行,记录行相对比较紧密的存储,适合大数据量磁盘存储;
-
非叶子节点存储记录的PK(KEY数据小,相同内存情况下,节点可以多存KEY,增大了节点广度(B+树出度更大,进而树高更矮,磁盘IO次数更少))用于查询加速,适合内存存储;
-
叶子之间,增加了链表。可以很好的支持范围查询,并且获取所有节点,不再需要中序遍历;
-
更少查询次数:B+树出度更大,树高更低,查询次数更少;
-
很适合磁盘存储,能够充分利用局部性原理,磁盘预读(为了减少IO操作,往往不严格按需读取,而是预读。B+树叶子结点存储相临,读取会快一些)
-