本系列文章翻译自 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-regionsstart 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-listsname 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-iteratestart_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-indexesid 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-listsname 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-pagespage 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,165 个 INDEX 类型的页面:
$ innodb_space -f test/t.ibd space-page-type-regionsstart 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-listsname 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-iterate2560 ................................................................2624 ................................................................
复制代码
free_frag 区段描述符链表中的区段有一些“碎片”页面被使用:
$ innodb_space -f test/t.ibd -L free_frag space-list-iteratestart_page page_used_bitmap 0 ######################################..........................
复制代码
通过 space-indexes 命令可以看出大量已使用的页面被分配给叶文件段,也正如预期的一样,只有3个“内部”页面来管理 b+树 中的2,162个(译者注:原文为2,137,但结合下文输出应该为2,162)“叶”页面:
$ innodb_space -f test/t.ibd space-indexesid 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-listsname 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-pagespage 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 区段和 6 个 not_full 区段:
$ innodb_space -f test/t.ibd -p 3 index-fseg-leaf-listsname 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-pagespage 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-iteratestart_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-iteratestart_page page_used_bitmap 2176 #############...................................................2240 #...............................................................2304 #...............................................................2368 #...............................................................2432 #...............................................................2496 #...............................................................
复制代码
你可以在这里看到 InnoDB 页面拆分优化的一点“影子”: 为了在磁盘上按顺序排列数据, InnoDB 多次从 free 区段的第一个页面分配页面(页码是最可疑之处)。未来我们将对这种行为进行更深入的研究。
译者注:这里的“可疑之处”指的是上述 not_full 区段链表的 start_page 之间的间隔都是相同的 64。
评论