浅谈B树,B+树,B*树及分析MySQL的索引

版权声明:本文为博主原创文章,转载请留下脚印,技术交流评论。 https://blog.csdn.net/ZLZ2017/article/details/86016105

树的基本概念

:树的顶端结点
兄弟:具有同一个双亲(Parent)的孩子(Child)之间互称为兄弟(Sibling)。
祖先:结点的祖先(Ancestor)是从根(Root)到该结点所经分支(Branch)上的所有结点。
叶子(终端结点):没有孩子的结点(也就是度为0的结点)称为叶子(Leaf)或终端结点。
:结点所拥有的子树个数称为结点的度(Degree)。
:一个结点和另一个结点之间的连接被称之为边(Edge)。
层次:结点的层次(Level)从根(Root)开始定义起,根为第0层,根的孩子为第1层。
路径:连接结点和其后代的结点之间的(结点,边)的序列。
结点的高度:结点的高度是该结点和某个叶子之间存在的最长路径上的边的个数。
树的高度:树的高度是其根结点的高度。
结点的深度:结点的深度是从树的根结点到该结点的边的个数。(注:树的深度指的是树中结点的最大层次。)
森林:森林是n(>=0)棵互不相交的树的集合。

二叉树(Binary Search Tree)

定义:每个结点至多拥有两棵子树(即二叉树中不存在度大于2的结点),并且,二叉树的子树有左右之分,其次序不能任意颠倒。
二叉树的性质

  1. 若二叉树的层次从0开始,则在二叉树的第i层至多有2^i个结点(i>=0)。
  2. 高度为k的二叉树最多有2^(k+1) - 1个结点(k>=-1)。 (空树的高度为-1)高度为k的二叉树最多有2^(k+1) - 1个结点(k>=-1)。 (空树的高度为-1)
  3. 对任何一棵二叉树,如果其叶子结点(度为0)数为m, 度为2的结点数为n, 则m = n + 1。对任何一棵二叉树,如果其叶子结点(度为0)数为m, 度为2的结点数为n, 则m = n + 1。

B树

B树的结构要求:
1)根节点至少有两个子节点
2)每个节点有M-1个key,并且以升序排列
3)位于M-1和M key的子节点的值位于M-1 和M key对应的Value之间
4)其它节点至少有M/2个子节点
5)所有叶子节点都在同一层
在这里插入图片描述

查询
  以上图为例:若查询的数值为5:
  第一次磁盘IO:在内存中定位(与17、35比较),比17小,左子树;
  第二次磁盘IO:在内存中定位(与8、12比较),比8小,左子树;
  第三次磁盘IO:在内存中定位(与3、5比较),找到5,终止。
整个过程中:比较的次数并不比二叉查找树少,尤其适当某一节点中的数据很多时,但是磁盘IO的次数却是大大减少。比较是在内存中进行的,相比于磁盘IO的速度,比较的耗时几乎可以忽略。所以当树的高度足够低的话,就可以极大的提高效率。相比之下,节点中的元素多点也没关系,仅仅是多了几次内存交互而已,只要不超过磁盘页的大小即可。

B+树

B+树是B树的升级版本,有着比B树更高的查询效率,也是一种多路搜索树:

  1. 其定义基本与B-树同,除了:
  2. 非叶子结点的子树指针与关键字个数相同;
  3. 非叶子结点的子树指针P[i],指向关键字值属于[K[i], K[i+1])的子树(B-树是开区间);
  4. 为所有叶子结点增加一个链指针;
  5. 所有关键字都在叶子结点出现;
    在这里插入图片描述

查找
首先,B+树的查找和B树一样,类似于二叉查找树。起始于根节点,自顶向下遍历树,选择其分离值在要查找值的任意一边的子指针。在节点内部典型的使用是二分查找来确定这个位置。
不同的是:

  1. B+树中间节点没有卫星数据(索引元素所指向的数据记录),只有索引,而B树每个结点中的每个关键字都有卫星数据;这就意味着同样的大小的磁盘页可以容纳更多节点元素,在相同的数据量下,B+树更加“矮胖”,IO操作更少。
    B树的卫星数据:
    在这里插入图片描述
    B+树的卫星数据:
    在这里插入图片描述
    需要补充的是,在数据库的聚集索引(Clustered Index)中,叶子节点直接包含卫星数据。在非聚集索引(NonClustered Index)中,叶子节点带有指向卫星数据的指针。
  2. 因为卫星数据的不同,导致查询过程也不同;B树的查找只需找到匹配元素即可,最好情况下查找到根节点,最坏情况下查找到叶子结点,所说性能很不稳定,而B+树每次必须查找到叶子结点,性能稳定
  3. 在范围查询方面,B+树的优势更加明显:B树的范围查找需要不断依赖中序遍历。首先二分查找到范围下限,在不断通过中序遍历,知道查找到范围的上限即可。整个过程比较耗时。
      而B+树的范围查找则简单了许多。首先通过二分查找,找到范围下限,然后同过叶子结点的链表顺序遍历,直至找到上限即可,整个过程简单许多,效率也比较高。
      例如:同样查找范围[3-11],两者的查询过程如下:
      B树的查找过程:
      在这里插入图片描述
      B+树的查找过程:
      在这里插入图片描述

