是什么影响了MySQL索引B+树的高度?
hello,大家好,我是张张,「架构精进之路」公号作者。
提到MySQL,想必大多后端同学都不会陌生,提到B+树,想必还是有很大部分都知道InnoDB引擎的索引实现,利用了B+树的数据结构。
那InnoDB 的一棵B+树可以存放多少行数据?它又有多高呢?
到底是哪些因素会对此造成影响呢,今天我们就来展开聊一下。
1 InnoDB引擎数据存储
在计算机中,磁盘存储数据最小单元是扇区,一个扇区的大小是512字节,而文件系统(例如XFS/EXT4)的最小单元是块,一个块的大小是4k,在InnoDB存储引擎中,也有页(Page)的概念,默认每个页的大小为16K,也就是每次读取数据时都是读取4*4k的大小!
在MySQL中,InnoDB页的大小默认是16k,当然也可以通过参数设置:
2 InnoDB引擎数据操作
接下来,为了让大家能更好地理解数据存储逻辑,我们来进行一个数据操作实例进行讲解。
假设我们现在有一个用户表,我们往里面写入数据。
这里需要注意的一点是,在某个页内插入新数据行时,为了减少数据的移动,通常是插入到当前行的后面或者是已删除行留下来的空间,所以在某一个页内的数据并不是完全有序的。但是为了为了数据访问顺序性,在每个记录中都有一个指向下一条记录的指针,因此构成了一条单向有序链表。
当数据还比较少时,一个页就能容下,所以只有一个根结点,主键和数据也都是保存在根结点(左边的数字代表主键,右边姓名、性别代表具体的记录数据)。
假设我们写入10条数据之后,Page1满了,再写入新的数据会怎么存放呢?
有个叫“无名氏”的朋友来了,但是Page1已经放不下数据了,这时候就需要进行页分裂,产生一个新的Page页。
在InnoDB 中的页分裂流程是怎么样的呢?
1、产生新的Page2,然后将Page1的内容复制到Page2。
2、产生新的Page3,“无名氏”的数据放入Page3。
3、原来的Page1依然作为根结点,但是变成了一个不存放数据只存放索引的页,并且有两个子结点Page2、Page3。
看到这里,大家可能会有两个问题:
Q1:为什么要复制Page1为Page2呢?直接创建一个新的页作为根结点,这样不就少了一次复制的开销么?
A:如果是新创建根结点,那根结点存储的物理地址可能经常会变,不利于查找。并且在InnoDB中根结点是会预读到内存中的,所以结点的物理地址固定会比较好。
Q2:原来Page1有10条数据,在插入第11条数据的时候进行页裂变,根据对B-Tree、B+Tree特性的了解,那这至少是一颗11阶的树,裂变之后每个结点的元素至少为11/2=5个,那是不是应该页裂变之后主键1-5的数据还是在原来的页,主键6-11的数据会放到新的页,根结点存放主键6呢?
A:如果是这样的话,新的页空间利用率只有50%,并且会导致更为频繁的页分裂。所以InnoDB对这一点做了优化,新的数据放入新创建的页,不移动原有页面的任何记录。
随着数据的不断写入,这棵树也逐渐枝繁叶茂,如下图:
每次新增数据,都是将一个页写满,然后新创建一个页继续写,这里其实是有个隐含条件的,那就是主键自增!主键自增写入时新插入的数据不会影响到原有页,插入效率高!且页的利用率高!但是如果主键是无序的或者随机的,那每次的插入可能会导致原有页频繁的分裂,影响插入效率!降低页的利用率!这也是为什么在InnoDB中建议设置主键自增的原因!
这棵树的非叶子结点上存的都是主键,那如果一个表没有主键会怎么样?在InnoDB中,如果一个表没有主键,那默认会找建了唯一索引的列,如果也没有,则会生成一个隐形的字段作为主键!
有数据插入那就有删除,如果这个用户表频繁的插入和删除,那会导致数据页产生碎片,页的空间利用率低,还会导致树变的“虚高”,降低查询效率!这可以通过索引重建来消除碎片提高查询效率!
3 InnoDB引擎索引高度
回到开篇的问题:InnoDB 的一棵B+树可以存放多少行数据?它又有多高呢?
这个需要区分叶子节点和非叶子节点:
非叶子节点
InnoDB 存储引擎默认一个数据页大小为16kb,非叶子节点存放(key,pointer),假设主键ID为bigint类型,长度为8字节,而指针大小在InnoDB源码中设置为6字节,这样一共14字节,即非叶子节点能存放 16kb/14 左右的key,pointer。
叶子节点
单个叶子节点(页)中的记录数 = 16K/1K = 16。
这里我们假设一行记录的数据大小为1k左右
总结一下:
如果B+树高度为2的话,那么这棵B+树的存放总记录数为:根节点指针数*单个叶子节点记录行数 = 16kb/14 * 16 大约 1.8w+ 数据。
如果B+树高度为3的话,那么这棵B+树的存放总记录数为:根节点指针数*单个叶子节点记录行数 = 16kb/14 * 16kb/14 * 16 大约2kw+数据。
因此常见 InnoDB存储引擎B+树的高度基本为2-3。
希望今天的讲解对大家有所帮助,谢谢!
Thanks for reading!
关注公众号,免费领学习资料
如果您觉得还不错,欢迎关注和转发~
本文分享自微信公众号 - 架构精进之路(jiagou_jingjin)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
最新出炉!开源 API 网关的性能对比:APISIX 3.0 和 Kong 3.0
背景 云原生时代下,企业逐渐向云上迁移,越来越多的应用和服务都在进行容器化改造,服务之间的流量也开始爆发性的增长。为了能高效地管理这些规模庞大的 API,API 网关开始在技术领域大展身手。 用户除了需要 API 网关提供请求代理、熔断限流、审计监控等常规能力外,更多开始关注云原生兼容性、支撑场景的多样性,以及更好的性能及稳定性。在这样的背景下,以 Apache APISIX 和 Kong 等为代表的云原生 API 网关项目得到了越来越多开发者的青睐。 Apache APISIX 是一个云原生、高性能、可扩展的 API 网关,由深圳支流科技捐赠给 Apache 基金会,并于 2020 年 7 月从 Apache 孵化器毕业, 成为 Apache 软件基金会顶级项目。APISIX 基于 NGINX 和 etcd 来实现,和传统 API 网关相比,APISIX 具备动态路由和插件热加载,特别适合云原生架构下的 API 管理。 Kong 也是一款高可用、易扩展的开源 API 网关项目。通过提供代理、路由、负载均衡、身份验证等功能,在微服务与传统 API 领域提供网关层面的支持。 2022 年...
- 下一篇
一种可能是目前最快的从ORACLE同步数据到openGauss的方式
一、前言 目前openGauss支持oracle中的绝大部分数据类型,基本上不用考虑类型转换的问题。所以从oracle到MogDB(openGauss)的数据同步无非就是从oracle里查出来再插入到MogDB(openGauss)中,只是查出来的结果是存成文件还是放内存而已。 用文件的形式,oracle端有sqluldr2可以快速导出,MogDB(openGauss)端可以用copy命令快速载入; 如果是放内存,则需要一定量的代码开发,而且目前通用的数据同步工具在导入时,大多用的是逐行insert命令,效率很低。 所以,是否存在一种可能,只利用现有的工具,就能实现数据的高速同步且不需要存文件? 在说这个方案前,先简单说明一下这个方案的几个知识点。 二、知识点 1.sqluldr2 sqluldr2是楼方鑫针对Oracle数据库开发的数据快速导出工具,应该绝大多数oracle用户都用过,因为它依旧是目前从oracle中导出文本数据最快的方式了,速度远超oracle官方的sqlplus spool导出。但是大多数人基本上都只使用其导出文件的功能,而不知道这个工具还可以导出标准输出(所谓标...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS7,CentOS8安装Elasticsearch6.8.6
- MySQL8.0.19开启GTID主从同步CentOS8
- 设置Eclipse缩进为4个空格,增强代码规范
- CentOS8安装Docker,最新的服务器搭配容器使用
- Hadoop3单机部署,实现最简伪集群
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- SpringBoot2整合Redis,开启缓存,提高访问速度
- SpringBoot2全家桶,快速入门学习开发网站教程
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- Linux系统CentOS6、CentOS7手动修改IP地址