[翻译] 使用 innodb_ruby 探索 InnoDB 的页面管理

510 阅读4分钟

本系列文章翻译自 Jeremy Cole's Blog 中的 InnoDB系列 文章 。共 16 篇,本文为第 5 篇。原文链接:Exploring InnoDB page management with innodb_ruby

因翻译水品有限,为了避免对读者造成误解,一些专有名词的翻译会在其后用[]标记出原文。

使用 innodb_ruby 探索 InnoDB 的页面管理

[本文中所使用的 innodb_ruby 版本为 0.8.8(发布于2014年2月3日)]

On learning InnoDB: A journey to the core译文)一文中,我提到了 innodb_ruby 项目以及其中的命令行工具。在后续 A quick introduction to innodb_ruby译文)一文中,我介绍了 innodb_space 命令行工具的安装和一些快速演示。

在上一篇文章 Page management in InnoDB space files译文)中,我描述了 InnoDB 的区段、文件段以及空闲空间管理相关的结构。接下来,我会提供一些使用 innodb_space 检查真实的表中这些结构的示例。

一个最小化的空表

我创建了一个空表(表结构不重要)来说明 InnoDB 页面管理结构的“最小化”状态。space-page-type-regions 命令会汇总出同一类型页面的所有连续区域:

$ innodb_space -f test/e.ibd space-page-type-regions
start       end         count       type                
0           0           1           FSP_HDR             
1           1           1           IBUF_BITMAP         
2           2           1           INODE               
3           3           1           INDEX               
4           5           2           FREE (ALLOCATED)  

通过输出可以看到,这张表已经在 .idb 文件中分配了“标准”页面: FSP_HDR 页面、IBUF_BITMAP 页面、INODE 页面、INDEX 页面(用于空索引的根),还有两个未使用的 FREE (ALLOCATED) 页面 。

space-lists 命令会汇总空间中的区段描述符链表和 inode 链表:

$ innodb_space -f test/e.ibd space-lists
name                length      f_page      f_offset    l_page      l_offset    
free                0           0           0           0           0           
free_frag           1           0           158         0           158         
full_frag           0           0           0           0           0           
full_inodes         0           0           0           0           0           
free_inodes         1           2           38          2           38          

只有 free_frag 区段描述符链表中有条目,并且其中只有一个区段。free_inodes 链表中有一个 INODE 页面。

可以使用 space-list-iterate 命令检查 free_frag 链表的内容,该命令将打印一个图形,说明在区段列表中所有区段内页面的使用情况("#"表示该页面已使用,"." 表示页面是空闲的) :

$ innodb_space -f test/e.ibd -L free_frag space-list-iterate
start_page  page_used_bitmap                                                
0           ####............................................................

译者注:实际上由上文的 space-page-type-regions 命令输出可以看出,InnoDB 目前只分配了6个页面,space-list-iterate 的输出中第一个区段中不应该有64#或者.。如果你使用最新版本的innodb_ruby运行上述命令会发现,只会输出####..,表示有4个页面已使用,2个页面未使用。

space-indexes可以将空间中所有索引的文件段进行汇总:

$ innodb_space -f test/e.ibd space-indexes
id          root        fseg        used        allocated   fill_factor 
16          3           internal    1           1           100.00%     
16          3           leaf        0           0           0.00%       

只有“内部”文件段分配了页面,并且只分配了一个页面。index-fseg-internal-lists 模式将汇总“内部”文件段中的区段链表:

$ innodb_space -f test/e.ibd -p 3 index-fseg-internal-lists
name                length      f_page      f_offset    l_page      l_offset    
free                0           0           0           0           0           
not_full            0           0           0           0           0           
full                0           0           0           0           0           

这三个列表都是空的,因为这个空表没有分配任何完整的区段。那么,已使用的一个页面在哪里呢?这是一个“碎片”页面,可以使用 index-fseg-internal-frag-pages 模式列出:

$ innodb_space -f test/e.ibd -p 3 index-fseg-internal-frag-pages
page        index   level   data    free    records 
3           16      0       0       16252   0       

这是表的“最小化”状态———空间文件中大部分是空白的簿记[bookeeping]结构,只有一个 INDEX 页面。接下来让我们来看一个包含一些实际数据的表。

一个有一百万行的表

A quick introduction to innodb_ruby译文)一文中,我创建了一个包含 100万 行的表。我们将在下文的示例中使用相同的表。

通过 space-page-type-regions 命令可以看到总共有 2,165INDEX 类型的页面:

$ innodb_space -f test/t.ibd space-page-type-regions
start       end         count       type                
3           37          35          INDEX               
38          63          26          FREE (ALLOCATED)    
64          2188        2125        INDEX               
2189        2239        51          FREE (ALLOCATED)    
2240        2240        1           INDEX               
2241        2303        63          FREE (ALLOCATED)    
2304        2304        1           INDEX               
2305        2367        63          FREE (ALLOCATED)    
2368        2368        1           INDEX               
2369        2431        63          FREE (ALLOCATED)    
2432        2432        1           INDEX               
2433        2495        63          FREE (ALLOCATED)    
2496        2496        1           INDEX               
2497        2687        191         FREE (ALLOCATED)    

译者注:上面的输出只展示了 INDEX 类型和 FREE (ALLOCATED) 类型的页面,如果你实际执行这个命令,应该还会输出其他类型的页面,比如 FSP_HDR 页面、IBUF_BITMAP 页面、INODE 页面。

注意,在 INDEX 页面的连续区域中间有一些 FREE (ALLOCATED) 页面组成的间隙。这是因为InnoDB 不能保证按顺序使用空闲页面,而且有关批量数据加载的许多优化也会导致页面无序使用。(更多关于页面拆分和这些优化的内容将在以后的文章中介绍)

再来看看看空间中的链表,有一些区段在 free 链表中,也有一些区段在 free_frag 链表中:

$ innodb_space -f test/t.ibd space-lists
name                length      f_page      f_offset    l_page      l_offset    
free                2           0           1758        0           1798        
free_frag           1           0           158         0           158         
full_frag           0           0           0           0           0           
full_inodes         0           0           0           0           0           
free_inodes         1           2           38          2           38          

如预期的一样,free 区段描述符链表中的区段 所有页面都是空闲的:

$ innodb_space -f test/t.ibd -L free space-list-iterate
2560        ................................................................
2624        ................................................................

free_frag 区段描述符链表中的区段有一些“碎片”页面被使用:

$ innodb_space -f test/t.ibd -L free_frag space-list-iterate
start_page  page_used_bitmap                                                
0           ######################################..........................

通过 space-indexes 命令可以看出大量已使用的页面被分配给叶文件段,也正如预期的一样,只有3个“内部”页面来管理 b+树 中的2,162个(译者注:原文为2,137,但结合下文输出应该为2,162)“叶”页面:

$ innodb_space -f test/t.ibd space-indexes
id          root        fseg        used        allocated   fill_factor 
15          3           internal    3           3           100.00%     
15          3           leaf        2162        2528        85.52%      

你还可以看到叶文件段分配的页面比实际使用的多,fill_factor(“填充因子”)为 85.52%。这是由于 InnoDB 的 段填充因子[segment fill factor] 在 MySQL 中固定为 87.5%。不过现在在 Twitter MySQL 中是可以进行配置的,这要归功于 Facebook 提交的 MySQL Bug 64673

因为内部文件段只有 3 个页面,所以文件段列表都是空的:

$ innodb_space -f test/t.ibd -p 3 index-fseg-internal-lists
name                length      f_page      f_offset    l_page      l_offset    
free                0           0           0           0           0           
not_full            0           0           0           0           0           
full                0           0           0           0           0           

3 个已使用的页面被分配为碎片页:

$ innodb_space -f test/t.ibd -p 3 index-fseg-internal-frag-pages
page        index   level   data    free    records 
3           15      2       26      16226   2       
36          15      1       14521   1401    1117    
37          15      1       13585   2341    1045    

而叶子索引文件段的链表则非常“繁忙”,有 33(译者注:原文这里为32,结合下文输出,应为33)个 full 区段和 6not_full 区段:

$ innodb_space -f test/t.ibd -p 3 index-fseg-leaf-lists
name                length      f_page      f_offset    l_page      l_offset    
free                0           0           0           0           0           
not_full            6           0           1518        0           1718        
full                33          0           198         0           1478        

此外,叶文件段已经分配了所有可以分配的 32 个碎片页面(在分配完整区段之前会先分配碎片页面):

$ innodb_space -f test/t.ibd -p 3 index-fseg-leaf-frag-pages
page        index   level   data    free    records 
4           15      0       9812    6286    446     
5           15      0       15158   860     689     
6           15      0       10912   5170    496     
7           15      0       10670   5412    485     
8           15      0       12980   3066    590     
9           15      0       11264   4808    512     
10          15      0       4488    11690   204     
11          15      0       9680    6418    440     
12          15      0       9306    6800    423     
13          15      0       9658    6434    439     
14          15      0       10032   6062    456     
15          15      0       9988    6108    454     
16          15      0       9570    6530    435     
17          15      0       9130    6978    415     
18          15      0       8844    7266    402     
19          15      0       11770   4300    535     
20          15      0       9020    7092    410     
21          15      0       8646    7462    393     
22          15      0       9746    6354    443     
23          15      0       11066   5014    503     
24          15      0       8910    7204    405     
25          15      0       11748   4322    534     
26          15      0       10978   5094    499     
27          15      0       11132   4940    506     
28          15      0       9350    6750    425     
29          15      0       13508   2526    614     
30          15      0       14938   1082    679     
31          15      0       14520   1506    660     
32          15      0       9086    7016    413     
33          15      0       9724    6368    442     
34          15      0       10978   5102    499     
35          15      0       9504    6592    432     

正如预期的那样,full区段都是“满”的:

$ innodb_space -f test/t.ibd -p 3 -L full index-fseg-leaf-list-iterate
start_page  page_used_bitmap                                                
64          ################################################################
128         ################################################################
192         ################################################################
256         ################################################################
320         ################################################################
384         ################################################################
448         ################################################################
512         ################################################################
576         ################################################################
640         ################################################################
704         ################################################################
768         ################################################################
832         ################################################################
896         ################################################################
960         ################################################################
1024        ################################################################
1088        ################################################################
1152        ################################################################
1216        ################################################################
1280        ################################################################
1344        ################################################################
1408        ################################################################
1472        ################################################################
1536        ################################################################
1600        ################################################################
1664        ################################################################
1728        ################################################################
1792        ################################################################
1856        ################################################################
1920        ################################################################
1984        ################################################################
2048        ################################################################
2112        ################################################################

not_full区段都是部分空闲的,正如预期的那样:

$ innodb_space -f test/t.ibd -p 3 -L not_full index-fseg-leaf-list-iterate
start_page  page_used_bitmap                                                
2176        #############...................................................
2240        #...............................................................
2304        #...............................................................
2368        #...............................................................
2432        #...............................................................
2496        #...............................................................

你可以在这里看到 InnoDB 页面拆分优化的一点“影子”: 为了在磁盘上按顺序排列数据, InnoDB 多次从 free 区段的第一个页面分配页面(页码是最可疑之处)。未来我们将对这种行为进行更深入的研究。

译者注:这里的“可疑之处”指的是上述 not_full 区段链表的 start_page 之间的间隔都是相同的64。