热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

数据库技术:【深入学习MySQL】MySQL的索引结构为什么使用B+树?

在MySQL中,无论是Innodb还是MyIsam,都使用了B+树作索引结构(这里不考虑hash等其他索引)。本文将从最普通的二叉查找树开始,逐步说明各种树解决的问题以及面临的新问

前言

在mysql中,无论是innodb还是myisam,都使用了b+树作索引结构(这里不考虑hash等其他索引)。数据库技术:【深入学习MySQL】MySQL的索引结构为什么使用B+树?将从最普通的二叉查找树开始,逐步说明各种树解决的问题以及面临的新问题,从而说明mysql为什么选择b+树作为索引结构。

目录

一、二叉查找树(bst):不平衡

二、平衡二叉树(avl):旋转耗时

四、b树:为磁盘而生

五、b+树

六、感受b+树的威力

一、二叉查找树(bst):不平衡

二叉查找树(bst,binary search tree),也叫二叉排序树,在二叉树的基础上需要满足:任意节点的左子树上所有节点值不大于根节点的值,任意节点的右子树上所有节点值不小于根节点的值。如下是一颗bst()。

【深入学习MySQL】MySQL的索引结构为什么使用B+树?

当需要快速查找时,将数据存储在bst是一种常见的选择,因为此时查询时间取决于树高,平均时间复杂度是o(lgn)。然而,bst可能长歪而变得不平衡,如下图所示(),此时bst退化为链表,时间复杂度退化为o(n)。

为了解决这个问题,引入了平衡二叉树。

【深入学习MySQL】MySQL的索引结构为什么使用B+树?

二、平衡二叉树(avl):旋转耗时

avl树是严格的平衡二叉树,所有节点的左右子树高度差不能超过1;avl树查找、插入和删除在平均和最坏情况下都是o(lgn)。

avl实现平衡的关键在于旋转操作:插入和删除可能破坏二叉树的平衡,此时需要通过一次或多次树旋转来重新平衡这个树。当插入数据时,最多只需要1次旋转(单旋转或双旋转);但是当删除数据时,会导致树失衡,avl需要维护从被删除节点到根节点这条路径上所有节点的平衡,旋转的量级为o(lgn)。

由于旋转的耗时,avl树在删除数据时效率很低;在删除操作较多时,维护平衡所需的代价可能高于其带来的好处,因此avl实际使用并不广泛。

三、红黑树:树太高

与avl树相比,红黑树并不追求严格的平衡,而是大致的平衡:只是确保从根到叶子的最长的可能路径不多于最短的可能路径的两倍长。从实现来看,红黑树最大的特点是每个节点都属于两种颜色(红色或黑色)之一,且节点颜色的划分需要满足特定的规则(具体规则略)。红黑树示例如下():

【深入学习MySQL】MySQL的索引结构为什么使用B+树? 

与avl树相比,红黑树的查询效率会有所下降,这是因为树的平衡性变差,高度更高。但红黑树的删除效率大大提高了,因为红黑树同时引入了颜色,当插入或删除数据时,只需要进行o(1)次数的旋转以及变色就能保证基本的平衡,不需要像avl树进行o(lgn)次数的旋转。总的来说,红黑树的统计性能高于avl。

因此,在实际应用中,avl树的使用相对较少,而红黑树的使用非常广泛。例如,java中的treemap使用红黑树存储排序键值对;java8中的hashmap使用链表+红黑树解决哈希冲突问题(当冲突节点较少时,使用链表,当冲突节点较多时,使用红黑树)。

对于数据在内存中的情况(如上述的treemap和hashmap),红黑树的表现是非常优异的。但是对于数据在磁盘等辅助存储设备中的情况(如mysql等数据库),红黑树并不擅长,因为红黑树长得还是太高了。当数据在磁盘中时,磁盘io会成为最大的性能瓶颈,设计的目标应该是尽量减少io次数;而树的高度越高,增删改查所需要的io次数也越多,会严重影响性能。

四、b树:为磁盘而生

b树也称b-树(其中-不是减号),是为磁盘等辅存设备设计的多路平衡查找树,与二叉树相比,b树的每个非叶节点可以有多个子树。因此,当总节点数量相同时,b树的高度远远小于avl树和红黑树(b树是一颗“矮胖子”),磁盘io次数大大减少。

定义b树最重要的概念是阶数(order),对于一颗m阶b树,需要满足以下条件:

  • 每个节点最多包含 m 个子节点。
  • 如果根节点包含子节点,则至少包含 2 个子节点;除根节点外,每个非叶节点至少包含 m/2 个子节点。
  • 拥有 k 个子节点的非叶节点将包含 k – 1 条记录。
  • 所有叶节点都在同一层中。

