追根溯源:MySQL 索引以及底层实现b+ tree的好处

什么是索引?

       首先索引引入的目的是为了快速查询以及更新表中的数据。索引是一种存储在硬盘上的,对数据库表中一列或多个列进行排序的数据结构。

      索引是一个单独存储在磁盘上的数据库结构,它们包含着对数据表里所有记录的引用指针,使用索引可以提高数据库特定数据的查询速度.索引时在存储引擎中实现的,因此每种存储引擎的索引不一定完全相同,并且每种存储引擎也不一定支持所有索引类型.

    索引的优点:

     1.通过创建唯一索引,可以确保数据库数据的唯一性

     2.可以加快数据库查询的速度

     3.通过使用索引,可以在查询中使用优化隐藏器,提高系统的性能

     4.加速表和表之间的连接;

    索引的缺点:

    1.索引是存储在磁盘上的数据结构,它们包含着对数据表里所有记录的引用指针。所以创建索引需要耗费空间内存。

    2.创建索引和维护索引需要耗费时间。并且随着数据量的增长耗费越来越大。

    3.对表中数据删改的时候,索引也需要动态的进行维护,这样降低了数据的维护速度。

   说了这么多,现在常用的MySQL数据库 ,索引的底层实现又是怎样的呢?

    MySQL InnoDB引擎的索引底层实现采用的是B+ tree。什么是B+ tree?为什么是B+ tree?B+ tree相较于二叉搜索树 AVL 红黑树又有什么样的优势?

      首先,要说B+ tree,得先说B- tree。B- tree读做 b  tree。不是 b减tree(音译)。从数据查找搜索来看。二叉搜索树,采取二分查找的思想,O(log N)的复杂度就可以完成对数据的查找任务。那么为什么不用二叉搜索树?

       一句话:因为磁盘IO问题。之前说过,索引是存储在磁盘上的,对数据库表中一列或多列进行排序的数据结构。数据库中的数据可能很大,在大量的数据存储在磁盘上时。计算机无法一次性将数据全部加载进内存。而是通过逐一加载每一磁盘页。而加载磁盘页对应着索引树的节点。那么一次查找所经历的索引树的深度对应着磁盘IO交互的次数。如果采取二叉树结构,那么显而易见,可能导致遍历的深度太大导致磁盘IO交互的次数太多。而相较于内存查询,磁盘IO才是影响查询以及更新表中数据的关键。那么由二叉树这样的瘦长结构自然容易联想到如何将它变的矮胖。这样做虽然没有降低比较次数,由于深度的降低,可以极大的减少磁盘IO次数。从而可以提升查询以及更新数据的性能。

       那么B tree,是怎样的一种数据结构呢?

       首先B tree是一种m叉的多路平衡树。B树具有这样的特点:

       1.每个节点最多含有m个孩子

       2.根节点含有[2,m]个孩子

       3.非叶子节点含有[[m/2],m]个孩子节点(向上取整的意思)

       4.一个节点如果含有K个关键字,那么它就有k+1个孩子

       5.所有叶子节点都在同一层  

       6.每个节点的K个关键数把节点拆成了K+1段

      图示举个栗子,顺便说下查找元素我的操作:

      首先把根节点加载进磁盘页,这是第一次磁盘IO,然后在内存中进行第一次比较,发现没有5这个元素,那么到根节点的儿子节点进行比较。根据B树m叉多路平衡树的性质,我们找到根节点的第一个孩子节点(2 6)节点。把这个具有两个关键字的节点加载进磁盘页。在内存中对(2 6)进行元素5的比较查找。

 经过两次比较后确定到(2 6)节点的第二个儿子节点中进行5元素的查找。开始第三次磁盘IO,将(3 5)节点装载进磁盘页。

在内存中对5进行查找。找到了!

那么可以发现一共是3次IO,而比较次数相较于二叉搜索树的中序遍历,其实一点也没有少。不过降低了磁盘IO的次数。对于提高索引查询以及更新数据来说。这就足够了。毕竟磁盘IO才是影响性能的关键。

        说了这么多,MySQL InnoDB索引的底层实现是什么?是B+ tree。并不是B-tree。那么为什么不选择B-Tree,而选择B+tree呢。

      我们还是先来看看B+ tree吧。

      B+ tree 是n路查找平衡树。它的特点是:

     1.有n个关键字的非叶子节点具有n个孩子(B树是n+1),这些关键字不保存数据只是用来索引。最终的数据都在叶子节点上(B树是每个关键字都保存数据)。

     2.所有的叶子结点中包含了全部关键字的信息,及指向含这些关键字记录的指针,且叶子结点本身依关键字的大小自小而大顺序链接。

    3.所有的非叶子节点可以看作是索引部分,节点关键字仅含其子树中最大最小的部分。

   4.同一数字可能在不同节点重复出现,非叶子节点是索引,叶子节点保存数据。

   上个图吧,直观一点。

   

b+树相比于b树的查询优势

  1. b+树的中间节点不保存数据,所以磁盘页能容纳更多节点元素,更“矮胖”;
  2. b+树查询必须查找到叶子节点,b树只要匹配到即可不用管元素位置,因此b+树查找更稳定(并不慢);

对于范围查找来说,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/majiawenzzz/article/details/81098870