面试题:MySQL为什么选择B+树作为索引结构


前言

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


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

二叉查找树(BST,Binary Search Tree),也叫二叉排序树,在二叉树的基础上需要满足:任意节点的左子树上所有节点值不大于根节点的值,任意节点的右子树上所有节点值不小于根节点的值。如下是一颗BST(图片来源)。

图片

当需要快速查找时,将数据存储在BST是一种常见的选择,因为此时查询时间取决于树高,平均时间复杂度是O(lgn)。然而BST可能长歪而变得不平衡,如下图所示(图片来源),此时BST退化为链表,时间复杂度退化为O(n)。
为了解决这个问题,引入了平衡二叉树。

图片

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

AVL树是严格的平衡二叉树,所有节点的左右子树高度差不能超过1;AVL树查找、插入和删除在平均和最坏情况下都是O(lgn)。
AVL实现平衡的关键在于旋转操作:插入和删除可能破坏二叉树的平衡,此时需要通过一次或多次树旋转来重新平衡这个树。当插入数据时,最多只需要1次旋转(单旋转或双旋转);但是当删除数据时,会导致树失衡,AVL需要维护从被删除节点到根节点这条路径上所有节点的平衡,旋转的量级为O(lgn)。
由于旋转的耗时,AVL树在删除数据时效率很低;在删除操作较多时,维护平衡所需的代价可能高于其带来的好处,因此AVL实际使用并不广泛。

三、红黑树:树太高

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

与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树的例子(图片来源):

图片

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个页面,可以存储10001000条记录;第三层(叶节点)有10001000个页面,每个页面可以存储100条记录,因此可以存储10001000100条记录,即1亿条。而对于二叉树,存储1亿条记录则需要26层左右。


七、总结

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

  1. 二叉查找树(BST):解决了排序的基本问题,但是由于无法保证平衡,可能退化为链表;
  2. 平衡二叉树(AVL):通过旋转解决了平衡的问题,但是旋转操作效率太低;
  3. 红黑树:通过舍弃严格的平衡和引入红黑节点,解决了AVL旋转效率过低的问题,但是在磁盘等场景下,树仍然太高,IO次数太多;
  4. B树:通过将二叉树改为多路平衡查找树,解决了树过高的问题;
  5. B+树:在B树的基础上,将非叶节点改造为不存储数据的纯索引节点,进一步降低了树的高度;此外将叶节点使用指针连接成链表,范围查询更加高效。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/209554.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Redis命令详解

文章目录 Key(键) DEL EXISTS EXPIRE EXPIREAT PEXPIRE PEXPIREAT PERSIST KEYS TTL PTTL RENAME RENAMENX TYPE SCAN HSCAN SSCAN ZSCAN DUMP String(字符串) SET GET INCR DECR MSET MGET APPEND SETNX STRLEN INCRBY DECRBY IN…

opencv知识库:cv2.add()函数和“+”号运算符

需求场景 现有一灰度图像,需求是为该图像增加亮度。 原始灰度图像 预期目标图像 解决方案 不建议的方案——“”运算符 假设我们需要为原始灰度图像的亮度整体提升88,那么利用“”运算符的源码如下: import cv2img_path r"D:\pych…

Django二转Day03 04

0 cbv执行流程,self问题 path(index/, Myview.as_view()),Myview.as_view() 实例化后返回 变成return Myview.dispatch(request, *args, **kwargs)但是视图函数Myview中没有 dispatch 方法 所以去 父类View中寻找return View.dispatch(request, *args, **kwargs)调用…

jmeter接口自动化部署jenkins教程

首先,保证本地安装并部署了jenkins,jmeter,xslproc 我搭建的自动化测试框架是jmeterjenkinsxslproc ---注意:原理是,jmeter自生成的报告jtl文件,通过xslproc工具,再结合jmeter自带的模板修改&…

9.Spring 整合 Redis

引入依赖:spring-boot-starter-data-redis配置 Redis:配置数据库参数、编写配置类,构造 RedisTemplate访问 Redis: redisTemplate.opsForValue() redisTemplate.opsForHash() redisTemplate.opsForList() redisTemplate.opsForSe…

el-table 删除某行数据时 删除语句包含行号/序号

