MySQL中的索引为什么使用B+树实现

一、前言

??这几天在研究MySQL相关的内容,而MySQL中比较重要的一个内容就是索引。对MySQL索引有了解的应该都知道,B+树是MySQL索引实现的一个主要的数据结构。今天这篇博客就来简单介绍一下B树、B+树以及MySQL索引使用这种数据结构实现的原因。

二、正文

2.1 B树

??关于B树的操作细节我这里就不详细介绍了,这里主要介绍一下B树的结构,让大家对B树有一个大致的了解。

??这里首先要纠正一个问题,网上大量文章将B树称为B-(减)树,但其实这是一种错误的叫法。会这么叫是因为B树的英文名称为“B-Tree”,错误的翻译导致这种错误的叫法逐渐传开。但实际上这里的“-”是,而不是,由于存在B+树,所以大家都觉得存在一个B-(减)树是很正常的,但实际上,B+树的英文完整写法应该是B+-Tree,“+”是加,但“-”是杠。除此之外,甚至有人认为B树就是二叉搜索树(Binary Search Tree,简称BST),这就错的更加离谱了。

??B树是一棵多路平衡查找树,相信很多人都了解过二叉搜索树,而B树和二叉搜索树类似,只是B树的每一个节点可以有超过两个子节点。而B树中,每一个节点具体可以有几个子节点,这与这棵B树的有关,而树的阶一般用字母m表示。抛开B树的维护操作不谈,B树可以简单理解为一棵m叉搜索树

??我们定义,一个树中,每个节点允许的子节点的最大数量,就称为这个数的阶,一般用字母m表示。例如:假设一棵5阶的B数,则B数上的每一个节点,最多只能有5个子节点。除此之外需要注意,B树的阶m > 2。

??下面我们直接通过一张图来了解B树的结构:

??上面这张图就是一棵标准的B树,他的每一个节点中可能存有多个值(每个节点记录了值的个数,也就是上图中的红色数字),以及多个指向子节点的指针,而值的个数 = 指针的个数 - 1 = m - 1。比如说上图中,根节点存有一个值50(图中称为关键字),而根节点的左子节点存有两个值,即1030。而由于根节点只有一个值,所以他有两个指向子节点的指针,从上图可以看出,这两个指针分别位于值的两边。我们之前说过,B树可以近似的认为是一棵m叉搜索树,所以上图中,根节点的左子树中的所有值都小于根节点的值50,而右边子树中所有节点的值大于根节点的值50

??根节点只有一个值,以及两个子节点,和二叉树类似,所以以它举例不够典型,我们现在以根节点的左子节点再次举例。根节点的左子节点中存有两个值,即10和30,且他有3个指向子节点的指针。在每一个节点中,多个值是排好序的,比如上图中是从小到大排序,于是10在30的左边。对于10左边这个指针指向的子树,包含的值都小于10;而位于10和30之间的指针指向的子树,包含的值一定时10到30之间;而30右边的指针,指向的子树的子节点一定是大于30。

??我们现在以一个例子来说明B树查找的过程,假设上图中,我们想要搜索值35,于是需要经历以下步骤:

  1. 35与根节点中的值比较,根节点中只有一个5035<50,于是向根节点的左子节点搜索;
  2. 根节点的左子节点中的第一个值是1035>10,于是判断下一个值,下一个值为3035>30,继续判断下一个值,但是此节点中没有下一个值了,于是向35右边的这个指针指向的子节点查找,也就是第三个叶子节点;
  3. 到了叶子节点后,发现其中只有一个值,就是35,于是查找成功;

??以上就是在B树中查找一个值的过程。

2.2 为什么需要B树?

??在B树的每一个节点中,都不止存储一个值,具体存储的值的个数依赖于B树的阶。而我们在查找一个值的过程中,需要对当前所在的节点包含的所有值进行一个遍历,以此来确定当前查找的值是否在当前节点中。这也就意味着,相比于二叉搜索树,平衡二叉树,红黑树等数据结构,B树查找一个值需要比较更多的次数。假设一棵B树的阶是100,那也就意味着在最坏情况下,我们在每一个访问到的节点中,都需要比较100次,而前面提到的三种数据结构比较的次数不会超过树的深度,也就是只需要少量的比较次数。既然B树相比较于它们需要比较更多的次数才能找到相应的值,那为什么还要B树呢?这取决于实际的应用场景。

??说到B树,大部分首先想到的就是数据库的索引,MySQL中使用的索引主要为BTree索引(实际是B+树实现的,这个后面再谈)。从上面的B树结构我们可以看到,B树中使用大量的指针维护节点之间的关系,这也就意味着B树在物理存储上并不是连续的。单个节点中的数据是连续存储,但是多个节点之间一般都是单独存储,然后通过指针相互引用。在实际的存储中,BTree索引一般被存放在磁盘中,然后只有需要使用时,才将使用到的部分节点加载进内存,进行比较判断。

