InnoDB一棵B+树可以存放多少行数据?

一个问题?

InnoDB一棵B+树可以存放多少行数据?这个问题的简单回答是:约2千万。为什么是这么多呢?因为这是可以算出来的,要搞清楚这个问题,我们先从InnoDB索引数据结构、数据组织方式说起。

我们都知道计算机在存储数据的时候,有最小存储单元,这就好比我们今天进行现金的流通最小单位是一毛。在计算机中磁盘存储数据最小单元是扇区,一个扇区的大小是512字节,而文件系统(例如XFS/EXT4)他的最小单元是块,一个块的大小是4k,而对于我们的InnoDB存储引擎也有自己的最小储存单元——页(Page),一个页的大小是16K。

下面几张图可以帮你理解最小存储单元:

文件系统中一个文件大小只有1个字节,但不得不占磁盘上4KB的空间。

innodb的所有数据文件(后缀为ibd的文件),他的大小始终都是16384(16k)的整数倍。

磁盘扇区、文件系统、InnoDB存储引擎都有各自的最小存储单元。

在MySQL中我们的InnoDB页的大小默认是16k,当然也可以通过参数设置:

mysql> show variables like 'innodb_page_size';

+------------------+-------+

| Variable_name    | Value |

+------------------+-------+

| innodb_page_size | 16384 |

+------------------+-------+

1 row in set (0.00 sec)

数据表中的数据都是存储在页中的,所以一个页中能存储多少行数据呢?假设一行数据的大小是1k,那么一个页可以存放16行这样的数据。

如果数据库只按这样的方式存储,那么如何查找数据就成为一个问题,因为我们不知道要查找的数据存在哪个页中,也不可能把所有的页遍历一遍,那样太慢了。所以人们想了一个办法,用B+树的方式组织这些数据。如图所示:

我们先将数据记录按主键进行排序,分别存放在不同的页中(为了便于理解我们这里一个页中只存放3条记录,实际情况可以存放很多),除了存放数据的页以外,还有存放键值+指针的页,如图中page number=3的页,该页存放键值和指向数据页的指针,这样的页由N个键值+指针组成。当然它也是排好序的。这样的数据组织形式,我们称为索引组织表。现在来看下,要查找一条数据,怎么查?

如select * from user where id=5;

这里id是主键,我们通过这棵B+树来查找,首先找到根页,你怎么知道user表的根页在哪呢?其实每张表的根页位置在表空间文件中是固定的,即page number=3的页(这点我们下文还会进一步证明),找到根页后通过二分查找法,定位到id=5的数据应该在指针P5指向的页中,那么进一步去page number=5的页中查找,同样通过二分查询法即可找到id=5的记录:

5zhao227

现在我们清楚了InnoDB中主键索引B+树是如何组织数据、查询数据的,我们总结一下:

1、InnoDB存储引擎的最小存储单元是页,页可以用于存放数据也可以用于存放键值+指针,在B+树中叶子节点存放数据,非叶子节点存放键值+指针。

2、索引组织表通过非叶子节点的二分查找法以及指针确定数据在哪个页中,进而在去数据页中查找到需要的数据;

那么回到我们开始的问题,通常一棵B+树可以存放多少行数据?

这里我们先假设B+树高为2,即存在一个根节点和若干个叶子节点,那么这棵B+树的存放总记录数为:根节点指针数*单个叶子节点记录行数。

上文我们已经说明单个叶子节点(页)中的记录数=16K/1K=16。(这里假设一行记录的数据大小为1k,实际上现在很多互联网业务数据记录大小通常就是1K左右)。

那么现在我们需要计算出非叶子节点能存放多少指针,其实这也很好算,我们假设主键ID为bigint类型,长度为8字节,而指针大小在InnoDB源码中设置为6字节,这样一共14字节,我们一个页中能存放多少这样的单元,其实就代表有多少指针,即16384/14=1170。那么可以算出一棵高度为2的B+树,能存放1170*16=18720条这样的数据记录。

