您当前的位置:首页 > 电脑百科 > 数据库 > MYSQL

Mysql索引为什么使用B+树而不使用跳表?

时间:2022-04-18 10:01:34  来源:  作者:二哥学Java

在我们的印象中,MySQL数据表里无非就是存储一行行的数据。跟个Excel似的。

直接遍历这一行行数据,性能就是O(n),比较慢。为了加速查询,使用了B+树来做索引,将查询性能优化到了O(lg(n))

但问题就来了,查询数据性能在 lg(n) 级别的数据结构有很多,比如redis的zset里用到的跳表,也是lg(n),并且实现还贼简单。

那为什么mysql的索引,不使用跳表呢?

我们今天就来聊聊这个话题。

 

B+树的结构

之前的一篇文章里,已经提到过B+树的结构了。文章不长,如果没看过,建议先看下。

当然,不看也行。

在这里,为了混点字数,我简单总结下B+树的结构。

Mysql的索引为什么使用B+树而不使用跳表?

 

B+树查询过程

如上图,一般B+树是由多个页组成的多层级结构,每个页16Kb,对于主键索引来说,最末级的叶子结点放行数据,非叶子结点放的则是索引信息(主键id和页号),用于加速查询。

比方说我们想要查找行数据5。会先从顶层页的record们入手。record里包含了主键id和页号(页地址)。关注黄色的箭头,向左最小id是1,向右最小id是7。那id=5的数据如果存在,那必定在左边箭头。于是顺着的record的页地址就到了6号数据页里,再判断id=5>4,所以肯定在右边的数据页里,于是加载105号数据页。

在105号数据页里,虽然有多行数据,但也不是挨个遍历的,数据页内还有个页目录的信息,它可以通过二分查找的方式加速查询行数据,于是找到id=5的数据行,完成查询。

从上面可以看出,B+树利用了空间换时间的方式(构造了一批非叶子结点用于存放索引信息),将查询时间复杂度从O(n)优化为O(lg(n))

 

跳表的结构

看完B+树,我们再来看下跳表是怎么来的。

同样的,还是为了存储一行行的数据。

我们可以将它们用链表串起来。

Mysql的索引为什么使用B+树而不使用跳表?

 

单链表

想要查询链表中的其中一个结点,时间复杂度是O(n),这谁顶得住,于是将部分链表结点提出来,再构建出一个新的链表。

Mysql的索引为什么使用B+树而不使用跳表?

 

两层跳表

这样当我想要查询一个数据的时候,我先查上层的链表,就很容易知道数据落在哪个范围,然后跳到下一个层级里进行查询。这样就把搜索范围一下子缩小了一大半。

比如查询id=10的数据,我们先在上层遍历,依次判断1,6,12,很快就可以判断出10在6到12之间,然后往下一跳,就可以在遍历6,7,8,9,10之后,确定id=10的位置。直接将查询范围从原来的1到10,变成现在的1,6,7,8,9,10,算是砍半了。

Mysql的索引为什么使用B+树而不使用跳表?

 

两层跳表查找id为10的数据

既然两层链表就直接将查询范围砍半了,那我多加几层,岂不妙哉?

于是跳表就这样变成了多层。

Mysql的索引为什么使用B+树而不使用跳表?

 

三层跳表

如果还是查询id=10的数据,就只需要查询1,6,9,10就能找到,比两层的时候更快一些。

Mysql的索引为什么使用B+树而不使用跳表?

 

三层跳表查询id为10的数据

可以看出,跳表也是通过牺牲空间换取时间的方式提升查询性能。时间复杂度都是lg(n)

 

B+树和跳表的区别

从上面可以看到,B+树和跳表的最下面一层,都包含了所有的数据,且都是顺序的,适合用于范围查询。往上的层级都是构建出来用于提升搜索性能的。这两者实在是太像了。但他们两者在新增和删除数据时,还是有些区别的。下面我们以新增数据为例聊一下。

 

B+树新增数据会怎么样

B+树本质上是一种多叉平衡二叉树。关键在于"平衡"这两个字,对于多叉树结构来说,它的含义是子树们的高度层级尽量一致(一般最多差一个层级),这样在搜索的时候,不管是到哪个子树分支,搜索次数都差不了太多。

当数据库表不断插入新的数据时,为了维持B+树的平衡,B+树会不断分裂调整数据页。

我们知道B+树分为叶子结点和非叶子结点

当插入一条数据时,叶子结点和它上层的索引结点(非叶子结点)最大容量都是16k,它们都有可能会满。

为了简化问题,我们假设一个数据页只能放三条行数据或索引。

加入一条数据,根据数据页会不会满,分为三种情况。

  • 叶子结点和索引结点都没满。这种情况最简单,直接插入到叶子结点中就好了。
