前言

作为DBA了解InnoDB的页组织方式是最基础的,在实际工作中,免不了会评估SQL会消耗多少IO,怎么评估呢?
作为InnoDB表和树的高度或者深度有关系。

查看树的高度?

之前研究了半天:
https://www.percona.com/blog/2009/04/28/the_depth_of_a_b_tree/
http://code.openark.org/blog/mysql/the-depth-of-an-index-primer
根据

Scholmi notes that there are two main features determining the depth of a B-tree (or B+-tree):The number of rows in the database. We’ll call that N.
The size of the indexed key. Let’s call B the number of key that fit in a B-tree node. (Sometimes B is used to refer to the node size itself, rather than the number of keys it holds, but I hope my choice will make sense directly.)
Given these quantities, the depth of a B-tree is logB N, give or take a little. That’s just (log N)/log B. Now we can rephrase Scholmi’s point as noting that small keys means a bigger B, which reduces (log N)/log B. If we cut the key size in half, then the depth of the B-tree goes from (log N)/log B to (log N)/log 2B (twice as many keys fit in the tree nodes), and that’s just (log N)/(1+log B).Let’s put some numbers in there. Say you have a billion rows, and you can currently fit 64 keys in a node. Then the depth of the tree is (log 109)/ log 64 ≈ 30/6 = 5. Now you rebuild the tree with keys half the size and you get log 109 / log 128 ≈ 30/7 = 4.3. Assuming the top 3 levels of the tree are in memory, then you go from 2 disk seeks on average to 1.3 disk seeks on average, for a 35% speedup.

里面算的不对,人肉算也没达到这个高度。也可能是我没有理解作者的意思,没有用对公式。那么根据结合前面的Innodb页结构,如何正确的获取数的高度呢?继续拿这个表举例子:

mysql> show create table sbtest1\G
*************************** 1. row ***************************Table: sbtest1
Create Table: CREATE TABLE `sbtest1` (`id` int(11) NOT NULL AUTO_INCREMENT,`gmt_create` datetime NOT NULL,`gmt_modified` datetime NOT NULL,`k` int(11) NOT NULL DEFAULT '0',`c` varchar(500) NOT NULL DEFAULT '',`pad` char(60) NOT NULL DEFAULT '',`is_used` int(11) DEFAULT NULL,PRIMARY KEY (`id`),KEY `k_1` (`k`),KEY `idx_is_used` (`is_used`),KEY `idx_gmt_create` (`gmt_create`)
) ENGINE=InnoDB AUTO_INCREMENT=69313841 DEFAULT CHARSET=latin1
1 row in set (0.00 sec)

方法一

[root@localhost mysql]# innodb_space -f test/sbtest1.ibd space-index-pages-summary | head -n 10
page        index   level   data    free    records
3           74      2       5166    10904   369
4           75      2       408     15834   24
5           76      2       486     15756   27
6           77      2       486     15756   27
7           74      0       15028   1192    68
8           74      0       15028   1192    68
9           74      1       14700   1030    1050
10          74      0       15028   1192    68
11          74      0       15028   1192    68

page_level是2,所以这个树高度是page_level+1=3;

方法二

mysql> show global variables like "%innodb_page%";
+----------------------+-------+
| Variable_name        | Value |
+----------------------+-------+
| innodb_page_cleaners | 1     |
| innodb_page_size     | 16384 |
+----------------------+-------+
2 rows in set (0.00 sec)mysql> show table status like 'sbtest1'\G
*************************** 1. row ***************************Name: sbtest1Engine: InnoDBVersion: 10Row_format: DynamicRows: 25926320Avg_row_length: 279Data_length: 7254032384
Max_data_length: 0Index_length: 1293697024Data_free: 3145728Auto_increment: 69313841Create_time: 2018-01-19 14:53:11Update_time: NULLCheck_time: NULLCollation: latin1_swedish_ciChecksum: NULLCreate_options:Comment:
1 row in set (0.00 sec)
mysql> desc sbtest1;
+--------------+--------------+------+-----+---------+----------------+
| Field        | Type         | Null | Key | Default | Extra          |
+--------------+--------------+------+-----+---------+----------------+
| id           | int(11)      | NO   | PRI | NULL    | auto_increment |
| gmt_create   | datetime     | NO   | MUL | NULL    |                |
| gmt_modified | datetime     | NO   |     | NULL    |                |
| k            | int(11)      | NO   | MUL | 0       |                |
| c            | varchar(500) | NO   |     |         |                |
| pad          | char(60)     | NO   |     |         |                |
| is_used      | int(11)      | YES  | MUL | NULL    |                |
+--------------+--------------+------+-----+---------+----------------+

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

