InnoDB一棵B +树可以存放多少行数据?这个问题的简单回答是:约2千万。为什么是这么多呢?因为这是可以算出来的,要搞清楚这个问题,我们先从InnoDB索引数据结构,数据组织方式说起。
我们都知道计算机在存储数据的时候,有最小存储单元,这就好比我们今天进行现金的流通最小单位是一毛。在计算机中磁盘存储数据最小单元是旋转,一个体积的大小是512字节,而文件系统(例如XFS / EXT4)他的最小单元是块,一个块的大小是4k,而对于我们的InnoDB存储引擎也有自己的最小存储单元-页(页),一个页面的大小是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 +树
如果数据库只按这样的方式存储,那么如何查找数据就成为一个问题,因为我们不知道要查找的数据存在该页中,也不可能把所有的页遍历一遍,那样太慢了。所以人们想了一个办法,用B +树的方式组织这些数据。如下所示:
我们先将数据记录按主键进行排序,分别放置在不同的页面中(为了便于理解我们这里一个页面中只存放3条记录,实际情况可以放置很多),除了存放数据的页面以外,还有存放键值+指针的页面,则中页 号= 3的页面,该页面放置键值和指向数据页的指针,这样的页面由N个键值+指针组成。当然它也是排好序的。这样的数据组织形式,我们称为索引组织表。现在来看下,要查找一条数据,怎么查?
如select * from user where id=5;
这里id是主键,我们通过这棵棵B +树来查找,首先找到根页,你怎么知道用户表的根页在哪呢?其实每张表的根页位置在表空间文件中是固定的,即页 number = 3的页面(这点我们逐步获得进一步证明),找到根页面后通过二分查找法,定位到id = 5的数据应该在指针P5指向的页面中,那么进一步去页面 number = 5的页面中查找,同样通过二分查询法即可找到id = 5的记录:
5 | zhao2 | 27
现在我们清楚了InnoDB中主键索引B +树是如何组织数据,查询数据的,我们总结一下:
-
1,InnoDB存储引擎的最小存储单元是页,页可以用于存放数据也可以用于存放键值+指针,在B +树中叶子放置的数据,非叶子存放的存放键值+指针。
-
2,索引组织表通过非叶子癌细胞的二分查找法以及指针确定数据在该页中,且在去数据页中查找到需要的数据;
那么回到我们开始的问题,通常一棵B +树可以存放多少行数据?
一棵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 +树的高度?
在InnoDB的表空间文件中,约定的 页码为3 的代表主键索引的根页,而在根页偏移量为 64 的地方存放了该B +树的页级别如果页级别为1,树高为2,页。 级别为2,则树高为3。即B +树的高度=页面级+ 1;下面我们重置实际环境中尝试找到这个页面级别。
在实际操作之前,您可以通过InnoDB元数据表确认主键索引根页的页码为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表主键索引根页的页码改为3,而其他的二级索引页 号为4。不在此介绍。
下面我们对数据库表空间文件做想相关的解析:
因为主键索引B +树的根页在整个表空间文件中的第3个页开始,所以可以算出它在文件中的偏移量:16384*3=49152(16384为页大小)
。
另外根据《 InnoDB存储引擎》中的描述在根页的64偏移量位置前2个字节,保存了页面级别的值,因此我们想要的页面 级别的值在整个文件中的替换量为:16384*3+64=49152+64=49216
,前2个字节中。
接下来我们用hexdump工具,查看表空间文件指定重新量上的数据:
linetem表的页面级别为2,B +树高度为页面级别+ 1 = 3;
region表的页面级别为0,B +树高度为页面级别+ 1 = 1;
客户表的页面级别为2,B +树高度为页面级别+ 1 = 3;
这三张表的数据量如下:
总结:
lineitem表的数据行数为600多万,B +树高度为3,客户表数据行数只有15万,B +树高度也为3。都是3,换句话说这两个表通过索引查询效率并没有太大差异,因为都只需要做3次IO。那么如果有一张表行数是一千万,那么他的B +树高度依旧是3,查询效率仍然不会相差太大。
region表只有5行数据,当然他的B +树高度为1。
最后回顾一道面试题
某些道MySQL的面试题,为什么MySQL的索引要使用B +树而不是其他树形结构?某些B树?
现在这个问题的复杂版本可以参考本文;
他的简单版本回答是:
小结
本文从一个问题出发,逐步介绍InnoDB索引组织表的原理,查询方式,并结合现有知识,回答该问题,结合实践来证明。当然为了表述简单易懂,文中忽略了一些细枝末节,其中一个页面中不可能所有空间都用作存放数据,它会放置一些少量的其他变量替换page level
,等等index number
,另外还有页面的填充因子也导致一个页面无法全部保存数据。关于二级索引数据存储取方式可以参考MySQL相关书籍,他的要点是结合主键索引进行回表查询。
最后我把我收集的各大厂经典高频面试题和Java高级进阶、架构师视频教程送予大家。部分资料如下图所示:
获取地址: