InnoDB的脏页刷盘时机

场景一:redo log快满了

我们知道,redo log的空间是固定大小的,那么就就有使用完毕的风险。

假设某个时刻系统疯狂的进行修改操作,那么大量的日志就会进入redo log中。此时刷盘速度(对应check point前进速度)的远远小于写盘速度(write pos前进速度)时,就会触发刷盘。

但这种刷盘的后果是非常严重的,和上图一样,这时系统就会停止所有更新操作,把checkoutpoint 往前推,redo log留出空间可以继续写。此时整个系统都不能接受更新。更新数会跌为0。后续我们必然会讲一下如何避免这个问题。

场景二:Buffer Pool内存不足时

一般会从LUA链表中,直接释放一些长期未被访问,且没有更新的操作的页(释放这种页不需要磁盘IO)。假设都是脏页,那也只能强行刷盘了。

场景三:数据库空闲时刷盘(主流刷盘方式)

场景一,场景二和大家解释了半天,但实际上绝大多数场景下,上面两种情况都是要避免的。正常来说,都是等数据库空闲的时候去刷盘,以保证MySQL一直保持一个较高的吞吐量。

场景四:数据库正常关闭时刷盘

数据库正常关闭的时候,也要把内存中所有的脏页全都flush到磁盘上。

InnoDB 刷脏页的控制策略

我们知道,如果触发了场景一,场景二的刷盘条件,就会让MySQL迫不得已做一些严重影响效率的事儿去强制刷盘。

因此,为了避免这两种情况,我们需要正确的控制 InnoDB IO 能力,来控制刷脏页的快慢。如果刷太慢,会出现什么情况?首先是内存脏页太多,其次是 redo log 写满。

基于以上的目的,InnoDB 的刷盘速度就是要参考这两个因素:一个是脏页比例,一个是 redo log 写盘速度。

基于脏页比例,计算F1

参数 innodb_max_dirty_pages_pct 是脏页比例上限,默认是 75%。InnoDB 会根据当前的脏页比例,计算出一个数字 F1。

F1(M)
{
  if M>=innodb_max_dirty_pages_pct then
      return 100;
  return 100*M/innodb_max_dirty_pages_pct;
}


M是实际的脏盘比例。这个公式可以理解为,脏盘越多,F1越高。

基于redo log剩余空间,计算F2

InnoDB 写入日志都会有一个序号,当前写入序号跟 checkpoint 对应的序号之间的差值,假设为N。InnoDB 会根据N计算出 F2.

上面的话你可能看的有点绕。多读读总结出来就是:redo log越满,值就越大。

取F1,F2的最大值,来控制刷盘速度

根据 F1和F2 取其中较大的值为 R,之后引擎就可以按照 Innodb_io_capacity 定义的能力乘以 R% 来控制刷脏页的速度。

mysql 刷脏页导致响应缓慢 mysql脏页什么时候刷盘_mysql 刷脏页导致响应缓慢

当然,如果开启,刷盘的速度越高,必然会影响MySQL的吞吐量。并且如果用最高的速度刷盘,依旧导致Buffer Pool或redo log满了,那也只能使用场景一,场景二这种无奈的办法刷盘了。

MySQL的刷盘优化

MySQL 中有一个机制,刷脏页的时候如果数据页旁边的数据页也是脏页,那么就会一起刷掉,而且这个逻辑是可以蔓延的,所以对于每个相邻的数据页,都会被一起刷。

在 InnoDB 中,innodb_flush_neighbors 参数就是用来控制这个行为的,值为 1 的时候会有上述的“连坐”机制,值为 0 时表示不找邻居,自己刷自己的。

在使用机械硬盘时,这个优化很有意义,可以减少很多随机 IO。如果使用的是 SSD 这种IOPS 比较高的设备,可以设置innodb_flush_neighbors 为0,只刷自己,这个时候 IOPS 往往就不是性能瓶颈了。只刷自己就可以提高刷脏页的速度,减少 SQL 语句的响应时间。(有钱烧机器还是强)