这里我们先假设B+树高为2,即存在一个根节点和若干个叶子节点,那么这棵B+树的存放总记录数为:根节点指针数*单个叶子节点记录行数。上文我们已经说明单个叶子节点(页)中的记录数=16K/279=58。(我们从上面可以看到每行记录的数据平均大小为279个字节)。那么现在我们需要计算出非叶子节点能存放多少指针,其实这也很好算,表中的主键ID为int类型,长度为4字节,而指针大小在InnoDB源码中设置为6字节,这样一共10字节,我们一个页中能存放多少这样的单元,其实就代表有多少指针,即16384/10=1638。那么可以算出一棵高度为2的B+树,能存放1638*58=95004条这样的数据记录。根据同样的原理我们可以算出一个高度为3的B+树可以存放:1638*1638*58=155616552条这样的记录。
高度为4的B+树可以存放:
1638*1638*1638*58=254899912176条这样的记录。
而在实际应用中,大部分是以bigint作为主键的,主键ID为bigint类型,长度为8字节,而指针大小在InnoDB源码中设置为6字节,这样一共14字节,我们一个页中能存放多少这样的单元,其实就代表有多少指针,即16384/14=1170。
根据同样的原理我们可以算出一个高度为2,3,4的B+树能够存放的记录数。
mysql> select count(*) from sbtest1;
+----------+
| count(*) |
+----------+
| 26313272 |
+----------+
1 row in set (4.23 sec)

我们的表一共是3层。

方法三

mysql> 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 and b.name='test/sbtest1';
+--------------+----------------+----------+------+-------+---------+
| name         | name           | index_id | type | space | PAGE_NO |
+--------------+----------------+----------+------+-------+---------+
| test/sbtest1 | PRIMARY        |       74 |    3 |    45 |       3 |
| test/sbtest1 | k_1            |       75 |    0 |    45 |       4 |
| test/sbtest1 | idx_is_used    |       76 |    0 |    45 |       5 |
| test/sbtest1 | idx_gmt_create |       77 |    0 |    45 |       6 |
+--------------+----------------+----------+------+-------+---------+
4 rows in set (0.00 sec)

primary key的高度是3,其他索引的可以看上表。

方法四

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

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

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

[root@localhost test]# hexdump -s 49216 -n 10 sbtest1.ibd
000c040 0200 0000 0000 0000 4a00
000c04a

page_level是2,B+树高度为page level+1=3

如果通过二级索引查找记录最多需要花费多少次IO呢?

从上面的图中可以看出需要花费:
从二级索引找到主键+主键找到记录,比如二级索引有3层,聚簇索引有3层,那么最多花费的IO次数是:3+3=6

参考

姜承尧 《MySQL技术内幕:InnoDB存储引擎》
姜承尧 http://www.innomysql.com/查看-innodb表中每个的索引高度/
https://blog.jcole.us/2013/01/10/btree-index-structures-in-innodb/?spm=a2c4e.11153940.0.0.77d994fcZH1hIv