根据同样的原理我们可以算出一个高度为3的B+树可以存放:1170*1170*16=21902400条这样的记录。所以在InnoDB中B+树高度一般为1-3层,它就能满足千万级的数据存储。在查找数据时一次页的查找代表一次IO,所以通过主键索引查询通常只需要1-3次IO操作即可查找到数据。

怎么得到InnoDB主键索引B+树的高度?

上面我们通过推断得出B+树的高度通常是1-3,下面我们从另外一个侧面证明这个结论。在InnoDB的表空间文件中,约定page number为3的代表主键索引的根页,而在根页偏移量为64的地方存放了该B+树的page level。如果page level为1,树高为2,page level为2,则树高为3。即B+树的高度=page level+1;下面我们将从实际环境中尝试找到这个page level。

在实际操作之前,你可以通过InnoDB元数据表确认主键索引根页的page number为3,你也可以从《InnoDB存储引擎》这本书中得到确认。

SELECT
b.name, a.name, index_id, type, a.space, a.PAGE_NO
FROM
information_schema.INNODB_SYS_INDEXES a,
information_schema.INNODB_SYS_TABLES b
WHERE
a.table_id = b.table_id AND a.space <> 0;

执行结果:

可以看出数据库dbt3下的customer表、lineitem表主键索引根页的page number均为3,而其他的二级索引page number为4。关于二级索引与主键索引的区别请参考MySQL相关书籍,本文不在此介绍。

下面我们对数据库表空间文件做想相关的解析:

因为主键索引B+树的根页在整个表空间文件中的第3个页开始,所以可以算出它在文件中的偏移量:16384*3=49152(16384为页大小)。

另外根据《InnoDB存储引擎》中描述在根页的64偏移量位置前2个字节,保存了page level的值,因此我们想要的page level的值在整个文件中的偏移量为:16384*3+64=49152+64=49216,前2个字节中。

接下来我们用hexdump工具,查看表空间文件指定偏移量上的数据:

linetem表的page level为2,B+树高度为page level+1=3;

region表的page level为0,B+树高度为page level+1=1;

customer表的page level为2,B+树高度为page level+1=3;

这三张表的数据量如下:

总结:

lineitem表的数据行数为600多万,B+树高度为3,customer表数据行数只有15万,B+树高度也为3。可以看出尽管数据量差异较大,这两个表树的高度都是3,换句话说这两个表通过索引查询效率并没有太大差异,因为都只需要做3次IO。那么如果有一张表行数是一千万,那么他的B+树高度依旧是3,查询效率仍然不会相差太大。

region表只有5行数据,当然他的B+树高度为1。

最后回顾一道面试题

有一道MySQL的面试题,为什么MySQL的索引要使用B+树而不是其它树形结构?比如B树?

现在这个问题的复杂版本可以参考本文;

他的简单版本回答是:

因为B树不管叶子节点还是非叶子节点,都会保存数据,这样导致在非叶子节点中能保存的指针数量变少(有些资料也称为扇出),指针少的情况下要保存大量数据,只能增加树的高度,导致IO操作变多,查询性能变低;

小结

本文从一个问题出发,逐步介绍了InnoDB索引组织表的原理、查询方式,并结合已有知识,回答该问题,结合实践来证明。当然为了表述简单易懂,文中忽略了一些细枝末节,比如一个页中不可能所有空间都用于存放数据,它还会存放一些少量的其他字段比如page level,index number等等,另外还有页的填充因子也导致一个页不可能全部用于保存数据。关于二级索引数据存取方式可以参考MySQL相关书籍,他的要点是结合主键索引进行回表查询。

 

参考资料:

姜承尧 《MySQL技术内幕:InnoDB存储引擎》

姜承尧 http://www.innomysql.com/查看-innodb表中每个的索引高度/

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

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

相关文章

调整路由的AD值