??为什么不一次性将BTree索引全部加载进内存?因为在实际生产中,索引往往需要维护百万甚至千万行数据,这就导致索引本身占据大量的内存,再加上我们使用的常常不止一个索引,再加上内存中需要运行其他程序,所以将索引一次性加载进内存是不现实的事情。只有当前需要使用的部分才被加载进内存,而不使用的部分则留在磁盘或者从内存中移除。

??而上面这种使用到才进行加载的方式有一个什么问题?我们每次需要查找树中的一个节点,都需要进行一次磁盘IO,将这个节点从磁盘中加载进内存。而对于B树或者说之前提到的平衡二叉树等数据结构,最多需要访问的节点的个数,实际上就是树的深度(想想搜索一个值的过程就能明白)。对于B树来说,他每一个节点可以存储多个值,而平衡二叉树等二叉结构,每一个节点只存储一个值,这也就意味着在值的个数相等的情况下,B树的深度小于二叉树的深度。这也就意味着以B树作为索引,可以进行更少次数的磁盘IO

??对于一棵含有n个元素的树,二叉搜索树的深度在n-log2(n)之间,而平衡二叉树的深度是log2(n),红黑树与平衡二叉树类似,平均深度也是log2(n)。然而,B树设计了一种高效简单的维护操作,使B树的深度维持在约log(ceil(m/2))(n)~logm(n)之间,大大降低树高(ceil是向上取整函数,例如5/2 = 3)。

??这里面临一个什么问题?磁盘的速度,相对于内存来说非常的缓慢,磁盘查找的速度比内存查找慢100000倍左右。也就是说,从磁盘中找到1个数据所花费的时间,可以从内存中查找100000个数据。这也就意味着我们在使用索引查找数据的过程中,时间主要是花费在了磁盘IO上,而不是数据的比较上。所以,我们希望尽可能少地进行磁盘IO。而B树作为索引,由于树的深度较小,相比于那些二叉树,可以进行更少的磁盘IO,这就是B树最大的优势。

??二叉树中,一个节点一般只存储一个元素,而在将磁盘的数据加载进内存中时,实际上是按页进行加载的,页是每次从磁盘加载进内存的数据的最小单位,一般为4K。这也就意味着我们使用这些二叉树的数据结构时,加载一个节点所在的页进入内存时,这个页中有大量内存都是浪费的。而B树中每一个节点可以存储多个数据,于是我们可以通过修改B树的阶,让他的每一个节点大致占用一个页(4K)的大小,以此最大限度的减少B树的深度,提高内存利用率。

2.3 B树存在的问题

?(1)难以存储具体数据

??我们上面在介绍B树的过程中,对于B树中存储的元素,都是用“值”这个字来说明,但是在上面那张图中可以看到,图中写的是关键字,这是因为我们在实际使用中,需要存储的是key-value型数据。比如说作为数据库索引,我们需要通过索引值进行查找,索引值就是key,但是我们真正需要的是索引值对应的数据行,也就是value

??有一个比较简单的解决方案,我们可以在B树的节点中直接存储keyvalue,这样在通过key找到元素是,可以直接取出value。但是,这会导致另外一个问题。我们上面说过,B树的一个节点,其大小一般被限制在一个磁盘页的大小(4K),如果我们在一个节点中既存key,又存value,就会导致一个节点中能够存储的元素数量减少,value越大,能够存储的元素就越少,于是树的深度就会增加,违背了我们使用B树减少磁盘IO的目的,所以这种方法并不可取。当然,其实也可以让value保存数据的地址,但是可能是需要综合考虑下面这个问题,所有没有这样做。

?(2)B树不适合用来处理范围查询

??在数据库中,进行范围查找的频率非常的高,比如查找员工工资在1000-2000中的全部员工这种类型的查询。但是,B树并不适合用来进行这种范围查找,因为在B树中,每一个节点都用来存储数据,它们之间并不是线性结构,不方便进行范围查询。想要在B树中进行范围查询,可以先找到范围的上界和下界,在通过DFS(或者BFS),遍历包含下界到上界中的所有节点,但这并不方便。

??B+树正是针对以上两个问题,而对B树做了一些修改而得来。

2.4 B+树

