一、 基本概念:
1、当内存数据页跟磁盘数据页内容不一致的时候,称这个内存页为“脏页”。内存数据写入到磁盘后(内存里的数据写入磁盘的过程叫做flush。),内存和磁盘上的数据页的内容就一致了,称为“干净页”。不论是脏页还是干净页,都在内存中。
2、平时执行很快的更新操作,其实就是在写内存和日志,而MySQL偶尔“抖”一下的那个瞬间,可能就是在刷脏页(flush)。
3、引发数据库的flush过程的四种情况:
1)、第一种情况是InnoDB的redo log写满了。这时候系统会停止所有更新操作,把checkpoint往前推进,redo log留出空间可以继续写。checkpoint可不是随便往前修改一下位置就可以的。在把checkpoint位置从CP推进到CP'时,就需要将两个点之间的日志对应的所有脏页都flush到磁盘上。之后,之前可以写入redo log的区域就从图中从write pos到CP之间变为了从write pos 到CP'。
2)、第二种情况是系统内存不足。当需要新的内存页,而内存不够用的时候,就要淘汰一些数据页,空出内存给别的数据页使用。如果淘汰的是“脏页”,就要先将脏页写到磁盘。如果刷脏页一定会写盘,就保证了每个数据页有两种状态:
a、一种是内存里存在,内存里就肯定是正确的结果,直接返回;
b、另一种是内存里没有数据,就可以肯定数据文件上是正确的结果,读入内存后返回。这样的效率最高。
3)第三种情况是MySQL认为系统“空闲”的时候。当然,即使是系统“忙碌”的时候,也要见缝插针地找时间,只要有机会就刷一点“脏页”。
4)、第四种情况是MySQL正常关闭的时候。这时候,MySQL会把内存的脏页都flush到磁盘上,这样下次MySQL启动的时候,就可以直接从磁盘上读数据,启动速度会很快。
4、引发数据库的flush过程的四种情况对性能的影响:
1)、“redo log写满了,要flush脏页”,这种情况是InnoDB要尽量避免的。因为出现这种情况的时候,整个系统就不能再接受更新了,所有的更新都必须堵住。如果你从监控上看,这时候更新数会跌为0。
2)、“内存不够用了,要先将脏页写到磁盘”,这种情况其实是常态。InnoDB用缓冲池(buffer pool)管理内存,缓冲池中的内存页有三种状态:第一种是,还没有使用的;第二种是,使用了并且是干净页;第三种是,使用了并且是脏页。InnoDB的策略是尽量使用内存,因此对于一个长时间运行的库来说,未被使用的页面很少。而当要读入的数据页没有在内存的时候,就必须到缓冲池中申请一个数据页。这时候只能把最久不使用的数据页从内存中淘汰掉:如果要淘汰的是一个干净页,就直接释放出来复用;但如果是脏页呢,就必须将脏页先刷到磁盘,变成干净页后才能复用。
3)、因为系统处于“空闲”的状态中,所以不需要关心其对性能的影响。
4)、因为数据库本来就要关闭了,所以不需要关心其对性能的影响。
5、刷脏页会明显影响性能的情况:
1)、一个查询要淘汰的脏页个数太多,会导致查询的响应时间明显变长;
2)、日志写满,更新全部堵住,写性能跌为0,这种情况对敏感业务来说,是不能接受的。
二、InnoDB刷脏页的控制策略:
首先,测试出InnoDB所在主机的IO能力,这样InnoDB才能知道需要全力刷脏页的时候,可以刷多快。也就是innodb_io_capacity这个参数,它的作用是告诉InnoDB你的磁盘能力。这个值建议设置成磁盘的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
又因为磁盘能力不能只用来刷脏页,还需要服务用户请求。所以InnoDB会控制引擎按照“全力”的百分比来刷脏页。
1、InnoDB的刷盘速度就是要参考的两个因素:
①脏页比例,参数innodb_max_dirty_pages_pct是脏页比例上限,默认值是75%。要合理地设置innodb_io_capacity的值,并且平时多关注脏页比例,不要让它经常接近75%。其中,脏页比例是通过Innodb_buffer_pool_pages_dirty/Innodb_buffer_pool_pages_total得到。
②redo log写盘速度。
2、InnoDB的刷盘速度的计算方法:
InnoDB会根据当前的脏页比例(假设为M),算出一个范围在0到100之间的数字,记为F1(M)。InnoDB每次写入的日志都有一个序号,当前写入的序号跟checkpoint对应的序号之间的差值(假设为N)。InnoDB会根据这个N算出一个范围在0到100之间的数字,这个计算公式可以记为F2(N)。N越大,算出来的值越大。根据上述算得的F1(M)和F2(N)两个值,取其中较大的值记为R,之后引擎就可以按照innodb_io_capacity定义的能力乘以R%来控制刷脏页的速度。
3、flush脏页时的邻居连坐机制:
在准备刷一个脏页的时候,如果这个数据页旁边的数据页刚好是脏页,就会把这个“邻居”也带着一起刷掉;而且这个把“邻居”拖下水的逻辑还可以继续蔓延,也就是对于每个邻居数据页,如果跟它相邻的数据页也还是脏页的话,也会被放到一起刷。在InnoDB中,innodb_flush_neighbors 参数就是用来控制这个行为的,值为1的时候会有上述的“连坐”机制,值为0时表示不找邻居,自己刷自己的。找“邻居”这个优化在机械硬盘时代是很有意义的,可以减少很多随机IO。机械硬盘的随机IOPS一般只有几百,相同的逻辑操作减少随机IO就意味着系统性能的大幅度提升。而如果使用的是SSD这类IOPS比较高的设备的话,建议把innodb_flush_neighbors的值设置成0。因为这时候IOPS往往不是瓶颈,而“只刷自己”,就能更快地执行完必要的刷脏页操作,减少SQL语句响应时间。
三、如果一个高配的机器,redo log设置太小,会发生什么情况?
因为每次事务提交都要写redo log,如果设置太小,很快就会被写满,write pos一直追着CP。这时候系统不得不停止所有更新,去推进checkpoint。这时,你看到的现象就是磁盘压力很小,但是数据库出现间歇性的性能下跌。