实验&#xff1a;调整路由的AD值【实验名称】调整路由的AD值 (注意&#xff1a;PT有可能不支持distance 99 192.168.1.2 0.0.0.0这条命令&#xff0c;所以我们做实验的时候最好用小凡模拟器)【实验目的】通过调整路由的管理距离值&#xff0c;实现路由的管理和控制【实验背景】…

C#学习日志三(流程控制语句)

if条件语句&#xff1a;根据某个条件对成都的执行进行两路分支。语法&#xff1a;if(条件){语句块1}else{语句块2}*else部分并不是必须存在的。 switch...case条件选择语句&#xff1a;当分支条件很多时&#xff0c;使用。语法&#xff1a;switch(控制表达式){case 常量表达式1…

MySQL索引的一些问题

MySQL索引的一些问题 注意&#xff1a;本文基于MySQL的InnoDB引擎说明。 一、什么是最左前缀原则 对于该表&#xff0c;如果按照name字段来建立索引的话&#xff0c;采用B树结构&#xff0c;大概的索引如下&#xff1a; 如果要进行模糊查找&#xff0c;查找name 以“张"…

线程间操作无效: 从不是创建控件“Control Name'”的线程访问它问题的解决方案及原理分析...

最近&#xff0c;在做一个使用线程控制下载文件的小程序&#xff08;使用进度条控件显示下载进度&#xff09;时&#xff0c;遇到这样的问题&#xff0c; 错误显示&#xff1a; 未处理的“System.InvalidOperationException”类型的异常出现在 System.Windows.Forms.dll 中。 其…

大家狂欢吧,我的Google帐号悲剧了

大家狂欢吧&#xff0c;我的Google帐号悲剧了 今早开始&#xff0c;鄙人在Google Code上突然被强行杯具&#xff0c;出现如下图。 最无奈的是询问Google Code管理组后得到回复如下。 然后管理员就睡觉去了|||…… 继续等待中&#xff0c;能恢复的话我会发个解决心得以警后人&am…

Hive简单实际操作(二)

两个常用的交互命令 不用启动hive就可以执行的命令 1. bin/hive -e "select * from default.animal;" 可以直接显示指定命令后的内容 2. 创建一个sql文件 vi hivef.sql 在文件内部添加你希望执行的命令例如: select * from default.animal; bin/hive -f /opt/module/…

在数据库中, 不用max()/min()找出一个列中最大/最小值的记录

不用max()/min()找出c1列中最大/最小值的记录 // 找出c1列中&#xff0c;c1是最小值的那条记录&#xff0c;不能用min() select * from t1 where c1 < all(select c1 from t1); // 找出c1列中&#xff0c;c1是最大值的那条记录&#xff0c;不能用max() select * from t1 …

C眼看J - 初窥JAVA

最近一直在学习JAVA&#xff0c;出发点并不是像当初学C那样&#xff0c;而只是想把JAVA作为下学期参加比赛的工具&#xff0c;带着这种“浮躁”的心态&#xff0c;使得我总是在想“这个用看么&#xff1f;”、“那个用看么&#xff1f;”。 这是第一次在掌握了一门语言&#xf…

为Mac OS X添加用Firefox搜索服务

为Mac OS X添加用Firefox搜索服务 在Mac OS X上&#xff0c;Firefox这种移植过来的程序往往不提供服务&#xff0c;比如只有safari才能利用服务搜索&#xff0c;经过一番实验终于自己做了一个服务&#xff1a; 第一步&#xff0c;打开Automator 第二部&#xff0c;新建一个服务…

jmeter持续集成测试中mongodb版本问题

jmeter测试mongodb&#xff0c;采用的是JSR223 Sampler脚本连接数据库&#xff0c;其中连接数据库用到了SCRAM-SHA1认证机制&#xff0c;代码如下&#xff1a; MongoCredential credential MongoCredential.createScramSha1Credential("username", "databaseN…

