在我不断探索完全理解InnoDB数据存储的过程中,我遇到了一个非常小而无关紧要的问题。这个问题还是比较有趣的。我注意到下面的页面的块,他们很早就在ibdata1系统标空间中分配,但是显然没用使用。(不必要的行从输出的过程中删除):
$ innodb_space -f ibdata1 space-page-type-regions
start end count type
13 44 32 ALLOCATED
大多数使用InnoDB的人都听说过“双写缓冲区”——InnoDB页面刷新策略的一部分。双写缓冲区用作一个“暂存区”,在将128页刷新到最终目的地(可能多达128个不同的写操作)之前,连续地写入(默认情况下)128页。MySQL手册上说,在“InnoDB磁盘I/O”中: InnoDB使用了一种新的文件刷新技术,涉及到一种叫做双写缓冲区的结构。它增加了在操作系统崩溃或停电后恢复的安全性,并通过减少对fsync()操作的需求提高了大多数Unix上的性能。 在将页面写入数据文件之前,InnoDB首先将它们写入一个连续的表空间区域,称为双写缓冲区。只有在对双写缓冲区的写入和刷新完成后,InnoDB才会将页面写入到数据文件中的正确位置。如果操作系统在写页面的过程中崩溃了,InnoDB可以在恢复过程中从双写缓冲区中找到一个好的页面副本。
通常,双写缓冲区由两个区段组成,每个区段是64个连续的页(1 MiB),总共有128个页(2 MiB)。但是,InnoDB不能盲目地借用这两个区段;它必须在空间文件中申明它们。为此,它创建一个文件段(又名Fseg)并使用一个Inode指向它。在InnoDB空间文件的页面管理中,我描述了文件段是如何包含的:
在分配完整的区段之前,分配给一个文件段总是会填满片段数组。奇怪的是,双写缓冲区在这种情况下并不特殊。分配它的代码在trx/trx0sys.c中第335行使用了以下循环:
for (i = 0; i < 2 * TRX_SYS_DOUBLEWRITE_BLOCK_SIZE
+ FSP_EXTENT_SIZE / 2; i++) {
不幸的是,这段代码没有任何注释,但总共分配了160页:
FSP_EXTENT_SIZE / 2 → 64 / 2 → 32 pages
2 * TRX_SYS_DOUBLEWRITE_BLOCK_SIZE → 2 * 64 → 128 pages
最初分配的32个页面纯粹是为了填充片段数组,从而迫使随后的fseg_alloc_free_page调用开始为剩下的128个页面分配完整的区段(这是双写缓冲区实际需要的)。然后,该代码检查分配了哪些区段,并将这些区段的初始页号添加到TRX_SYS报头中,作为双写缓冲区分配。在一个典型的系统中,InnoDB会分配以下页面:
我最近在innodb_ruby的innodb_space程序中添加了一个新的空格-inode -detail和空格-inode -summary模式,它可以方便地精确地显示分配给给定文件段的页面和区段(为了清晰而剪裁,为了换行而重新格式化;通常打印在一行上):
$ innodb_space -f ibdata1 space-inodes-detail
INODE fseg_id=15, pages=160,
frag=32 pages (13, 14, ..., 43, 44),
full=2 extents (64-127, 128-191),
not_full=0 extents () (0/0 pages used),
free=0 extents ()
在这里,您可以清楚地看到文件段的“full”列表中的两个完整区段,以及32个片段页。
有几种方法可以避免这种情况,比如在分配两个区段之后释放各个页面,或者添加特殊的“无片段”分配方法。然而,正如我在开始时所说的,这是非常无关紧要的,因为每次安装总共只有512 KiB。考虑到这些细节的行为,代码绝对可以使用重写来保持清晰。它还可以使用现有的定义,如fseg_frag_arr_n_slot或FSEG_FRAG_LIMIT,而不是重复基本无法解释的计算FSP_EXTENT_SIZE / 2。此外,重写它以使用一个更有意义的循环结构将是有益的;它没有理由在同一个for循环中分配所有三组页面(特别是在没有注释的情况下)。