InnoDB Tidbit:The doublewrite buffer wastes 32 pages (512 KiB) (12.双写缓冲区会导致512KB的浪费)

时间:2022-07-24
本文章向大家介绍InnoDB Tidbit:The doublewrite buffer wastes 32 pages (512 KiB) (12.双写缓冲区会导致512KB的浪费),主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

在我不断探索完全理解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空间文件的页面管理中,我描述了文件段是如何包含的:

  • 最多32个单独分配的“片段”页面的数组
  • “完整”区段列表(无页面空闲)
  • “未满”区段列表(部分分配)
  • “空闲”区列表(没有分配页)

导致分配完整的区段

在分配完整的区段之前,分配给一个文件段总是会填满片段数组。奇怪的是,双写缓冲区在这种情况下并不特殊。分配它的代码在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会分配以下页面:

  • 片段页13-44——永久未使用的片段页,但保留分配给双写缓冲区的文件段。
  • 范围从第64页开始,到第127页结束,实际上是双写缓冲区的第1块。
  • 范围从第128页开始,到第191页结束,实际上是双写缓冲区的第2块。

使用innodb_ruby转储文件段(通过inode)

我最近在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循环中分配所有三组页面(特别是在没有注释的情况下)。