MYSQL索引——B+树讲解

2023-09-12 20:43:47

B-/B+树看 MySQL索引结构

B-树

B-树,这里的 B 表示 balance( 平衡的意思),B-树是一种多路自平衡的搜索树.它类似普通的平衡二叉树,不同的一点是B-树允许每个节点有更多的子节点。下图是 B-树的简化图.
在这里插入图片描述

B-树有如下特点:
所有键值分布在整颗树中;
任何一个关键字出现且只出现在一个结点中;
搜索有可能在非叶子结点结束;
在关键字全集内做一次查找,性能逼近二分查找;

B+ 树

B+树是B-树的变体,也是一种多路搜索树, 它与 B- 树的不同之处在于:
所有关键字存储在叶子节点出现,内部节点(非叶子节点并不存储真正的 data)
为所有叶子结点增加了一个链指针
简化 B+树 如下图
在这里插入图片描述

为什么使用B-/B+ Tree

红黑树等数据结构也可以用来实现索引,但是文件系统及数据库系统普遍采用B-/+Tree作为索引结构。MySQL 是基于磁盘的数据库系统,索引往往以索引文件的形式存储的磁盘上,索引查找过程中就要产生磁盘I/O消耗,相对于内存存取,I/O存取的消耗要高几个数量级,索引的结构组织要尽量减少查找过程中磁盘I/O的存取次数。为什么使用B-/+Tree,还跟磁盘存取原理有关。
局部性原理与磁盘预读
由于磁盘的存取速度与内存之间鸿沟,为了提高效率,要尽量减少磁盘I/O.磁盘往往不是严格按需读取,而是每次都会预读,磁盘读取完需要的数据,会顺序向后读一定长度的数据放入内存。而这样做的理论依据是计算机科学中著名的局部性原理:
当一个数据被用到时,其附近的数据也通常会马上被使用
程序运行期间所需要的数据通常比较集中
由于磁盘顺序读取的效率很高(不需要寻道时间,只需很少的旋转时间),因此对于具有局部性的程序来说,预读可以提高I/O效率.预读的长度一般为页(page)的整倍数。
MySQL(默认使用InnoDB引擎),将记录按照页的方式进行管理**,每页大小默认为16K(这个值可以修改)**.linux 默认页大小为4K

B-/+Tree索引的性能分析

实际实现B-Tree还需要使用如下技巧:
每次新建节点时,直接申请一个页的空间,这样就保证一个节点物理上也存储在一个页里,加之计算机存储分配都是按页对齐的,就实现了一个结点只需一次I/O。
假设 B-Tree 的高度为 h,B-Tree中一次检索最多需要h-1次I/O(根节点常驻内存),渐进复杂度为O(h)=O(logdN)O(h)=O(logdN)。一般实际应用中,出度d是非常大的数字,通常超过100,因此h非常小(通常不超过3)。
而红黑树这种结构,h明显要深的多。由于逻辑上很近的节点(父子)物理上可能很远,无法利用局部性,所以红黑树的I/O渐进复杂度也为O(h),效率明显比B-Tree差很多。

B-Tree和B+Tree中为什么优先选择B+Tree

B+树更适合外部存储,由于内节点无 data 域,一个结点可以存储更多的内结点,每个节点能索引的范围更大更精确,也意味着 B+树单次磁盘IO的信息量大于B-树,I/O效率更高
Mysql是一种关系型数据库,区间访问是常见的一种情况,B+树叶节点增加的指向相邻节点的链指针,加强了区间访问性,可使用在范围区间查询等,而B-树每个节点 key 和 data 在一起,则无法区间查找(between, <,>)。

B+Tree的定义

B+Tree是B树的变种,有着比B树更高的查询性能,来看下m阶B+Tree特征:
有m个子树的节点包含有m个元素(B-Tree中是m-1)
根节点和分支节点中不保存数据,只用于索引,所有数据都保存在叶子节点中。
所有分支节点和根节点都同时存在于子节点中,在子节点元素中是最大或者最小的元素。
叶子节点会包含所有的关键字,以及指向数据记录的指针,并且叶子节点本身是根据关键字的大小从小到大顺序链接。

在这里插入图片描述

红点表示是指向卫星数据的指针,指针指向的是存放实际数据的磁盘页,卫星数据就是数据库中一条数据记录。
叶子节点中还有一个指向下一个叶子节点的next指针,所以叶子节点形成了一个有序的链表,方便遍历B+树。

B+树的优势

1.更加高效的单元素查找
B+树的查找元素3的过程:
第一次磁盘IO
在这里插入图片描述

第二次磁盘IO
在这里插入图片描述

第三次磁盘IO
在这里插入图片描述

这个过程看下来,貌似与B树的查询过程没有什么区别。但实际上有两点不一样:
a、首先B+树的中间节点不存储卫星数据,所以同样大小的磁盘页可以容纳更多的节点元素,如此一来,相同数量的数据下,B+树就相对来说要更加矮胖些,磁盘IO的次数更少。
b、由于只有叶子节点才保存卫星数据,B+树每次查询都要到叶子节点;而B树每次查询则不一样,最好的情况是根节点,最坏的情况是叶子节点,没有B+树稳定。
2.叶子节点形成有顺链表,范围查找性能更优
B树范围查找3-8的过程
a、先查找3
在这里插入图片描述

