在 InnoDB MySQL 中,脏数据库页面在刷新回磁盘之前通常会在内存中保留多长时间?

How long do dirty database pages usually stay inside memory before getting flushed back to disk in InnoDB MySQL?

数据库页面我的意思是:

https://dev.mysql.com/doc/internals/en/innodb-page-structure.html

现在,当我们对这些页面发出查询时,这些页面会加载到内存中,并且只会在内存中更改并标记为脏

我不确定这是否取决于 O.S 或数据库,但我的问题是这些页面通常在内存中停留多长时间?

假设我们有一个用于高负载网络服务器的数据库,流量很大,缓冲区大小大约为 1gb 左右(不确定数据库服务器通常有多少),现在这些 1gb 有多少脏页?

如果在没有备用电源的情况下断电,那么对这些脏页所做的所有更改都会丢失,对吗? (基本上我想知道是否发生断电,如果没有备用电源并且发生大量插入和查询,内存中将要丢失的脏数据的估计百分比是多少?)

例如 这些脏页是否有可能在繁忙的服务器上停留超过 12 或 24 小时?

编辑:脏页是指页面在内存中被修改,例如其中的一行被更新或删除

how long do these pages usually stay dirty in memory?

这是可变的。 InnoDB 有一个后台线程将脏页刷新到磁盘。它会刷新少量页面,然后在 1 秒后再次刷新。

因此,如果您在短 space 时间内进行大量更新,就会使很多页面变脏。然后冲洗线程会逐渐将它们冲洗到磁盘。我们的想法是,这有助于随着时间的推移延长工作时间,因此突然出现的更新高峰不会使您的磁盘不堪重负。

但这意味着 "how long do these pages stay dirty in memory" 可能会有很大差异。我认为在典型情况下,它会在几分钟内完成。

不同版本的MySQL刷新方式不同。多年前,主后台线程每 1 秒刷新固定数量的页面。然后他们提出了自适应刷新,因此如果它检测到您进行了大量更改,它会自动增加刷新率。然后他们想出了一个名为页面清理器的专用线程。我认为甚至可以配置 MySQL 到 运行 多个页面清理器线程,但这对大多数应用程序来说不是必需的。

您可能也对我对过去这些问题的回答感兴趣:

  • How to calculate amount of work performed by the page cleaner thread each second?

Lets say ... the buffer size is like 1gb or something(not sure how much database servers usually have)

它确实因应用程序而异。开箱即用的默认 innodb 缓冲池大小为 128MB,但这对于大多数应用程序来说太小了,除非它是测试实例。

在我的公司,我们尽量将缓冲池保持在磁盘上数据大小的至少 10%。有些应用程序需要更多。我们最常见的大小是 24GB,但最小的是 1GB,最大的是 200GB。我们管理着 4,000 多个生产 MySQL 实例。

how much of these 1gb could be dirty pages?

理论上都是。 MySQL 有一个配置变量 calls innodb_max_dirty_pages_pct 如果你有太多的话,你可能会认为它会阻止任何进一步的脏页面。但事实并非如此。即使缓冲池比该变量更脏(按百分比),您仍然可以修改更多页面。

这个变量真正做的是,如果缓冲池中脏页的百分比超过这个百分比,则刷新脏页的速率会增加(IIRC,它使每个周期刷新的页数加倍),直到数字再次低于该百分比阈值。

if the power is lost with no backup power, then all of the changes to these dirty pages get lost correct?

是的,但您不会丢失更改,因为它们可以从 InnoDB 重做日志中重建——您可能在数据目录中看到的这两个文件 iblogfile_0iblogfile_1 .任何创建脏页的事务都必须在提交期间记录在重做日志中。

如果断电(或其他类型的 mysqld 进程重启),InnoDB 做的第一件事是扫描重做日志以检查记录的每个更改是否在崩溃前刷新,否则如果没有,加载原始页面并重新应用日志中的更改以再次制作脏页面。这就是 InnoDB 所说的崩溃恢复。

你可以看到这一切发生。在 MySQL 服务器的测试实例上跟踪错误日志,而你 kill -9 mysqld 进程。 mysqld_safe 将重新启动 mysqld 进程,它会在执行崩溃恢复时将大量信息输出到错误日志中。

如果只有少量脏页需要恢复,这会很快,也许只有几秒钟。如果缓冲池很大并且有很多脏页,则需要更长的时间。 MySQL 服务器未完全启动,并且无法接受新的客户端连接,直到崩溃恢复完成。这导致许多 MySQL DBA 在观看崩溃恢复的进度时焦虑了很多分钟。无法预测崩溃后需要多长时间。

由于崩溃恢复需要重做日志,如果重做日志填满,MySQL 必须 刷​​新一些脏页。它不允许脏页被取消刷新,也不允许从重做日志中恢复。如果发生这种情况,您实际上会看到 InnoDB 暂停写入,直到它可以执行某种 "emergency flush" 最旧的脏页。这曾经是 MySQL 的问题,但随着自适应刷新和页面清理器等改进,它可以更好地跟上变化的步伐。您必须拥有非常多的写入和一个过小的重做日志才能在执行同步刷新时在 InnoDB 上遇到硬停止。

这是一篇关于冲洗的好博客:https://www.percona.com/blog/2011/04/04/innodb-flushing-theory-and-solutions/

P.S.: 对于针对 MyISAM 的强制性 bash,我会指出 MyISAM 没有重做日志,没有崩溃恢复,并且依赖于主机OS 写入其数据文件期间的文件缓冲区。如果您的主机在文件缓冲区中有未决写入且尚未写入磁盘时出现电源故障,您将丢失它们。 MyISAM 对 ACID 的持久性 属性 没有任何真正的支持。


回复您的评论:

重做日志回收时可能会刷新页面。也就是说,如果您有 2 个 48MB 的重做日志文件(默认大小),并且您向其中写入了足够多的事务以完全循环并从头开始,那么缓冲池中的任何页面在此期间变脏都需要脸红了。如果重做日志中的相应事务被新事务覆盖,则页面不能在 BP 中保持脏状态。

据我了解,脏页几乎不可能在缓冲池中保持脏状态而不被刷新 12-24 小时。

可能的例外情况(我只是对此进行推测)是给定页面在刷新之前一次又一次地更新。因此很长一段时间它仍然是最近的脏页。同样,我不确定这是否克服了重做日志回收时刷新页面的需要。

无论如何,我认为这不太可能。

此外,我不确定您所说的取证是什么意思。没有直接的方法来检查缓冲池中的页面版本。要从 InnoDB 获取有关最近更改的信息,您需要检查撤消段以查找以前版本的页面,并将它们与重做日志条目相关联。脏页及其之前的版本都可以在缓冲池中,也可以在磁盘上。没有命令或 API 或任何数据结构来进行任何关联。因此,您需要手动转储磁盘映像和内存映像,并手动跟踪指针。

一种更简单的跟踪数据更改的方法是检查二进制日志中的更改流。这与 InnoDB 无关。