InnoDB刷脏页策略
什么是脏页?为什么会有脏页?
前文已介绍过,Mysql有Write Ahead Logging 机制,InnoDB 在处理更新语句的时候,只做了写日志redo log这一个磁盘操作。而将内存里的数据写入磁盘的过程,术语就是 flush
。在flush 操作执行之前,内存与磁盘数据是不一致的。
当内存数据页跟磁盘数据页内容不一致的时候,我们称这个内存页为“脏页”。 内存数据写入到磁盘后,内存和磁盘上的数据页的内容就一致了,称为“干净页”。
什么情况下会刷脏页?
刷脏页有下列四种场景: 一、 redo log 写满了,要 flush 脏页
redo log 写满了,系统会停止所有更新操作,把 checkpoint 往前推进,redo log 留出空间可以继续写。 需要将两个点之间的日志(要推进的部分),对应的所有脏页都 flush 到磁盘上。 innodb要尽量避免,会堵住所有更新。
二、内存不够用了,要先将脏页写到磁盘
这种情况是常态。InnoDB 用缓冲池(buffer pool)管理内存,缓冲池中的内存页有三种状态:
还没有使用的;
使用了并且是干净页;
使用了并且是脏页。
InnoDB 的策略是尽量使用内存,因此对于一个长时间运行的库来说,未被使用的页面很少。 而当要读入的数据页没有在内存的时候,就必须到缓冲池中申请一个数据页。这时候只能把最久不使用的数据页从内存中淘汰掉:如果要淘汰的是一个干净页,就直接释放出来复用;但如果是脏页呢,就必须将脏页先刷到磁盘,变成干净页后才能复用。
三、 mysql认为系统空闲,flush脏页
四、 mysql正常关闭
刷脏页虽然是常态,但是出现以下这两种情况会导致性能问题
一个查询要淘汰的脏页个数太多,会导致查询的响应时间明显变长;
日志写满,更新全部堵住,写性能跌为 0,这种情况对敏感业务来说,是不能接受的。
InnoDB刷脏页控制策略与参数
innodb_io_capacity
参数建议设置成磁盘的 IOPS
innodb_io_capacity
设置错误案例: 主机磁盘用的是 SSD,但是innodb_io_capacity
的值设置的是 300。于是,InnoDB 认为这个系统的能力就这么差,所以刷脏页刷得特别慢,甚至比脏页生成的速度还慢,这样就造成了脏页累积,影响了查询和更新性能。
如果设计策略控制刷脏页的速度,应该参考哪些因素呢?
刷太慢,会出现什么情况?首先是内存脏页太多,其次是 redo log 写满。所以,InnoDB 的刷盘速度就是要参考这两个因素:
脏页比例
redo log 写盘速度。
innodb_max_dirty_pages_pct
是脏页比例上限,默认值是 75%。
通过脏页比例和 redo log 写入速度算出来的两个值,取其中较大值R, 引擎就可以按照innodb_io_capacity
定义的能力乘以 R% 来控制刷脏页的速度。
InnoDB 会在后台刷脏页,而刷脏页的过程是要将内存页写入磁盘。所以,无论是你的查询语句在需要内存的时候可能要求淘汰一个脏页,还是由于刷脏页的逻辑会占用 IO 资源并可能影响到了你的更新语句,都可能是造成你从业务端感知到 MySQL“抖”了一下的原因。
刷脏页“连坐”参数
MySQL 中的一个机制,在准备刷一个脏页的时候,如果这个数据页旁边的数据页刚好是脏页,就会把这个“邻居”也带着一起刷掉;而且这个把“邻居”拖下水的逻辑还可以继续蔓延,也就是对于每个邻居数据页,如果跟它相邻的数据页也还是脏页的话,也会被放到一起刷。InnoDB 中,innodb_flush_neighbors = 1
会有上述的“连坐”机制,为0不会。
这种机制在机械硬盘上,可以减少很多随机 IO,提升系统性能。而使用SSD这类IOPS高的设备,建议设置成0,此时IOPS 往往不是瓶颈,可以设置“只刷自己”减少 SQL 语句响应时间。
参考: 《mysql实战45讲》丁奇
Last updated
Was this helpful?