Mysql的索引为什么使用B+树而不使用跳表?

 

叶子和非叶子都未满

  • 叶子结点满了,但索引结点没满。此时需要拆分叶子结点,同时索引结点要增加新的索引信息。
Mysql的索引为什么使用B+树而不使用跳表?

 

叶子满了但非叶子未满.drawio

  • 叶子结点满了,且索引结点也满了。叶子和索引结点都要拆分,同时往上还要再加一层索引。
Mysql的索引为什么使用B+树而不使用跳表?

 

叶子和非叶子都满了

从上面可以看到,只有在叶子和索引结点都满了的情况下,B+树才会考虑加入一层新的结点。

而从之前的文章知道,要把三层B+树塞满,那大概需要2kw左右的数据。

 

跳表新增数据

跳表同样也是很多层,新增一个数据时,最底层的链表需要插入数据。

此时,是否需要在上面的几层中加入数据做索引呢?

这个就纯靠随机函数了。

理论上为了达到二分的效果,每一层的结点数需要是下一层结点数的二分之一。

也就是说现在有一个新的数据插入了,它有50%的概率需要在第二层加入索引,有25%的概率需要在第三层加个索引,以此类推,直到最顶层。

举个例子,如果跳表中插入数据id=6,且随机函数返回第三层(有25%的概率),那就需要在跳表的最底层到第三层都插入数据。

Mysql的索引为什么使用B+树而不使用跳表?

 

跳表插入数据

如果这个随机函数设计成上面这样,当数据量样本足够大的时候,数据的分布就符合我们理想中的"二分"。

跟上面B+树不一样,跳表是否新增层数,纯粹靠随机函数,根本不关心前后上下结点。

 

好了,基础科普也结束了,我们可以进入正题了。

 

Mysql的索引为什么使用B+树而不使用跳表?

B+树是多叉树结构,每个结点都是一个16k的数据页,能存放较多索引信息,所以扇出很高三层左右就可以存储2kw左右的数据(知道结论就行,想知道原因可以看之前的文章)。也就是说查询一次数据,如果这些数据页都在磁盘里,那么最多需要查询三次磁盘IO

 

跳表是链表结构,一条数据一个结点,如果最底层要存放2kw数据,且每次查询都要能达到二分查找的效果,2kw大概在2的24次方左右,所以,跳表大概高度在24层左右。最坏情况下,这24层数据会分散在不同的数据页里,也即是查一次数据会经历24次磁盘IO

因此存放同样量级的数据,B+树的高度比跳表的要少,如果放在mysql数据库上来说,就是磁盘IO次数更少,因此B+树查询更快

而针对写操作,B+树需要拆分合并索引数据页,跳表则独立插入,并根据随机函数确定层数,没有旋转和维持平衡的开销,因此跳表的写入性能会比B+树要好。

其实,mysql的存储引擎是可以换的,以前是myisam,后来才有的innodb,它们底层索引用的都是B+树。也就是说,你完全可以造一个索引为跳表的存储引擎装到mysql里。事实上,facebook造了个rocksDB的存储引擎,里面就用了跳表。直接说结论,它的写入性能确实是比innodb要好,但读性能确实比innodb要差不少。感兴趣的话,可以在文章最后面的参考资料里看到他们的性能对比数据。

 

redis为什么使用跳表而不使用B+树或二叉树呢?

redis支持多种数据结构,里面有个有序集合,也叫ZSET。内部实现就是跳表。那为什么要用跳表而不用B+树等结构呢?

这个几乎每次面试都要被问一下。

虽然已经很熟了,但每次都要装作之前没想过,现场思考一下才知道答案。

真的,很考验演技。

大家知道,redis 是纯纯的内存数据库。

进行读写数据都是操作内存,跟磁盘没啥关系,因此也不存在磁盘IO了,所以层高就不再是跳表的劣势了。

并且前面也提到B+树是有一系列合并拆分操作的,换成红黑树或者其他AVL树的话也是各种旋转,目的也是为了保持树的平衡

而跳表插入数据时,只需要随机一下,就知道自己要不要往上加索引,根本不用考虑前后结点的感受,也就少了旋转平衡的开销

因此,redis选了跳表,而不是B+树。

 

总结

  • B+树是多叉平衡搜索树,扇出高,只需要3层左右就能存放2kw左右的数据,同样情况下跳表则需要24层左右,假设层高对应磁盘IO,那么B+树的读性能会比跳表要好,因此mysql选了B+树做索引。
  • redis的读写全在内存里进行操作,不涉及磁盘IO,同时跳表实现简单,相比B+树、AVL树、少了旋转树结构的开销,因此redis使用跳表来实现ZSET,而不是树结构。
  • 存储引擎RocksDB内部使用了跳表,对比使用B+树的innodb,虽然写性能更好,但读性能属实差了些。在读多写少的场景下,B+树依旧YYDS。

