为什么Mysql的常用引擎都默认使用B+树作为索引?
一、前言
为了讲清楚这个问题,我们要先了解什么是索引。
我记得刚刚学习数据库的时候,老师喜欢用书本的目录来类比数据库的索引,并告诉我们索引能够像目录一样,让我们更快地找到想要找到的数据。
如果是第一次接触索引,这个比喻能够让我们有一个直观的印象。但是当深入去学习索引的时候,我们不能继续持有索引即目录的思想,我们要跳出来去思考索引的本质是什么。
二、索引的本质
在没有索引的情况下,我们查找数据只能按照从头到尾的顺序逐行查找,每查找一行数据,意味着我们要到到磁盘相应的位置去读取一条数据。
如果把查询一条数据分为到磁盘中查询和比对查询条件两步的话,到磁盘中查询的时间会远远大于比对查询条件的时间,这意味着在一次查询中,磁盘io占用了大部分的时间。更进一步地说,一次查询的效率取绝于磁盘io的次数,如果我们能够在一次查询中尽可能地降低磁盘io的次数,那么我们就能加快查询的速度。
在知道了减少磁盘io能加快查询速度后,我们就要聚焦于如何减少磁盘io。如果按照原表逐行查询的话,n条数据就要查询n次,也就是O(N)的时间复杂度,为了减少磁盘io的次数,我们必须用一种查询时间复杂度更低的数据结构来保存数据。
这种查询时间复杂度低的数据结构,我们称之为索引。所以通俗来说,索引其实就是某种数据结构,能充当索引的数据结构是多种多样的。
三、索引的选择
既然索引是一种便于查询的数据结构,如果大家对数据结构有一定了解的话,大概率会首选树型结构。毕竟树型结构普遍有着O(logN)的查询时间复杂度,而且插入删除数据的性能也比较平均。(可能你会说数组,哈希表的查询速度也很高啊,这个后面也会分析)
虽然我们都已经知道Mysql中最常用的引擎像InnoDB和MyISAM,最终都选择了B+树作为索引,但是这里我还是打算从最常见的二叉树开始讲起,推导一下为什么最终选择了B+树作为索引,并比较一下几种树型结构在充当索引时的优劣。
二叉树
最普通的二叉树的问题在于他不能保证O(logN)的查询时间复杂度,我们看下面的图:
由于插入的元素逐渐增大,元素始终在右边进行插入,好好的一棵二叉树最终变成了一条“链表”。在这种极端的情况下,二叉树的查询时间复杂度不再是O(logN),而是退化为O(N),这样显然不符合索引的要求。
平衡二叉树(红黑树)
像红黑树这样的平衡二叉树,无论如何插入元素,他都可以通过一些旋转的方法调整树的高度,使得整棵树的查询效率维持在O(logN),如下图所示:
这么来说他已经符合了成为索引的必备条件,但是最终没有选择他作为索引说明还有不足的地方。仔细看看可以发现平衡二叉树的每个节点只有两个孩子节点,如果一张表的数据量特别大,整棵树的高度也会随之上升。一个千万级别的表如果用平衡二叉树作为索引的话,树高将会达到二十多层。这也就意味着做一次查询需要二十多次磁盘io,这是一个不小的开销。
那么有没有能在大数据量的情况下,还能保持一个较小树高的树型结构呢?
B树和B+树
答案就是B树。上面我们说到了平衡二叉树的瓶颈在于一个节点只有两个孩子节点,而B树一个节点可以存放N个孩子节点,这就完美解决了树高的问题,我们可以把B树称为平衡多叉树,B树作为索引如下图所示:
但是以B树的结构作为索引仍有可以优化的地方,我们先看看最终的B+树,再仔细分析B+树在B树的基础上作了哪些改进,为什么B+树最终能够胜任索引的工作:
从图片中可以看到B+树同样是一棵多差平衡树,和B树一样很好地解决了树高的问题。
改进点一:
但仔细看可以发现,B树的节点中既存储索引,也存储表对应的数据;而B+树的非叶子节点是不存储数据的,只存储索引,数据全部存储在叶子节点上。
为什么要做这样的改进?我们做一次算术就知道了。
假设树高为2,主键ID为bigint类型,长度为8字节,节点指针为6字节,一行数据记录的大小为1k,一次io操作能获得一页16k的数据。
在索引为B+树的情况下,根节点能存储:16k / (6 + 8) = 1170 条索引指针;到了第一层,一共能指向 1170 * 1170 = 1368900 条索引指针;到了最底一层叶子节点,一个节点能存储16k / 1k = 16 条记录,一共能存储 1170 * 1170 * 16 = 21902400 条记录
在B树的情况下,由于非叶子节点使用了大量空间存储数据,存放的索引指针肯定就少,最终整棵树如果想要存储和B+树一样多的数据就必须要增加树高,这样一来就增加了磁盘io,所以说B+树作为索引的性能比B树高。
改进点二:
叶子节点之间使用指针连接,提高区间访问效率。如果我们要进行范围查询,可以轻松通过B+树叶子节点之间的指针进行遍历,减少了不必要的磁盘io。
总结
看到这里,相信大家对为什么Mysql的常用引擎都默认使用B+树作为索引已经有了初步的认知。我们只要牢记一点:索引是为了减少磁盘io提高查询性能而存在的。
最后回应一下为什么不常用哈希表和数组作为索引
哈希表虽然单一个值的查询效率很高,但是撑不住范围查询,哪个公司的业务还没个范围查询呢?
而数组虽然查询的效率高,但是增加和删除的效率低,由于记录在增加和删除的时候索引也得跟着维护,这会导致大数据量的情况下,增加或删除一条记录效率较低。
为什么Mysql的常用引擎都默认使用B+树作为索引?的更多相关文章
- MySQL 的常用引擎
1. InnoDB InnoDB 的存储文件有两个,后缀名分别是 .frm 和 .idb,其中 .frm 是表的定义文件,而 idb 是数据文件. InnoDB 中存在表锁和行锁,不过行锁是在命中索引 ...
- mysql的常用引擎
在MySQL数据库中,常用的引擎主要就是2个:Innodb和MyIASM. 首先: 1.简单介绍这两种引擎,以及该如何去选择.2.这两种引擎所使用的数据结构是什么. 1. a.Innodb引擎,Inn ...
- MySQL数据库常用引擎
在MySQL数据库中,常用的引擎主要就是2个:Innodb和MyIASM. 首先: 1.简单介绍这两种引擎,以及该如何去选择.2.这两种引擎所使用的数据结构是什么. 1. a.Innodb引擎,Inn ...
- 为什么用B+树做索引&MySQL存储引擎简介
索引的数据结构 为什么不是二叉树,红黑树什么的呢? 首先,一般来说,索引本身也很大,不可能全部存在内存中,因此索引往往以索引文件的方式存在磁盘上.然后一般一个结点一个磁盘块,也就是读一个结点要进行一次 ...
- 2020-05-18:MYSQL为什么用B+树做索引结构?平时过程中怎么加的索引?
福哥答案2020-05-18:此答案来自群员:因为4.0成型那个年代,B树体系大量用于文件存储系统,甚至当年的Longhorn的winFS都是基于b树做索引,开源而且好用的也就这么个体系了.B+树的磁 ...
- Mysql 常用引擎的特点及选择使用策略
Mysql 常用引擎的特点及选择使用策略 Mysql数据库常用存储引擎 Mysql数据库是一款开源的数据库,支持多种存储引擎的选择,比如目前最常用的存储引擎有:MyISAM,InnoDB,Memory ...
- mysql中有多种存储引擎,每种引擎都有自己的特色
mysql中有多种存储引擎,每种引擎都有自己的特色. 用途: MyISAM:快读, Memory:内存数据, InnoDB:完整的事务支持 锁: MyISAM:全表锁定, Memory:全表锁定, I ...
- MySql的多存储引擎架构, 默认的引擎InnoDB与 MYISAM的区别(滴滴)
1.存储引擎是什么? MySQL中的数据用各种不同的技术存储在文件(或者内存)中.这些技术中的每一种技术都使用不同的存储机制.索引技巧.锁定水平并且最终提供广泛的不同的功能和能力.通过选择不同的技术, ...
- 简单描述MySQL常用引擎的特点及MySQL的逻辑架构
目录 表的分类数据库引擎? 首先得说说mysql的逻辑架构,它整体分为3层: 常用引擎: 补充知识点: 表的分类数据库引擎? 引擎是什么? 引擎就是一个系统最核心的部分,比如汽车的发动机,人的心脏 数 ...
随机推荐
- 850. Dijkstra求最短路 II
给定一个n个点m条边的有向图,图中可能存在重边和自环,所有边权均为正值. 请你求出1号点到n号点的最短距离,如果无法从1号点走到n号点,则输出-1. 输入格式 第一行包含整数n和m. 接下来m行每行包 ...
- Linux命令alias - 设置命令的别名
用途说明设置命令的别名.在linux系统中如果命令太长又不符合用户的习惯,那么我们可以为它指定一个别名.虽然可以为命令建立“链接”解决长文件名的问题,但对于带命令行参数的命令,链接就无能为力了.而指定 ...
- C++走向远洋——27(项目三,时间类)
*/ * Copyright (c) 2016,烟台大学计算机与控制工程学院 * All rights reserved. * 文件名:time.cpp * 作者:常轩 * 微信公众号:Worldhe ...
- C++走向远洋——49(项目一2、复数类中的运算符重载、类的友元函数)
*/ * Copyright (c) 2016,烟台大学计算机与控制工程学院 * All rights reserved. * 文件名:text.cpp * 作者:常轩 * 微信公众号:Worldhe ...
- webpack环境配置2
1.webpack安装 Step 1: 首先安装Node.js, 在1中已经详细介绍了. Step2: 在Git或者cmd中输入下面这段代码, 通过全局先将webpack指令安装进电脑中npm ins ...
- Java入门教程二(语言基础)
常量与变量 常量值又称为字面常量,它是通过数据直接表示 常量 实型常量值 Java 的实型常量值主要有如下两种形式 十进制数形式:由数字和小数点组成,且必须有小数点,如 12.34.-98.0 科学记 ...
- 压力测试(八)-多节点JMeter分布式压测实战
1.Jmeter4.0分布式压测准备工作 简介:讲解Linux服务器上jmeter进行分布式压测的相关准备工作 1.压测注意事项 the firewalls on the systems are tu ...
- 从头解决PKIX path building failed
从头解决PKIX path building failed的问题 本篇涉及到PKIX path building failed的原因和解决办法(包括暂时解决和长效解决的方法),也包括HTTP和HTTP ...
- clientWidth offsetWidth等视窗尺寸
clientWidth和offsetWidth clientWidth 是一个只读属性,返回元素的内部宽度,该属性包括内边距,但不包括垂直滚动条(如果有).边框和外边距. offsetWidth 是一 ...
- 小程序自定义switch组件
如上图,小程序api中的switch组件只能自定义颜色,不能自定义宽高,所以就开始了自己写switch组件. 自定义组件样式 switch组件样式大致如图,样式思路:未选中时为一个长方形有圆角按钮,和 ...