我们知道InnoDB表是索引组织表,分为聚集索引和二级索引,但不管哪种索引,其结构都是B+树结构。由于InnoDB B+树结构高扇区特性,所以每个索引高度基本在3-4层之间,如果数据量比较大,页比较小,如4K,那么高度也是可能破4的。问题来了,以上说法都是我们根据理论知识计算得出的结果,那么在InnoDB中如何查看每个索引的高度呢?InnoDB也没有提供相应的视图进行查看。然,只要明白InnoDB索引的内部构造,就能迅速得出索引的高度。
姜总这篇文章给出了一个方法,如下图InnoDB索引的内部构造:
InnoDB是索引组织表,每个页都包含一个PAGE_LEVEL的信息(见上图右半部分),用于表示当前页所在索引中的高度。默认叶子节点的高度为0,那么root页的PAGE_LEVEL + 1就是这棵索引的高度。接下去的问题就是怎样得到一张表所有索引的Root页所在的位置呢?其实InnoDB中Root页默认就在3号页,且Root页的位置通常是不会更改的,为什么是3号页呢?因为0,1,2号的Page被类型为“FSP_HDR”、“IBUF_BITMAP”、“INODE”分别占用了,从3号Page开始才是存放数据和索引的页(Index)。
其实官方提供了内部视图来查看每个索引的Root页,通过下面的SQL语句可以查出某这表对应索引的Root页:
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;
1
2
3
4
5
6
7
8
9
10
SELECTb.name,
a.name,
index_id,
TYPE,
a.space,
a.PAGE_NO
FROMinformation_schema.INNODB_SYS_INDEXESa,
information_schema.INNODB_SYS_TABLESb
WHEREa.table_id=b.table_id
ANDa.space<>0;
运行上述的SQL语句应该可以得到类似如下的结果(space等于0的为系统表):
+----------------+---------+----------+------+-------+---------+
| name | name | index_id | TYPE | space | PAGE_NO |
+----------------+---------+----------+------+-------+---------+
| sbtest/sbtest1 | PRIMARY | 319 | 3 | 138 | 3 |
| sbtest/sbtest1 | idx_k_c | 320 | 0 | 138 | 4 |
+----------------+---------+----------+------+-------+---------+
2 rows in set (0.00 sec)
1
2
3
4
5
6
7
+----------------+---------+----------+------+-------+---------+
|name|name|index_id|TYPE|space|PAGE_NO|
+----------------+---------+----------+------+-------+---------+
|sbtest/sbtest1|PRIMARY|319|3|138|3|
|sbtest/sbtest1|idx_k_c|320|0|138|4|
+----------------+---------+----------+------+-------+---------+
2rowsinset(0.00sec)
其中(SPACE,PAGE_NO)就是索引的Root页。SPACE对应每个表空间ID号,PAGE_NO对应每个页的ID号。
有了这些信息就可以方便的定位啦,因为PAGE_LEVEL在每个页的偏移量64位置初,占用两个字节,通过hexdump这样的工具就可以快速定位到所需要的树高度信息:
$ hexdump -s 49216 -n 02 /var/lib/mysql/sbtest/sbtest1.ibd
000c040 0200
1
2
$hexdump-s49216-n02/var/lib/mysql/sbtest/sbtest1.ibd
000c0400200
查看sbtest1表,49216表示的是3*16384+64(这里innodb_page_size设置为了16384,如果是8192就是3*8192),即第3个页偏移量64位置开始读取2个字节,这里PAGE_LEVEL为00 02,那么索引的高度就为3。
最后,如果我们不知道索引内部结构,那么就可以使用工具来协助,innodb_ruby是一个非常好的各种维度查看索引及页结构的工具。所以,同样使用innodb_ruby查出每个表的索引信息。
$ innodb_space -s ibdata1 -T sbtest/sbtest1 space-indexes
id name root fseg used allocated fill_factor
319 PRIMARY 3 internal 36 96 37.50%
319 PRIMARY 3 leaf 41094 41120 99.94%
320 idx_k_c 4 internal 521 608 85.69%
320 idx_k_c 4 leaf 36159 41376 87.39%
1
2
3
4
5
6
$innodb_space-sibdata1-Tsbtest/sbtest1space-indexes
idnamerootfsegusedallocatedfill_factor
319PRIMARY3internal369637.50%
319PRIMARY3leaf410944112099.94%
320idx_k_c4internal52160885.69%
320idx_k_c4leaf361594137687.39%
id:表示此索引的ID。
name:索引的名称,PRIMARY代表的就是聚集索引,因为InnoDB表是聚集索引组织表,行记录就是聚集索引;idx_c就是辅助索引的名称。
root:索引中根节点的page号,可以看出聚集索引的Root节点是第3号page(前0、1、2号Page已经被使用),辅助索引的根节点是第4个page。
fseg:page的说明,internal表示非叶子节点或属于根节点,leaf表示叶子节点(也就是数据页)。
used:索引使用了多少个page,可以看出聚集索引的非叶子节点使用了36个page,叶子节点使用了41094个page;辅助索引的非叶子节点使用了521个页,叶子节点使用了36159个page。
allocated:索引分配了多少个page,可以看出聚集索引的非叶子节点分配了96个page,叶子节点分配了41120个page;辅助索引的非叶子节点分配了608个页,叶子节点分配了41376个page。
fill_factor:索引的填充度,used/allocated表示填充度,也就是实际使用的大小百分比。
同样,现在我们知道了Root节点页后,就可以使用innodb_ruby的另外一个功能,打印页结构信息,需要了解InnoDB页结构。
$ innodb_space -s ibdata1 -T sbtest/sbtest1 -p 3 page-dump
fil header:
{:checksum=>900405545,
:offset=>3,
:prev=>nil,
:next=>nil,
:lsn=>20030130167,
:type=>:INDEX,
:flush_lsn=>0,
:space_id=>138}
fil trailer:
{:checksum=>3300141601, :lsn_low32=>2850260983}
page header:
{:n_dir_slots=>9,
:heap_top=>575,
:garbage_offset=>0,
:garbage_size=>0,
:last_insert_offset=>567,
:direction=>:right,
:n_direction=>34,
:n_recs=>35,
:max_trx_id=>0,
:level=>2,
:index_id=>319,
:n_heap=>37,
:format=>:compact}
fseg header:
{:leaf=>
<:inode space="
:internal=>
<:inode space="
sizes:
header 120
trailer 8
directory 18
free 15783
used 601
record 455
per record 13.00
page directory:
[99, 164, 216, 268, 320, 372, 424, 476, 112]
system records:
.............................
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
$innodb_space-sibdata1-Tsbtest/sbtest1-p3page-dump
filheader:
{:checksum=>900405545,
:offset=>3,
:prev=>nil,
:next=>nil,
:lsn=>20030130167,
:type=>:INDEX,
:flush_lsn=>0,
:space_id=>138}
filtrailer:
{:checksum=>3300141601,:lsn_low32=>2850260983}
pageheader:
{:n_dir_slots=>9,
:heap_top=>575,
:garbage_offset=>0,
:garbage_size=>0,
:last_insert_offset=>567,
:direction=>:right,
:n_direction=>34,
:n_recs=>35,
:max_trx_id=>0,
:level=>2,
:index_id=>319,
:n_heap=>37,
:format=>:compact}
fsegheader:
{:leaf=>
<:inodespace>,fseg=2>,
:internal=>
<:inodespace>,fseg=1>}
sizes:
header120
trailer8
directory18
free15783
used601
record455
perrecord13.00
pagedirectory:
[99,164,216,268,320,372,424,476,112]
systemrecords:
.............................
页结构信息中有一个level字段,表示的就是Root节点页的高度,同样,level + 1就等于这个索引的高度。
如果您觉得本站对你有帮助,那么可以支付宝扫码捐助以帮助本站更好地发展,在此谢过。