什么是刷脏?
当执行更新语句之后,MySQL先会将操作记录在change buffer中。然后执行WAL操作,将更新操作写入redo log中,此时内存中的数据与磁盘中的数据是不一致的,这些内存数据中不一致的数据称为脏数据“脏页”,这些脏数据需要同步到磁盘中,并且将redo log中对应的记录清除(因为redo log的大小是固定的,循环写),这个过程就叫刷脏。内存数据页写入到磁盘之后内容就一致了,就称为“干净页”。(“脏页和干净页都在内存中”)
刷脏会导致什么?
何时会刷脏?
拿酒店老板记账来说,什么时候需要将粉板上的帐记到账本上?
从redo log角度来说
- 第一种场景:当粉板写满的时候,也就是redo log写满的时候
从内存角度来说
- 第二种场景:当一天中帐太多老板记不住的时候,也就是系统内存不足了,而又需要新的内存页,因此需要淘汰一些旧的数据页,如果旧的数据页是脏页的话,则需要将脏页写到磁盘上。为何不直接清除内存中的数据页,等到下次需要读取的时候,直接将磁盘中的读取出来,把redo log中的应用不就行了,但是这种性能会很差,如果不清除内存的话,可以保证数据如果内存中有,那么就是正确的,如果内存中没有就直接取磁盘的就行了。
- 第三种场景:当老板空闲的时候,就会把粉板上的账记到账本上,也就是当MySQL认为系统”空闲“的时候
第四种场景:当店铺有事需要关门几天的时候,也就是MySQL正常停止的时候,这时候MySQL会把内存的脏页全都flush到磁盘上,这样下次启动的时候就直接从磁盘中读数据,启动速度很快。
四种场景的影响?
首先第三种,是再系统空闲的时候做的,此时系统没什么压力;第四种场景是本来数据库要关闭了,也不会有问题。
- 第一种场景是redo log写满了,要刷脏,这种情况是InnoDB要尽量避免的。因为出现这种情况的时候,整个系统就不能再执行更新了,所有的更新都要阻塞住,此时的更新数就为0
- 第二种是内存不够用了,需要刷脏,这种情况是常态。因为InnoDB是采用的buffer pool缓冲池来管理内存的,缓冲池中的内存有三种状态
- 还未使用的
- 使用了并且是干净页
- 使用了并且是脏页
InnoDB的策略是尽量使用内存,对于一个长期运行的库来说,内存池中未被使用的数据页很少
当要读入的数据页没有在内存中的时候,这时候就需要到缓冲池中申请一个数据页,这时候只能把最久不使用的数据页从内存中淘汰掉;如果淘汰的是一个干净页,就直接释放掉复用,如果淘汰的是脏页,就需要先将脏页flush到磁盘上变成干净页之后才能复用。
刷脏会出现的影响
- 当一个查询要淘汰的脏页太多的时候,会产生随机IO,导致查询的响应时间很长
- 日志写满,更新全部堵住,写性能跌为0,这种情况对于敏感的业务是不允许的。
InnoDB刷脏页控制策略
首先需要告诉MySQL所在主机的IO能力,这样InnoDB才能知道需要全力刷脏的时候可以刷多块。可以通过设置参数来设置这个值
innodb_io_capacity,这个参数建议设置为磁盘的IOPS。磁盘的IOPS可以通过fio工具来测试fio -filename=$filename -direct=1 -iodepth 1 -thread -rw=randrw -ioengine=psync -bs=16k -size=500M -numjobs=10 -runtime=10 -group_reporting -name=mytest
但是这个参数定义的很大的时候也不行,总不能一直刷脏因此还需``要控制刷脏的速度,首先如果刷太慢的话会发生什么?脏页太多、redo log写满。
- 因此需要考虑两个因素:一个是脏页比例,一个是redo log写盘的速度
- MySQL会先根据这两个因素分别算出来两个数,然后取最大的,参数
innodb_max_dirty_pages_pct是脏页比例上限,默认值是 75%,根据这个数算出来一个0-100之间的数字N;然后根据redo log写入序号和checkpoint的序号的差值,计算出一个0-100的数字M,差值越大算出的数字M越大,M和N取出大值R。之后引擎按照innodb_io_capacity乘以R%来控制刷脏页的速度。
平时要多关注脏页比例,脏页比例是通过innodb_buffer_pool_pages_dirty/innodb_buffer_pool_pages_total得到的,具体命令
mysql> select VARIABLE_VALUE into @a from global_status where VARIABLE_NAME = 'Innodb_buffer_pool_pages_dirty';
select VARIABLE_VALUE into @b from global_status where VARIABLE_NAME = 'Innodb_buffer_pool_pages_total';
select @a/@b;
刷脏的连座机制
MySQL如果在刷一个脏页的时候,如果这个数据页旁边的数据页刚好是脏页,就会把这个“邻居”一起刷掉,而且这个“邻居”还会继续连带刷脏。
在InnoDB中,通过设置innodb_flush_neighbors来控制这个行为,值为1的时候会有上述的“连座”机制,值为0的时候表示不找邻居,自己刷自己的。
意义
开启连座的时候对机械硬盘的优化是很大的,可以减少很多随机IO,因为磁盘的IOPS很低,所以刷脏会很慢。
如果使用的是SSD的固态硬盘的话,建议关闭,因为这个时候IOPS已经不是瓶颈了,只刷自己可以更快的完成必要的刷脏操作,减少SQL的执行时间。
在MySQL8.0中,innodb_flush_neighbors参数的默认值已经是0了。