??B+树相对于B树主要做了如下的修改:

  • B+树中的每一个非叶子节点并不存储值value,只存储键key,而具体的value全部存放在子节点中,这也就意味着每次查找需要访问的节点数量都是固定的,都需要向下查找到子节点;
  • 每一个子节点都有一个指向下一个子节点的指针,所有的子节点相互串联,组成一个线性的结构;
  • 对于一个m阶的B+树,每一个节点最多只有m个子节点,同时存储mkey(对于m阶的B树,只有m-1个key);
  • 每一个子节点中最小(或最大)的key,也包含在父节点中(这个通过下面的例子理解);

??下面我们通过一张图来看看B+树的结构:

??上图中,就是一棵B+树。根节点存储了3keykey值分别是5,28,65,且是按照从小到达的顺序存储。同时根节点包含3个指针,指向自己的3个子节点。第一个子节点中最小的key5,就是根节点中最小的key,而这个节点中所有的key,大小都是在根节点的第一个key(5)到第二个key(28)之间(不包含28);第二个子节点中最小的key28,也就是根节点中的第二个key,在这个子节点中所有的key,大小都在根节点第二个key(28)到第三个key(65)之间;第三个子节点同理,它包含根节点的第三个key(65),同时其中所有的key>=65。再往下的子节点也是同理。

??而根据上图我们可以看到,在最下层的叶子节点中,存储了全部的key值(尽管有些key已经在上层节点出现过),同时不仅存储了key值,还存储了这些key值对应的value。除此之外,每一个叶子节点都包含一个指针,指向下一个叶子节点。这些叶子节点相互串联,组成了一个key值从小到大排好序的线性结构。

??这样处理有什么好处呢?好处就是我们在树中存储了value,但是由于value存储在叶子节点中,所以对于作为索引的非叶子节点来说,并没有增加它们的大小,从而并没有导致树的高度增加。除此之外,由于value都存储在叶子节点中,并且叶子节点相互串联,所以非常方便进行范围查询。比如说上图,我们要查找key26-60对应的数据,那我们首先查找26所在的叶子节点,发现它在第三个叶子节点,于是我们将第三个叶子节点读入内存,然后发现并不包含全部数据,于是通过指针找到第四个叶子节点,将第四个叶子节点读入内存,还不包含全部,于是将第五个叶子节点也读入,这时就包含全部数据了。

2.5 InnoDB和MyISAM中索引的实现

??在MySQL5.1之前,MySQL的默认存储引擎是MyISAM,而在这之后改为了InnoDB。这两个存储引擎中,都使用了B+Tree实现索引,但是实现的方式有所区别。

?(1)InnoDB中的聚簇索引

??什么是聚簇索引,就是指索引与数据库表中的数据存储在一起。InnoDB使用B+树实现聚簇索引,而数据库表中的数据行,实际上就是存储在B+树的叶子节点中,我们上面说的B+树中的key-value,其中的value指的就是具体的一行数据,我们在叶子节点中找到了key,实际上也就得到了key对应的那一行数据。所以严格来讲,聚簇索引不单单是索引,更是数据的一种存储结构。

??InnoDB使用表的主键作为key,建立聚簇索引,如果表没有主键,将选择一个唯一的非空索引替代,若也没有,将隐式的定义一个主键用来建立。

?(2)MyISAM的非聚簇索引

??在MyISAM中,并不使用聚簇索引,也就是说MyISAM中通过B+树实现的索引,并不包含表中具体的数据行,子节点中的value,存储的是这一行数据的地址,也就是说数据和索引分开存储。

三、总结

??在MySQL的实际应用中,BTree索引都是通过B+树建立的,而不是B树。而在InnoDB中,使用的是聚簇索引,在B+树的叶子节点中直接存储表中的数据行;而MyISAM没有使用聚簇索引,B+树的叶子节点中,存放的是数据行的地址。

四、参考

原文地址:https://www.cnblogs.com/tuyang1129/p/12635692.html

时间: 2024-11-07 13:35:56

MySQL中的索引为什么使用B+树实现的相关文章

(转)MySQL中的索引详讲

序言 之前写到MySQL对表的增删改查(查询最为重要)后,就感觉MySQL就差不多学完了,没有想继续学下去的心态了,原因可能是由于别人的影响,觉得对于MySQL来说,知道了一些复杂的查询,就够了,但是我认为,不管有没有用,现在学着不懂的东西,说明就是自己薄弱的地方,多学才能比别人更强 --WH 一.什么是索引?为什么要建立索引? 索引用于快速找出在某个列中有一特定值的行,不使用索引,MySQL必须从第一条记录开始读完整个表,直到找出相关的行,表越大,查询数据所花费的时间就越多,如果表中查询的列有

MySQL中的索引详讲