可以看出,b树的定义,主要是对非叶结点的子节点数量和记录数量的限制。

下图是一个3阶b树的例子():

 【深入学习MySQL】MySQL的索引结构为什么使用B+树?

b树的优势除了树高小,还有对访问局部性原理的利用。所谓局部性原理,是指当一个数据被使用时,其附近的数据有较大概率在短时间内被使用。b树将键相近的数据存储在同一个节点,当访问其中某个数据时,数据库会将该整个节点读到缓存中;当它临近的数据紧接着被访问时,可以直接在缓存中读取,无需进行磁盘io;换句话说,b树的缓存命中率更高。

b树在数据库中有一些应用,如mongodb的索引使用了b树结构。但是在很多数据库应用中,使用了是b树的变种b+树。

五、b+树

b+树也是多路平衡查找树,其与b树的区别主要在于:

  • b树中每个节点(包括叶节点和非叶节点)都存储真实的数据,b+树中只有叶子节点存储真实的数据,非叶节点只存储键。在mysql中,这里所说的真实数据,可能是行的全部数据(如innodb的聚簇索引),也可能只是行的主键(如innodb的辅助索引),或者是行所在的地址(如myisam的非聚簇索引)。
  • b树中一条记录只会出现一次,不会重复出现,而b+树的键则可能重复重现——一定会在叶节点出现,也可能在非叶节点重复出现。
  • b+树的叶节点之间通过双向链表链接。
  • b树中的非叶节点,记录数比子节点个数少1;而b+树中记录数与子节点个数相同。

由此,b+树与b树相比,有以下优势:

  • 更少的io次数:b+树的非叶节点只包含键,而不包含真实数据,因此每个节点存储的记录个数比b数多很多(即阶m更大),因此b+树的高度更低,访问时所需要的io次数更少。此外,由于每个节点存储的记录数更多,所以对访问局部性原理的利用更好,缓存命中率更高。
  • 更适于范围查询:在b树中进行范围查询时,首先找到要查找的下限,然后对b树进行中序遍历,直到找到查找的上限;而b+树的范围查询,只需要对链表进行遍历即可。
  • 更稳定的查询效率:b树的查询时间复杂度在1到树高之间(分别对应记录在根节点和叶节点),而b+树的查询复杂度则稳定为树高,因为所有数据都在叶节点。

b+树也存在劣势:由于键会重复出现,因此会占用更多的空间。但是与带来的性能优势相比,空间劣势往往可以接受,因此b+树的在数据库中的使用比b树更加广泛。

六、感受b+树的威力

前面说到,b树/b+树与红黑树等二叉树相比,最大的优势在于树高更小。实际上,对于innodb的b+索引来说,树的高度一般在2-4层。下面来进行一些具体的估算。

树的高度是由阶数决定的,阶数越大树越矮;而阶数的大小又取决于每个节点可以存储多少条记录。innodb中每个节点使用一个页(page),页的大小为16kb,其中元数据只占大约128字节左右(包括文件管理头信息、页面头信息等等),大多数空间都用来存储数据。

  • 对于非叶节点,记录只包含索引的键和指向下一层节点的指针。假设每个非叶节点页面存储1000条记录,则每条记录大约占用16字节;当索引是整型或较短的字符串时,这个假设是合理的。延伸一下,我们经常听到建议说索引列长度不应过大,原因就在这里:索引列太长,每个节点包含的记录数太少,会导致树太高,索引的效果会大打折扣,而且索引还会浪费更多的空间。
  • 对于叶节点,记录包含了索引的键和值(值可能是行的主键、一行完整数据等,具体见前文),数据量更大。这里假设每个叶节点页面存储100条记录(实际上,当索引为聚簇索引时,这个数字可能不足100;当索引为辅助索引时,这个数字可能远大于100;可以根据实际情况进行估算)。

对于一颗3层b+树,第一层(根节点)有1个页面,可以存储1000条记录;第二层有1000个页面,可以存储1000*1000条记录;第三层(叶节点)有1000*1000个页面,每个页面可以存储100条记录,因此可以存储1000*1000*100条记录,即1亿条。而对于二叉树,存储1亿条记录则需要26层左右。

七、总结

最后,总结一下各种树解决的问题以及面临的新问题:

1)       二叉查找树(bst):解决了排序的基本问题,但是由于无法保证平衡,可能退化为链表;

2)       平衡二叉树(avl):通过旋转解决了平衡的问题,但是旋转操作效率太低;