如何获取InnoDB树的高度相关推荐

  1. 二叉树(C++):创建,前中后序遍历(递归+非递归),获取叶子节点个数,获取树的高度

    文章目录 前言 创建二叉树 先序遍历 中序遍历 后序遍历 获取叶子节点个数 获取树的高度 测试代码 前言 现有如下二叉树: 关于二叉树的相关操作,我们能够发现二叉树从根节点到子节点,以及每个中间节点基 ...

  2. MySQL之InnoDB主键索引的B+树的高度计算

    文章目录 MySQL之InnoDB主键索引的B+树的高度计算 1.高度为2和3.主键bigint类型.一行记录数据大小1k MySQL之InnoDB主键索引的B+树的高度计算 1.高度为2和3.主键b ...

  3. 获取二叉树的所有叶子节点、获取全树深度与左右子树深度求解:递归

    树的左右子树深度 全树的深度 //因为我们现在能够不重复,不遗漏获取每一个节点,只有前中后序3种方法,所以,我们首先先写出 //先序递归获取树的深度 int hight = 0;//它可以在多个函数之 ...

  4. 多叉树的构建和树的高度的计算

    题目描述 现在有一棵合法的二叉树,树的节点都是用数字表示,现在给定这棵树上所有的父子关系,求这棵树的高度 输入描述: 输入的第一行表示节点的个数n(1 ≤ n ≤ 1000,节点的编号为0到n-1)组 ...

  5. btree高度mysql_如何知道btree树的高度

    来自网络,总结到这里: 当我想看btree树高度的时候,筛选出来这篇文章"为什么 B-tree 在不同著作中度的定义有一定差别?",知道了高度的算法是这个公式:但是里面又提高t和出 ...

  6. 是什么影响了MySQL索引B+树的高度?

    hello,大家好,我是张张,「架构精进之路」公号作者. 提到MySQL,想必大多后端同学都不会陌生,提到B+树,想必还是有很大部分都知道InnoDB引擎的索引实现,利用了B+树的数据结构. 那Inn ...

  7. 为什么生产环境中B+树的高度总是3-4层?

    答案:一个高度为 3 的 B+ 树大概可以存放 1170 × 1170 × 16 = 21902400 行数据,已经是千万级别的数据量了. 大多数项目也就是这个量级的数据了吧?再大的--也该拆分拆分了 ...

  8. 树的高度(小米2017秋招真题)

    现在有一棵合法的二叉树,树的节点都是用数字表示,现在给定这棵树上所有的父子关系,求这棵树的高度. 输入 输入的第一行表示节点的个数n(1<=n<=1000,节点的编号为0到n-1)组成, ...

  9. Swift4 - 动态计算UITableView中tableHeaderView的高度 - 获取子控件高度和宽度

    核心 : /// 获取 子控件高度func sizeHeaderToFit(view:UIView) {view.setNeedsLayout()view.layoutIfNeeded()let wi ...

最新文章

  1. 全国信息化计算机应用技术水平教育考试,全国信息化计算机应用技术水平教育考试试卷.doc...
  2. mysql.net开发驱动_mysql数据库.net开发驱动(mysql connector net )
  3. phpquery类php,一个基于phpQuery的php通用采集类分享
  4. java usb 无驱打印_Windows Usb 无驱动打印
  5. merge intervals(合并间隔)
  6. C语言带参宏定义和函数的区别
  7. js与c语言互相调用,Objc与JS间相互调用
  8. Cisco交换机路由器口令恢复
  9. 内存占用小的手机输入法_华为手机输入法中6个超实用的小技巧,你没用过就太可惜了!...
  10. 在 Windows 下远程桌面连接 Linux - XManager 篇
  11. 【李宏毅2020 ML/DL】P4 Basic Concept
  12. 云计算时代的数据库研究
  13. C 语言调用python 脚本函数
  14. VirtualBox6.1安装及使用教程
  15. 国赛高教杯使用python/matlab必会基础数学建模-数据处理模块(课程4)
  16. isEmpty()的坑
  17. mysql添加多个字段删除多个字段
  18. 大连市计算机软件产业,大连市软件产业高技能型人才培养与市场需求拟合度研究.doc...
  19. 猫本来就长这样,为什么叫我重画?
  20. 孪生网络图像相似度_论文浅尝 | 使用孪生BERT网络生成句子的嵌入表示

热门文章

  1. 你应该知道的 8 个Java 的领军人物
  2. 项目管理: 软件质量的可靠保证
  3. IT兄弟连 JavaWeb教程 文件下载技术
  4. ILSpy反编译工具的使用
  5. canvas图形处理和进阶用法
  6. 使用Travis-CI 与 Github Webhook自动部署你的页面
  7. 驱动12.移植dm9000驱动程序
  8. 输出1-10之间的偶数,并统计奇数的个数
  9. Adapter 如果客户需要使用某个类的服务,而这项服务是这个类用一个不同的接口提供的,那么,可以使用适配器模式为客户提供一个期望的接口...
  10. selenium环境搭建,浏览器驱动安装