一.什么是索引?为什么要建立索引? 索引用于快速找出在某个列中有一特定值的行,不使用索引,MySQL必须从第一条记录开始读完整个表,直到找出相关的行,表越大,查询数据所花费的时间就越多,如果表中查询的列有一个索引,MySQL能够快速到达一个位置去搜索数据文件,而不必查看所有数据,那么将会节省很大一部分时间. 例如:有一张person表,其中有2W条记录,记录着2W个人的信息.有一个Phone的字段记录每个人的电话号码,现在想要查询出电话号码为xxxx的人的信息. 如果没有索引,那么将从表中第一条

一步一步带你入门MySQL中的索引和锁 (转)

出处: 一步一步带你入门MySQL中的索引和锁 索引 索引常见的几种类型 索引常见的类型有哈希索引,有序数组索引,二叉树索引,跳表等等.本文主要探讨 MySQL 的默认存储引擎 InnoDB 的索引结构. InnoDB的索引结构 在InnoDB中是通过一种多路搜索树——B+树实现索引结构的.在B+树中是只有叶子结点会存储数据,而且所有叶子结点会形成一个链表.而在InnoDB中维护的是一个双向链表. 你可能会有一个疑问,为什么使用 B+树 而不使用二叉树或者B树? 首先,我们知道访问磁盘需要访问到

mysql中查看索引是否被使用到

http://blog.sina.com.cn/s/blog_5037eacb0102vm5c.html 官方MySQL中查看索引是否被使用到: 在percona版本或marida中可以通过 information_schea.index_statistics查看得到, 在官方版本中如何查看呢? select object_type,object_schema,object_name,index_name,count_star,count_read,COUNT_FETCH from perfor

Mysql中的索引

众所周知,索引能够加快查询的速度,类似看书的时候先查目录之后再翻到具体那一页. 一.聚集索引和非聚集索引 1.聚集索引 聚集索引一张表只能存在一个. 聚集索引是物理上连续的(如果数据结构是btree的话,则数据直接存在叶子节点上),所以查询一个范围的数据会相当快. 2. 非聚集索引 非聚集索引一张表可以存在抖个. 非聚集索引在逻辑上是连续的(如果数据结构是btree的话,则叶子节点上存的的数据的位置信息),所以 mysql中的innodb引擎支持聚集索引,在mysql中主键索引就是聚集索引. M

MySQL中是索引

MySQL中是索引: --.唯一索引: 一行中的内容不能一样, create t2( id int , num int, unique weiyisuiyin (id,num) ) --唯一; --约束不能重复(可以为空) --主键不能重复 --加速查找 一个表中可以有多列索引,组合起来具有唯一性; 原文地址:https://www.cnblogs.com/tataerzu/p/10165603.html

mysql中的索引原理与表设计

索引是有效使用数据库的基础,但你的数据量很小的时候,或许通过扫描整表来存取数据的性能还能接受,但当数据量极大时,当访问量极大时,就一定需要通过索引的辅助才能有效地存取数据.一般索引建立的好坏是性能好坏的成功关键. 1.InnoDb数据与索引存储细节 使用InnoDb作为数据引擎的Mysql和有聚集索引的SqlServer的数据存储结构有点类似,虽然在物理层面,他们都存储在Page上,但在逻辑上面,我们可以把数据分为三块:数据区域,索引区域,主键区域,他们通过主键的值作为关联,配合工作.默认配置下

一步一步带你入门MySQL中的索引和锁

索引 索引常见的几种类型 索引常见的类型有哈希索引,有序数组索引,二叉树索引,跳表等等.本文主要探讨 MySQL 的默认存储引擎 InnoDB 的索引结构. InnoDB的索引结构 在InnoDB中是通过一种多路搜索树——B+树实现索引结构的.在B+树中是只有叶子结点会存储数据,而且所有叶子结点会形成一个链表.而在InnoDB中维护的是一个双向链表. 你可能会有一个疑问,为什么使用 B+树 而不使用二叉树或者B树? 首先,我们知道访问磁盘需要访问到指定块中,而访问指定块是需要 盘片旋转 和 磁臂

mysql中的索引、触发器、和事务

一.索引 1.什么是索引 如果把表看做一本书,索引就好像书里的目录或者书签,能帮助你快速找到你要检索的内容,所以叫做索引. 索引用来快速地寻找那些具有特定值的记录,所有MySQL索引都以B-树的形式保存.如 果没有索引,执行查询时MySQL必须从第一个记录开始扫描整个表的所有记录,直至找到符合 要求的记录.表里面的记录数量越多,这个操作的代价就越高.如果作为搜索条件的列上已 经创建了索引,MySQL无需扫描任何记录即可迅速得到目标记录所在的位置.如果表有1000个 记录,通过索引查找记录至少要比