参考资料

《MYSQL内核:INNODB存储引擎 卷1》

《RocksDB和Innodb引擎性能PK胜负难料?》

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

 

最后

最近在看《龙蛇演义》,剧情很一般,但我硬是一口气看到了最新一集,还很上头。

为啥?

点开它,看到女主角的时候你就理解我了。

Mysql的索引为什么使用B+树而不使用跳表?

 

这么说吧,一个颜值出众,身材火辣的姐姐,还是个世界顶级的武术高手,穿着旗袍,踩着高跟,做着各种让牛顿棺材板都快要按不住的动作,只为手把手教会你武术基本功。

这时候,剧情还重要吗?

不得不说,当我看到姐姐穿成这样用木棍顶起400斤的汞球时。

Mysql的索引为什么使用B+树而不使用跳表?

 

我可以肯定,导演根本不懂物理。

 

但是!

 

导演很懂男人!

 

这不得不让我陷入沉思,到底什么才是好的内容?

难道现在有个大姐姐穿个黑丝高跟超短裙,教你变量的声明和定义这么基础的东西,你也会去看吗?

我不知道你们会不会。

 

反正我会。



Tags:Mysql索引   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
mysql索引失效的场景
MySQL中索引失效是指数据库查询时无法有效利用索引,这可能导致查询性能显著下降。以下是一些常见的MySQL索引失效的场景:1.使用非前导列进行查询: 假设有一个复合索引 (A, B)。...【详细内容】
2024-01-15  Search: Mysql索引  点击:(88)  评论:(0)  加入收藏
理解Mysql索引原理及特性
作为开发人员,碰到了执行时间较长的 sql 时,基本上大家都会说” 加个索引吧”。但是索引是什么东西,索引有哪些特性,下面和大家简单讨论一下。1 索引如何工作,是如何加快查询速度...【详细内容】
2023-12-22  Search: Mysql索引  点击:(102)  评论:(0)  加入收藏
MySQL索引入门看这个就够了
MySQL索引是数据库中一个关键的概念,它可以极大地提高查询性能,加快数据检索速度。但是,要充分发挥索引的作用,需要深入理解它们的工作原理和使用方式。在本文中,我们将深入解析M...【详细内容】
2023-11-16  Search: Mysql索引  点击:(210)  评论:(0)  加入收藏
解密MySQL索引原理与优化策略:Java开发者必读
MySQL索引是数据库管理系统中的关键概念之一,用于提高查询性能和加速数据的检索。下面将介绍MySQL索引的原理和优化策略,供Java开发者参考。一、MySQL索引原理1、索引的定义:索...【详细内容】
2023-11-01  Search: Mysql索引  点击:(209)  评论:(0)  加入收藏
Mysql索引怎么设置更加合理
Mysql是我们经常使用到的数据库,因为很多时候是免费的,所以用的比较多,我们在设计表的时候应该会使用到索引,所以我们一起来聊下索引应该怎么去设置1、索引的定义什么叫索引 ,索...【详细内容】
2023-08-30  Search: Mysql索引  点击:(324)  评论:(0)  加入收藏
MySQL索引,快速记忆法
哈喽,大家好,我是了不起。面试的时候,面试官总喜欢问一些关于MySQL索引的问题,但是如果单纯的记忆,还是有难度的;今天了不起把MySQL索引的知识点进行汇总,方便大家快速记忆MySQL索...【详细内容】
2023-05-26  Search: Mysql索引  点击:(242)  评论:(0)  加入收藏
一线大厂面试的MySQL索引夺命连环15问
我们准备了索引的15连问,相信大家看完肯定会有帮助的。1. 索引是什么? 索引是一种能提高数据库查询效率的数据结构。它可以比作一本字典的目录,可以帮你快速找到对应的记录。 ...【详细内容】
2023-05-08  Search: Mysql索引  点击:(333)  评论:(0)  加入收藏
MySQL索引数据结构入门
之前松哥写过一个 MySQL 系列,但是当时是基于 MySQL5.7 的,最近有空在看 MySQL8 的文档,发现和 MySQL5.7 相比还是有不少变化,同时 MySQL 又是小伙伴们在面试时一个非常重要的知...【详细内容】
2023-04-13  Search: Mysql索引  点击:(144)  评论:(0)  加入收藏
深入理解MySQL索引底层数据结构
1 引言 在日常工作中,我们会遇见一些慢SQL,在分析这些慢SQL时,我们通常会看下SQL的执行计划,验证SQL执行过程中有没有走索引。通常我们会调整一些查询条件,增加必要的索引,SQL执行...【详细内容】
2023-04-06  Search: Mysql索引  点击:(215)  评论:(0)  加入收藏
Mysql索引为什么使用B+树而不使用跳表?
在我们的印象中,mysql数据表里无非就是存储一行行的数据。跟个excel似的。直接遍历这一行行数据,性能就是O(n),比较慢。为了加速查询,使用了B+树来做索引,将查询性能优化到了O(lg...【详细内容】
2022-04-18  Search: Mysql索引  点击:(1021)  评论:(0)  加入收藏
▌简易百科推荐
MySQL 核心模块揭秘
server 层会创建一个 SAVEPOINT 对象,用于存放 savepoint 信息。binlog 会把 binlog offset 写入 server 层为它分配的一块 8 字节的内存里。 InnoDB 会维护自己的 savepoint...【详细内容】
2024-04-03  爱可生开源社区    Tags:MySQL   点击:(10)  评论:(0)  加入收藏
MySQL 核心模块揭秘,你看明白了吗?
为了提升分配 undo 段的效率,事务提交过程中,InnoDB 会缓存一些 undo 段。只要同时满足两个条件,insert undo 段或 update undo 段就能被缓存。1. 关于缓存 undo 段为了提升分...【详细内容】
2024-03-27  爱可生开源社区  微信公众号  Tags:MySQL   点击:(17)  评论:(0)  加入收藏
MySQL:BUG导致DDL语句无谓的索引重建
对于5.7.23之前的版本在评估类似DDL操作的时候需要谨慎,可能评估为瞬间操作,但是实际上线的时候跑了很久,这个就容易导致超过维护窗口,甚至更大的故障。一、问题模拟使用5.7.22...【详细内容】
2024-03-26  MySQL学习  微信公众号  Tags:MySQL   点击:(14)  评论:(0)  加入收藏
从 MySQL 到 ByteHouse,抖音精准推荐存储架构重构解读
ByteHouse是一款OLAP引擎,具备查询效率高的特点,在硬件需求上相对较低,且具有良好的水平扩展性,如果数据量进一步增长,可以通过增加服务器数量来提升处理能力。本文将从兴趣圈层...【详细内容】
2024-03-22  字节跳动技术团队    Tags:ByteHouse   点击:(29)  评论:(0)  加入收藏
MySQL自增主键一定是连续的吗?
测试环境:MySQL版本:8.0数据库表:T (主键id,唯一索引c,普通字段d)如果你的业务设计依赖于自增主键的连续性,这个设计假设自增主键是连续的。但实际上,这样的假设是错的,因为自增主键不...【详细内容】
2024-03-10    dbaplus社群  Tags:MySQL   点击:(14)  评论:(0)  加入收藏
准线上事故之MySQL优化器索引选错
1 背景最近组里来了许多新的小伙伴,大家在一起聊聊技术,有小兄弟提到了MySQL的优化器的内部策略,想起了之前在公司出现的一个线上问题,今天借着这个机会,在这里分享下过程和结论...【详细内容】
2024-03-07  转转技术  微信公众号  Tags:MySQL   点击:(32)  评论:(0)  加入收藏
MySQL数据恢复,你会吗?
今天分享一下binlog2sql,它是一款比较常用的数据恢复工具,可以通过它从MySQL binlog解析出你要的SQL,并根据不同选项,可以得到原始SQL、回滚SQL、去除主键的INSERT SQL等。主要...【详细内容】
2024-02-22  数据库干货铺  微信公众号  Tags:MySQL   点击:(54)  评论:(0)  加入收藏
如何在MySQL中实现数据的版本管理和回滚操作?
实现数据的版本管理和回滚操作在MySQL中可以通过以下几种方式实现,包括使用事务、备份恢复、日志和版本控制工具等。下面将详细介绍这些方法。1.使用事务:MySQL支持事务操作,可...【详细内容】
2024-02-20  编程技术汇    Tags:MySQL   点击:(54)  评论:(0)  加入收藏
MySQL数据库如何生成分组排序的序号
经常进行数据分析的小伙伴经常会需要生成序号或进行数据分组排序并生成序号。在MySQL8.0中可以使用窗口函数来实现,可以参考历史文章有了这些函数,统计分析事半功倍进行了解。...【详细内容】
2024-01-30  数据库干货铺  微信公众号  Tags:MySQL   点击:(55)  评论:(0)  加入收藏
mysql索引失效的场景
MySQL中索引失效是指数据库查询时无法有效利用索引,这可能导致查询性能显著下降。以下是一些常见的MySQL索引失效的场景:1.使用非前导列进行查询: 假设有一个复合索引 (A, B)。...【详细内容】
2024-01-15  小王爱编程  今日头条  Tags:mysql索引   点击:(88)  评论:(0)  加入收藏
站内最新
站内热门
站内头条