问题

1.线上数据库时不时莫名其妙的来一次性能抖动的问题,而且造成性能抖动的还不是之前我们讲过的数据库锂电池充放电的问题
2.如果 你要执行的是一个查询语句,需要查询大量的数据到缓存页里去,此时就可能导致内存里大量的脏页需
要淘汰出去刷入磁盘上,才能腾出足够的内存空间来执行这条查询语句。
在这种情况下,可能你会发现突然莫名其妙的线上数据库执行某个查询语句就一下子性能出现抖动,平时只要几十毫秒的查询语句,这次一下子要几秒都有可能,毕竟你要等待大量脏页flush到磁盘,然后语句才能执行

3.redo log buffer里的redo log本身也是会随着各种条件刷入磁盘上的日志文件的,比如redo log buffer里的数据超过容量的一定比例了,或者是事务提交的时候,都会强制buffer里的redo log刷入磁盘上的日志文件。
image.png
然后,当存在大量写请求进入时,redo log日志文件被快速写满,此时会重新回到第一个日志文件再次写入。
但是需要判断第一个日志文件是否已经刷入磁盘,否则直接覆盖时,万一数据库崩溃,数据会丢失。
在日志文件都被写满的情况下,也会触发一次脏页的刷新

尤其是在这一种刷脏页的情况下,因为redo log所有日志文件都写满了,此时会导致数据库直接hang死,无法处理任何更新请求,因为执行任何一个更新请求都必须要写redo log,此时你需要刷新一些脏页到磁盘,然后才能继续执行更新语句,把更新语句的redo log从第一个日志文件开始覆盖写。

所以此时假设你在执行大量的更新语句,可能你突然发现线上数据库莫名其妙的很多更新语句短时间内性能都抖动了,可能很多更新语句平时就几毫秒就执行好了,这次要等待1秒才能执行完毕。
因此遇到这种情况,你必须要等待第一个日志文件里部分redo log对应的脏页都刷入磁盘了,才能继续执行更新语句,此时必然会导致更新语句的性能很差。

优化方案

上述场景导致的大量缓存页flush到磁盘,就会导致莫名其妙的SQL语句性能抖动了
解决该问题的两个核心

  1. 尽量减少缓存页flush到磁盘的频率,
  2. 尽量提升缓存页flush到磁盘的速度

频率比较难控制,被填满时间不可控
刷入磁盘速度进行优化

  1. 把innodb_io_capacity设置为SSD固态硬盘的IOPS,让他刷缓存页尽量快
  2. 设置innodb_flush_neighbors为0,让他每次别刷临近缓存页,减少要刷缓存页的数量