[转载]为什么mysql用的是B+树?

什么是索引
在百度上查找索引的时候,很多回答会是:数据库就像是一本书,索引就像是他的目录,我们通过索引可以很快的定位到我们要找的数据。

这话说的通俗易懂,一点没错。但是如果要说的更确切一点,那么应该这么表达:

索引是为了加速对表中数据行检索而创建的一种分散的数据结构。

首先索引的存在是为了加速对表中数据行的检索,其次他是分散的,最后他是一种数据结构。

所以我们说正确的创建索引是提升数据库性能的基础。

既然这样说,那么,mysql索引的数据结构到底是什么样的呢?

一般情况下,索引里存储的是数据的磁盘地址,这样查找数据库的时候,并不需要全表搜索,而只需要在索引里先找到数据对应的地址,直接到数据库里取就好了。这样说起来,索引确实起到了“目录”的作用。那么索引本身是什么结构呢?又是怎么提高搜索吸能的呢?

大家都略有耳闻是B+ tree。但是为什么是b+ tree,让我来给大家捋一捋。

为什么不是二叉查找树?


这个样的一棵树就是二叉查找树,但是首先这棵树是不平衡的,如果我们要查找22这个数就几乎要遍历所有的数据,这个结构就看起来很不对头。肯定不能作为索引的数据结构。

那么如果说不平衡的二叉树不可以,那么平衡二叉树可以吗?

为什么平衡二叉树不行?


平衡二叉树是平衡的二叉树(每个节点左右两边的深度差不能大于一),这样的树的深度已经很平均了,但是还是不行。

原因是:

1. 它太深了。

因为一个节点才分两个岔,那一百万条数据的索引得多深啊,不敢想不敢想,性能一定很差。

2. 它太小了。

其实太小也是太深的原因之一,但是它最重要的是,因为查找一个节点对于磁盘来说就是一次IO。磁盘的速率是很慢的,有cpu一天,磁盘一年的说法。所以IO次数尤其重要,磁盘IO一次能读取4k的数据,但是我们的节点里居然只有一条数据吗???这简直是浪费生命,所以AVL Tree也是不行的。没有很好的利用操作磁盘IO的数据交换特性,也没有很好的利用磁盘的预读能力(空间局部性原理),从而带来频繁的IO操作。

那么,多路平衡查找树(B-Tree)?
既然由上面可以得到平衡二叉树不行,是因为每个节点数据量太少,而分叉也太少导致太深。那么我们可以来看看多路平衡查找树了,也就是简称B-Tree.

其实说起来,二叉树是一种特殊的多路平衡查找树。

图上为每个节点存放2条数据,而路数数量一般为关键字数量+1,即区间。

图为三路,即二三树。但是真正的多路平衡查找树可以有很多路,IO一次是4k,那么我们一个节点可以放4k的数据,这样正好一次读取。

其实到这里大家就可以看出多路查找树已经比之前的两种树好太多了,但是实际上mysql依然没有采用多路平衡查找树。

那么 b+ tree到底有什么特别的优势吗?

为什么是B+ Tree?


B+ Tree和B- Tree相比有以下特点:

1. B+节点关键字才有左闭合区间

2. B+非叶节点不保存数据的相关信息,只保存关键字和子节点引用

3. B+关键字对应的数据保存在叶子节点中

4. B+叶子节点是顺序排列的,并且相邻节点居然有顺序引用的关系(双向链表)

其实这里123条都是有关系的。

首先放弃把关键字的数据保存在非叶子节点中,肯定能让非叶子节点存储更多的关键字,这样就能更好的降低深度(因为能保存的 数据多分的路多,也就意味着相同数据需要的层数减少)。其次关键字的数据不保存在当前节点中,但他总需要一个地方存放,于是就放在叶子节点中,这样叶子节点中的数据就需要包含关键字,所以它是左闭合区间。

特点4就已经体现出了b+树的另一优势,方便排序。也正是因为关键字也在叶子节点上,就让这种排序变的更加轻松,每个叶子节点其实是连起来的双向链表的关系,这样无论你是分组还是排序,都不需要在返回非叶子节点,可以一轮顺着查找下去。所以他在排序和范围查找中非常牛逼。

所以选B+树的原因就是如下(其实上面已经基本提到了):

1. B+树是多路平衡查找树(B-Tree)的变种(Plus版本),多路绝对平衡查找树,他拥有B-树的优势

2. B+树的扫库扫表能力更强。

3. B+树的磁盘读写能力更强。

4. B+树的排序能力更强。

5. B+树的查询效率更加稳定(仁者见仁,智者见智)

其中第一点非常容易理解,因为B+也是多路的,也能减少层数,甚至可以比B减少的更多,所以B+其实拥有了B的优势。

第二点因为B+的非叶子节点全为索引,数据全存放在叶子节点中,可以用遍历叶子节点的方式直接取出数据。B树就总要返回非叶子节点,会增加IO。

第三点是因为B+的数据结构非叶节点不用保存关键字的数据,这样他只需要保存关键字和叶子节点的指针,这样每个单位更小,也就是说,保存更多关键字,分更多路。这样一次IO加载回来的有效数据会比B树更多,效率更高,IO读写次数就会更少。

第四点就不用解释了,叶子节点之间的顺序关系帮助我们排序,天然具有排序功能。双向的引用让我们无论是正序还是倒序都能更快。

第五点其实是仁者见仁智者见智,是因为B树的查找效率实际上是分情况的:如果你在前几位节点(比如根节点)就直接命中,那么你的查询速度会极快,但是如果你在叶子节点才命中,那你的速度就会相对较慢。这样就造成了你每次查询的性能并不稳定,当然如果你精确的命中了那么B树的速度一定会比B+高,因为B树的数据是保存在关键字所在节点中的,只要命中就能直接返回,而B树无论你是在第一个节点命中,还是在最后的叶子节点命中,你都需要到叶子节点中获取数据,这样就让你每次查询的性能变的比较稳定。一般来说,我们在开发中要摒弃不稳定因素,因为如果查找效率非常随机,你实际上会很迷惑我查询慢是因为命中了叶子节点,还是sql语句出现了问题,还是到了性能瓶颈,而查询快的时候也不能 保证性能就很优秀,有可能是直接命中了根节点。我们认为这样无法确定问题的系统是不健壮的,所以B+相比更加稳定。

到这里,大概就能确定为什么Mysql选用了B+Tree的数据结构作为索引的数据结构。因为他具有B树的所有优势,但是能更好的扫库扫表,在磁盘IO上也有更好的表现,天然具有排序功能,并且表现的更加稳定。

发布了99 篇原创文章 · 获赞 4 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/m0_37313888/article/details/105397586