磁盘存储数据的最小单元是扇区,一般一个扇区的大小是 512 字节,计算机操作系统为了更好地与磁盘通信,抽象出了簇或块的概念,一般大小为 4K。InnoDB 存储引擎也有自己的最小存储单元——页,默认页的大小为 16K,我们可以通过以下命令查看该值。
1 2 3 4 5 6
   | mysql> show variables like 'innodb_page_size'; +------------------+-------+ | Variable_name    | Value | +------------------+-------+ | innodb_page_size | 16384 | +------------------+-------+
   | 
 
InnoDB 中所有的表空间文件的大小都是 16K 的整数倍。在 InnoDB 的表空间文件(这里说的不是共享的表空间,而是每个表独立的表空间)中,页号为 3 的页是主键索引的根页,其它索引的页号为 4,该值可以通过以下命令查看。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
   | 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; +--------------------------------------------+---------------------------------------+----------+------+-------+---------+ | name                                       | name                                  | index_id | type | space | PAGE_NO | +--------------------------------------------+---------------------------------------+----------+------+-------+---------+ | demo/t_certification                       | PRIMARY                               |       79 |    3 |    48 |       3 | | demo/t_certification                       | t_certification_idx                   |       80 |    0 |    48 |       4 | | demo/t_company_abstract                    | PRIMARY                               |       72 |    3 |    43 |       3 | | demo/t_company_location                    | PRIMARY                               |       73 |    3 |    44 |       3 | | demo/t_copyrights_copyright                | PRIMARY                               |       81 |    3 |    49 |       3 | +--------------------------------------------+---------------------------------------+----------+------+-------+---------+
   | 
 
因为主键索引 B+树的根页在整个表空间文件中的第 3 页,所以我们可以计算出它在文件中的偏移量为:16384 byte * 3 = 49152 byte。根据《MySQL技术内幕:InnoDB存储引擎》的描述,MySQL 的数据页中,前 38 个字节为 File Header,紧接着的 56 个字节为 Page Header。在 Page Header 中,第 27 到 28 两个字节为 PAGE_LEVEL,代表当前页在索引树中的位置,值 0x00 表示当前页为叶子节点,叶子节点总是在第 0 层。由此我们可以推断出,只要拿到主键索引的根页中 PAGE_LEVEL 的值就可以计算整棵树的高度。具体来说就是根页中第 65 到 66 这两个字节的值。我们首先计算整体的偏移量为:49152 byte + 64 byte = 49216 byte。接下来我们使用 hexdump 查看表空间文件指定偏移量上的数据。
1 2 3 4 5 6 7
   |  > hexdump -C -s 49216 -n 10 ../data/demo/t_eci_company.ibd 0000c040  00 03 00 00 00 00 00 00  00 29 0000c04a > hexdump -C -s 49216 -n 10 ../data/demo/t_eci_count.ibd 0000c040  00 02 00 00 00 00 00 00  00 4a 0000c04a
 
  | 
 
可以看到例子中表 t_eci_company 的 PAGE_LEVEL 为 3,t_eci_count 的 PAGE_LEVEL 为 2,所以它们的树高分别为 4 和 3。
除了手工分析,我们还可以借助于工具。innodb_ruby 就是一款可以分析 innoDB 文件的工具,使用 ruby 编写。在安装完 ruby 后,使用 ruby 的包管理工具直接下载该工具,然后我们简单使用一下,可以看到它的结果与我们的分析是一致的。
1 2 3 4 5 6 7 8 9 10 11
   | > innodb_space -f ../data/demo/t_eci_company.ibd space-index-pages-summary | head -n 10 page        index   level   data    free    records 3           41      3       92      16160   2 4           41      0       14339   1909    16 5           41      0       15117   1131    16 6           41      0       8617    7633    9 7           41      0       12208   4042    12 8           41      0       9232    7018    9 9           41      0       12755   3495    13 10          41      0       7846    8404    8 11          41      0       14079   2169    15
   | 
 
参考
MySQL Internals Manual: High-Altitude View