b、再查找4、5、6、7、8,中间过程省略,直接到8的查找
在这里插入图片描述

这里查找的范围跨度越大,则磁盘IO的次数越多,性能越差。
B+树范围查找3-11的过程
在这里插入图片描述

先从上到下找到下限元素3,然后通过链表指针,依次遍历得到元素5/6/8/9/11;如此一来,就不用像B树那样一个个元素进行查找。

总结

1.单节点可以存储更多的元素,使得查询磁盘IO次数更少。
2.所有查询都要查找到叶子节点,查询性能稳定。
3.所有叶子节点形成有序链表,便于范围查询。
PS:在数据库的聚集索引(Clustered Index)中,叶子节点直接包含卫星数据。在非聚集索引(NonClustered Index)中,叶子节点带有指向卫星数据的指针。

更多推荐

【Apollo】开启Apollo之旅:让自动驾驶如此简单

前言Apollo是百度公司推出的自动驾驶平台。它是一个综合性的自动驾驶解决方案,提供了包括感知、决策、规划和控制等核心功能,以及地图、定位、仿真、数据管理等配套工具。文章目录前言Apollo的发展历程Apollo8.0新特性软件包管理感知框架工具链小结云端体验软件包安装总结活动活动介绍学习形式课程安排活动奖励报名方式A

Docker部署单点Elasticsearch与Kibana

一、创建网络因为需要部署kibana容器,因此需要让es和kibana容器互联。这里创建一个网络:dockernetworkcreatees-net#创建一个网络名称为:es-net二、拉取并加载镜像方式一dockerpullelasticsearch:7.12.1版本为elasticsearch的7.12.1版本的镜

Jenkins学习笔记3

git+github+jenkins:架构图:说明:jenkins知道github有更新了,就pull进行构建build,编译、自动化测试。然后部署到应用服务器。mavenjava的项目构建工具。在开发者电脑上创建空密码密钥对。[root@git-developer~]#gitclonegit@github.com:c

解决https页面加载http资源报错

为什么会报错?HTTPS页面加载HTTP资源会报错的原因是出于安全性考虑。HTTPS(HyperTextTransferProtocolSecure)是一种通过使用SSL/TLS加密通信来保护数据传输的协议,它确保了客户端和服务器之间的安全连接。当HTTPS页面尝试加载非加密的HTTP资源时,存在以下问题:混合内容警告

Scrapy爬虫框架实战

Python实现爬虫是很容易的,一般来说就是获取目标网站的页面,对目标页面的分析、解析、识别,提取有用的信息,然后该入库的入库,该下载的下载。以前写过一篇文章《Python爬虫获取电子书资源实战》,以一个电子书的网站为例来实现python爬虫获取电子书资源。爬取整站的电子书资源,按目录保存到本地,并形成索引文件方便查找

海康摄像头开发笔记(一):连接防爆摄像头、配置摄像头网段、设置rtsp码流、播放rtsp流、获取rtsp流、调优rtsp流播放延迟以及录像存储

文为原创文章,转载请注明原文出处本文章博客地址:https://hpzwl.blog.csdn.net/article/details/131679108红胖子(红模仿)的博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬结合等等)持续更新中…(点击传

git的使用(详细教程)通过命令行操作及vscode插件

个人仓库创建首先在网页中注册并登录gitee,然后进行如下操作:1、在Gitee页面右上角找点+号点击新建仓库2、填写一个仓库名称,下移将红框圈起的方框勾选上即可创建仓库(仓库介绍可写可不写)3、创建成功跳到如下界面4、此时不要关闭该页面,到文件中新建一个文件夹(文件夹名字随意,在C/D/F盘建都可以)打开新建的文件夹

RES 系列 GRES: Generalized Referring Expression Segmentation 论文阅读笔记

RES系列GRES:GeneralizedReferringExpressionSegmentation论文阅读笔记一、Abstract二、引言三、相关工作有关的指代任务和数据集指代分割方法四、任务设置及数据集4.1GRES设置RES回顾一般化的RES评估4.2gRefCOCO:一个大尺度的GRES数据集多目标样本计数

Kotlin File.reader BufferedReader readLine

KotlinFile.readerBufferedReaderreadLineimportjava.io.BufferedReaderimportjava.io.Filefunmain(args:Array<String>){valfilePath="./myfile.txt"valfile=File(filePath

【跟小嘉学 Rust 编程】二十八、Rust中的日期与时间(DateTime)

系列文章目录【跟小嘉学Rust编程】一、Rust编程基础【跟小嘉学Rust编程】二、Rust包管理工具使用【跟小嘉学Rust编程】三、Rust的基本程序概念【跟小嘉学Rust编程】四、理解Rust的所有权概念【跟小嘉学Rust编程】五、使用结构体关联结构化数据【跟小嘉学Rust编程】六、枚举和模式匹配【跟小嘉学Rust

【Vue入门】MVVM数据双向绑定与Vue的生命周期

目录一、Vue介绍1.1什么是Vue?1.2Vue的优点1.3库与框架的区别二、Vue入门2.1MVVM(数据双向绑定)2.2BootCDN(加速服务)三、Vue实例3.1Vue开发示例3.2双向数据绑定3.3Vue生命周期钩子一、Vue介绍1.1什么是Vue?Vue是一个渐进式的JavaScript框架,用于构建用户

热文推荐