B*树

接下来要说明的就是B树,B树是对B+树进行的又一次的升级。在B+树的非根和非叶子结点再增加指向兄弟的指针;
在这里插入图片描述
在B+树基础上,为非叶子结点也增加链表指针,将结点的最低利用率从1/2提高到2/3;
在这比如说当你进行插入节点的时候,它首先是放到兄弟节点里面。如果兄弟节点满了的话,进行分裂的时候从兄弟节点和这个节点各取出1/3,放入新建的节点当中,这样也就实现了空间利用率从1/2到1/3。

MySQL的索引

什么是索引?
首先索引引入的目的是为了快速查询以及更新表中的数据。索引是一种存储在硬盘上的,对数据库表中一列或多个列进行排序的数据结构。
索引是一个单独存储在磁盘上的数据库结构,它们包含着对数据表里所有记录的引用指针,使用索引可以提高数据库特定数据的查询速度.索引时在存储引擎中实现的,因此每种存储引擎的索引不一定完全相同,并且每种存储引擎也不一定支持所有索引类型.
为什么不用二叉搜索树
一句话:因为磁盘IO问题。之前说过,索引是存储在磁盘上的,对数据库表中一列或多列进行排序的数据结构。数据库中的数据可能很大,在大量的数据存储在磁盘上时。计算机无法一次性将数据全部加载进内存。而是通过逐一加载每一磁盘页。而加载磁盘页对应着索引树的节点。那么一次查找所经历的索引树的深度对应着磁盘IO交互的次数。如果采取二叉树结构,那么显而易见,可能导致遍历的深度太大导致磁盘IO交互的次数太多。而相较于内存查询,磁盘IO才是影响查询以及更新表中数据的关键。那么由二叉树这样的瘦长结构自然容易联想到如何将它变的矮胖。这样做虽然没有降低比较次数,由于深度的降低,可以极大的减少磁盘IO次数。从而可以提升查询以及更新数据的性能。
B树相较于二叉搜索树的中序遍历,没有减少多少,甚至一点都没有减少,不过降低了磁盘IO的次数。对于提高索引查询以及更新数据来说。这就足够了,毕竟磁盘IO才是影响性能的关键。
b+树相比于b树的查询优势

  1. b+树的中间节点不保存数据,所以磁盘页能容纳更多节点元素,更“矮胖”;
  2. b+树查询必须查找到叶子节点,b树只要匹配到即可不用管元素位置,因此b+树查找更稳定(并不慢);
  3. 所有叶子节点形成有序链表,便于范围查询,b+树只需遍历叶子节点链表即可,b树却需要重复地中序遍历。
    为什么说B±tree比B 树更适合实际应用中操作系统的文件索引和数据库索引?
    B+tree的磁盘读写代价更低:B+tree的内部结点并没有指向关键字具体信息的指针(红色部分),因此其内部结点相对B 树更小。如果把所有同一内部结点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多。一次性读入内存中的需要查找的关键字也就越多,相对来说IO读写次数也就降低了;
    B+tree的查询效率更加稳定:由于内部结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引,所以,任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当;
    数据库索引采用B+树而不是B树的主要原因:B+树只要遍历叶子节点就可以实现整棵树的遍历,而且在数据库中基于范围的查询是非常频繁的,而B树只能中序遍历所有节点,效率太低。
    文件索引和数据库索引为什么使用B+树?
      文件与数据库都是需要较大的存储,也就是说,它们都不可能全部存储在内存中,故需要存储到磁盘上。而所谓索引,则为了数据的快速定位与查找,那么索引的结构组织要尽量减少查找过程中磁盘I/O的存取次数,因此B+树相比B树更为合适。数据库系统巧妙利用了局部性原理与磁盘预读原理,将一个节点的大小设为等于一个页,这样每个节点只需要一次I/O就可以完全载入,而红黑树这种结构,高度明显要深的多,并且由于逻辑上很近的节点(父子)物理上可能很远,无法利用局部性。最重要的是,B+树还有一个最大的好处:方便扫库。B树必须用中序遍历的方法按序扫库,而B+树直接从叶子结点挨个扫一遍就完了,B+树支持range-query非常方便,而B树不支持,这是数据库选用B+树的最主要原因

猜你喜欢

转载自blog.csdn.net/ZLZ2017/article/details/86016105