3)       红黑树:通过舍弃严格的平衡和引入红黑节点,解决了avl旋转效率过低的问题,但是在磁盘等场景下,树仍然太高,io次数太多;

4)       b树:通过将二叉树改为多路平衡查找树,解决了树过高的问题;

5)       b+树:在b树的基础上,将非叶节点改造为不存储数据的纯索引节点,进一步降低了树的高度;此外将叶节点使用指针连接成链表,范围查询更加高效。

参考文献

《mysql技术内幕:innodb存储引擎》

《mysql运维内参》

https://zhuanlan.zhihu.com/p/54102723

https://cloud.tencent.com/developer/article/1425604

https://blog.csdn.net/whoamiyang/article/details/51926985

https://www.jianshu.com/p/37436ed14cc6

https://blog.csdn.net/crankz/article/details/83301702

https://www.cnblogs.com/gaochundong/p/btree_and_bplustree.html

 

创作不易,如果文章对你有帮助,就点个赞、评个论呗~

创作不易,如果文章对你有帮助,就点个赞、评个论呗~

创作不易,如果文章对你有帮助,就点个赞、评个论呗~

需要了解更多数据库技术:【深入学习MySQL】MySQL的索引结构为什么使用B+树?,都可以关注数据库技术分享栏目—编程笔记


推荐阅读
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • PHP设置MySQL字符集的方法及使用mysqli_set_charset函数
    本文介绍了PHP设置MySQL字符集的方法,详细介绍了使用mysqli_set_charset函数来规定与数据库服务器进行数据传送时要使用的字符集。通过示例代码演示了如何设置默认客户端字符集。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • 本文介绍了在SpringBoot中集成thymeleaf前端模版的配置步骤,包括在application.properties配置文件中添加thymeleaf的配置信息,引入thymeleaf的jar包,以及创建PageController并添加index方法。 ... [详细]
  • 关于我们EMQ是一家全球领先的开源物联网基础设施软件供应商,服务新产业周期的IoT&5G、边缘计算与云计算市场,交付全球领先的开源物联网消息服务器和流处理数据 ... [详细]
  • 本文介绍了在Mac上搭建php环境后无法使用localhost连接mysql的问题,并通过将localhost替换为127.0.0.1或本机IP解决了该问题。文章解释了localhost和127.0.0.1的区别,指出了使用socket方式连接导致连接失败的原因。此外,还提供了相关链接供读者深入了解。 ... [详细]
  • 解决VS写C#项目导入MySQL数据源报错“You have a usable connection already”问题的正确方法
    本文介绍了在VS写C#项目导入MySQL数据源时出现报错“You have a usable connection already”的问题,并给出了正确的解决方法。详细描述了问题的出现情况和报错信息,并提供了解决该问题的步骤和注意事项。 ... [详细]
  • 2018深入java目标计划及学习内容
    本文介绍了作者在2018年的深入java目标计划,包括学习计划和工作中要用到的内容。作者计划学习的内容包括kafka、zookeeper、hbase、hdoop、spark、elasticsearch、solr、spring cloud、mysql、mybatis等。其中,作者对jvm的学习有一定了解,并计划通读《jvm》一书。此外,作者还提到了《HotSpot实战》和《高性能MySQL》等书籍。 ... [详细]
  • 篇首语:本文由编程笔记#小编为大家整理,主要介绍了软件测试知识点之数据库压力测试方法小结相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 在IDEA中运行CAS服务器的配置方法
    本文介绍了在IDEA中运行CAS服务器的配置方法,包括下载CAS模板Overlay Template、解压并添加项目、配置tomcat、运行CAS服务器等步骤。通过本文的指导,读者可以轻松在IDEA中进行CAS服务器的运行和配置。 ... [详细]
  • 本文介绍了Hive常用命令及其用途,包括列出数据表、显示表字段信息、进入数据库、执行select操作、导出数据到csv文件等。同时还涉及了在AndroidManifest.xml中获取meta-data的value值的方法。 ... [详细]
  • 求解连通树的最小长度及优化
    本文介绍了求解连通树的最小长度的方法,并通过四边形不等式进行了优化。具体方法为使用状态转移方程求解树的最小长度,并通过四边形不等式进行优化。 ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • PatchODAX8: ... [详细]
  • 在搜索数据库中的数据时,您可以使用SQL通配符。SQL通配符在搜索数据库中的数据时,SQL通配符可以替代一个或多个字符。SQL通配符必须与LIKE运算符 ... [详细]
author-avatar
卫宇欢试试
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有