速度之王 — LZ4压缩算法与其他算法的比较

LZ4 (Extremely Fast Compression algorithm) 项目&#xff1a;http://code.google.com/p/lz4/ 作者&#xff1a;Yann Collet 本文作者&#xff1a;zhangskd csdn blog 简介 LZ4 is a very fast lossless compression algorithm, providing compression speed at 400MB/…

我用过的DOS命令

我用过的DOS命令 1、进入当前目录的子目录&#xff1a;cd xxx 2、放回到当前目录的上一子目录&#xff1a;cd.. 3、返回到根目录&#xff1a;cd/ 5、进入到指定目录&#xff1a;先键入指定目录的本目录如D盘&#xff1a;D: 然后回车键入cd xxx\xxx\xxx\xxx\xxx 如图&#xf…

XAMPP 无法启动解决

当安装完XAMPP后&#xff0c;在XAMPP Control Panel 里面无法启动apache,网上找了很多都是关于apache端口冲突问题&#xff0c;但我的不是&#xff0c;解决步骤如下&#xff1a;1&#xff1a;进入apache目录 conf/httpd.conf 改变监听端口&#xff1a;Listen 8080--启动任然失败…

lz4压缩算法--速度之王

简介 lz4是目前综合来看效率最高的压缩算法&#xff0c;更加侧重压缩解压速度&#xff0c;压缩比并不是第一。在当前的安卓和苹果操作系统中&#xff0c;内存压缩技术就使用的是lz4算法&#xff0c;及时压缩手机内存以带来更多的内存空间。本质上是时间换空间。 压缩原理 lz…

Mac OS defaults命令(Access the Mac OS user defaults system)

defaults命令: 用于访问和修改Mac 上一些系统的默认设置&#xff08;Access the Mac OS user defaults system&#xff09; 仅修改当前用户设置&#xff0c;所以不应加sudo。 用法&#xff1a; 读取所有默认配置&#xff1a; defaults read 读取某项配置&#xff1a; defaults …

推荐12个绚丽的CSS3图片悬停效果

CSS3为我们开发提供了很多特效&#xff0c;如鼠标悬停&#xff0c;线性渐变&#xff0c;在web开发早期常用的是鼠标悬停&#xff0c;他是网页制作的常用特效之一&#xff0c;早期使用Flash可以制作非常炫的效果&#xff0c;后来慢慢演变成现在的是图片悬停效果&#xff0c;现在…

SAP和ABAP内存的区别

1、读取哈使用方法不同SAP内存使用SET/GET parameters方法&#xff1b;ABAP内存使用 EXPORT 和 IMPORT 方法&#xff1b;2、共享范围不同SAP内存可以被所有的主session访问&#xff0c;内存数据可以同一个session中不同程序之间&#xff0c;或者不同session之间&#xff1b;AB…

RocksDB事务实现TransactionDB分析

基本概念 1. LSN (log sequence number) RocksDB中的每一条记录(KeyValue)都有一个LogSequenceNumber(后面统称lsn)&#xff0c;从最初的0开始&#xff0c;每次写入加1。该值为逻辑量&#xff0c;区别于InnoDB的lsn为redo log物理写入字节量。 我有几张阿里云幸运券分享给你&…

Leetcode 771. Jewels and Stones

class Solution(object):def numJewelsInStones(self, J, S):""":type J: str:type S: str:rtype: int"""countcollections.Counter(S)ans0for j in J:anscount.get(j,0)return ans 转载于:https://www.cnblogs.com/zywscq/p/10562590.html

iptables规则的增删改查

1、查看 iptables -nvL --line-number -L 查看当前表的所有规则&#xff0c;默认查看的是filter表&#xff0c;如果要查看NAT表&#xff0c;可以加上-t NAT参数 -n 不对ip地址进行反查&#xff0c;加上这个参数显示速度会快很多 -v 输出详细信息&#xff0c;包含通过该规则的数…