el-table可展示每行数据的序号列,在点击删除按钮的时候,会获取到该行所有的数据值,但是要想删除时提示到具体的序号,如:“是否确认删除序号为1的数据项?”,我是这样写的: /** 删除按…

C++ Easyx 让圆球跟随鼠标移动

目录 下载Easyx 检验 绘制窗口 画圆 响应事件的处理 清除原先绘图 渲染缓冲区 逻辑 代码托管 下载Easyx 在Easyx官网下载大暑版: 检验 写如下代码: 编译运行,如果控制台出现2023字样,代表配置成功: 绘制窗口 进入Eaxy官方网站,点…

【Flink进阶】-- Flink kubernetes operator 快速入门与实战

1、课程目录 2、课程链接 https://edu.csdn.net/course/detail/38831

代码随想录第二十三天(一刷C语言)|组合总数组合总数II分割回文串

创作目的:为了方便自己后续复习重点,以及养成写博客的习惯。 一、组合总数 思路:参考carl文档 定义两个全局变量,二维数组result存放结果集,数组path存放符合条件的结果。(这两个变量可以作为函数参数传入…

【电机控制】PMSM无感foc控制(五)相电流检测及重构 — 单电阻采样

0. 前言 相电流采样再FOC控制中是一个关键的环节,鉴于成本和易用性,目前应用较多的相电流采样方式是分流电阻采样,包括单电阻、双电阻以及三电阻采样法。 本章节先讲解单电阻采样相电流的检测及重构技术,在下一章讲解双电阻和三电…

使用postman请求x5接口

x5接口简介 1.接口样例 {"header"{"appid":"bpmnew_fanwei","sign":"C033162E86E4CADE80C7EB44D68A5AD2","sign_type":"md5","url":"https://oa.mioffice.cn/api/bpm/xm/app/show/tod…

预约按摩小程序有哪些功能特点?

随着科技的飞速发展,我们的生活方式发生了翻天覆地的变化。现在,只需动动手指,就能解决许多生活中的问题。同城预约上门按摩小程序,就是这样一个方便、快捷的解决方案。 在忙碌的生活中,身心疲惫的人们急需一种快速有效…

代码签名证书的作用

代码签名证书也是一种数字证书,它主要用于证明软件的来源和完整性。通过使用这种证书,开发者可以在发布软件时对其代码进行数字签名,以确保用户下载的是未经篡改的原始版本。 代码签名证书通过数字签名技术,为软件添加了一个数字签…

蓝桥杯每日一题2023.12.4

题目描述 竞赛中心 - 蓝桥云课 (lanqiao.cn) 题目分析 本题使用树型DP,蓝桥杯官网出现了一个点的错误,但实际答案是正确的 状态表示:f[u]:在以u为根的子树中包含u的所有联通块的权值的最大值 假设s1,s2,…sk 是u的…

动能资讯 | 智能音箱—万物物联新纽带

音箱市场在过去几年经历了显着的增长,这主要得益于数字音乐的普及和技术创新的推动。随着语音助手技术的发展,智能音箱如Amazon Echo、Google Home、Apple HomePod等逐渐成为市场中的热点。这些音箱不仅提供音频播放功能,还整合了语音识别和智…

【PyTorch】softmax回归

文章目录 1. 模型与代码实现1.1. 模型1.2. 代码实现 2. Q&A 1. 模型与代码实现 1.1. 模型 背景 在分类问题中,模型的输出层是全连接层,每个类别对应一个输出。我们希望模型的输出 y ^ j \hat{y}_j y^​j​可以视为属于类 j j j的概率,然…

关于你对 Zookeeper 的理解

看看普通人和高手是如何回答这个问题的? 普通人 Zookeeper 是一种开放源码的分布式应用程序协调服务 是一个分布式的小文件存储系统 一般对开发者屏蔽分布式应用开发过过程种的底层细节 用来解决分布式集群中应用系统的一致性问题 高手 对于 Zookeeper 的理解…

win10使用copilot(尝试中)

一、 Microsoft account | Sign In or Create Your Account Today – Microsoft 一路next全部点好【1】 二、 查看当前win10的版本,cmd输入命令winver 三、 修改区域为美国 四、更新和安全 Reference 【1】完美|在 Win10 强行开启 Win11 的独有功能…

IDEA2023找不到 Allow parallel run

我的idea版本:2023.1.4 第一步:点击Edit Configrations 第二步:点击Modify options 第三步:勾选Allow multiple instances 最后点击Apply应用一下 ok,问题解决!

COMP4121Advanced Algorithms

COMP4121Advanced Algorithms WeChat:yj4399_ Sina Visitor System