什么情况会引发数据库的flush过程(把内存中的数据写入磁盘中)

flush过程的类比例子

InnoDB在处理更新语句的时候,只做了写日志这一个磁盘操作。《孔乙己》里咸亨酒店掌柜用来记账的粉板,在更新内存写完redo log后,就返回给客户端,本次更新成功。
做下类比的话,掌柜记账的账本是数据文件,记账用的粉板是日志文件(redo log),掌柜的记忆就是内存

flush过程: 就是把内存里的数据写入磁盘的过程,对应是掌柜更新账本

脏页和干净页

脏页 当内存数据页跟磁盘数据页内容不一致的时候,我们称这个内存页为"脏页”。
干净页 内存数据写入到磁盘后,内存和磁盘上的数据页的内容就一致了,称为“干净页”。

什么情况会引发数据库的flush过程

由类比例子我们可以看到以下类似关系
日志relog ⟹ \Longrightarrow 粉笔板
内存 ⟹ \Longrightarrow 掌柜的记忆
磁盘 ⟹ \Longrightarrow 账本

  • 第一种场景,粉板满了,记不下了。这时候如果再有人来赊账,掌柜就只得放下手里的活儿,将粉板上的记录擦掉一些,留出空位以便继续记账。当然在擦掉之前,他必须先将正确的账目记录到账本中才行。
    这个场景,对应的就是InnoDB的redo log写满了。这时候系统会停止所有更新操作,把checkpoint往前推进,redo log留出空间可以继续写。我在第二讲画了一个redo log的示意图,这里我改成环形,便于大家理解。
    什么情况会引发数据库的flush过程(把内存中的数据写入磁盘中)
    checkpoint可不是随便往前修改一下位置就可以的。比如图2中,把checkpoint位置从CP推进到CP’,就需要将两个点之间的日志(浅绿色部分),对应的所有脏页都flush到磁盘上。之后,图中从write pos到CP’之间就是可以再写入的redo log的区域。
  • 第二种场景是,这一天生意太好,要记住的事情太多,掌柜发现自己快记不住了,赶紧找出账本把孔乙己这笔账先加进去。
    这种场景,对应的就是系统内存不足。当需要新的内存页,而内存不够用的时候,就要淘汰一些数据页,空出内存给别的数据页使用。如果淘汰的是“脏页”,就要先将脏页写到磁盘。你一定会说,这时候难道不能直接把内存淘汰掉,下次需要请求的时候,从磁盘读入数据页,然后拿redo log出来应用不就行了这里其实是从性能考虑的。如果刷脏页一定会写盘,就保证了每个数据页有两种状态:
    \qquad (1) 一种是内存里存在,内存里就肯定是正确的结果,直接返回;
    \qquad (2) 另一种是内存里没有数据,就可以肯定数据文件上是正确的结果,读入内存后返回。这样的效率最高。
  • 第三种场景是,生意不忙的时候,或者打样之后。这时候柜台没事,掌柜闲着也是闲着,不如更新账本。
    这种场景,对应的就是MSQL认为系统"空闲”的时候。当然,MSQL“这家酒店”的生意好起来可是会很快就能把粉板记满的,所以“掌柜"要合理地安排时间,即使是“生意好”的时候,也要见缝插针地找时间,只要有机会就刷一点“脏页”。
  • 第四种场景是,年底了咸亨酒店要关门几天,需要把账结清一下。这时候掌柜要把所有账都记到账本上,这样过完年重新开张的时候,就能就着账本明确账目情况了。
    这种场景,对应的就是MSQL正常关闭的情况。这时候,MSQL会把内存的脏页都flush到磁盘上,这样下次MySQL启动的时候,就可以直接从磁盘上读数据,启动速度会很快。

四种场景对性能的影响

第三种情况是属于MSQL空闲时的操作,这时系统没什么压力。第四种场景是数据库本来就要关闭了。这两种情况下,你不会太关注“性能"问题。所以这里,我们主要来分析一下前两种场景下的性能问题。

第一种是"redo log写满了,要flush脏页",这种情况是InnoDB要尽量避免的。因为出现这种情况的时候,整个系统就不能再接受更新了,所有的更新都必须堵住。如果你从监控上看,这时候更新数会跌为0。

第二种是“内存不够用了,要先将脏页写到磁盘”,这种情况其实是常态。InnoDB用缓冲池(buffer pool)管理内存,缓冲池中的内存页有三种状态:

  • 第一种是,还没有使用的;
  • 第二种是,使用了并且是干净页;
  • 第三种是,使用了并且是脏页。

本文参考:林晓彬《为什么我的MySQL会“抖”一下》