一、预备知识
WAL
InnoDB在处理更新语句的时候是会遵循WAL(Write-Ahead Logging)机制的,即是更新了内存和做了写redo log这个磁盘操作而不是直接更新磁盘中对应的表(因为去到磁盘中全表遍历找到对应的表更新,因为这样会耗费很多时间),理想情况下是等到系统空闲时再把内存与磁盘中不一致的数据同步到磁盘。
脏页&&干净页
在MySQL中将数据从磁盘读取到内存是按页读取的,这个页称为数据页。当更新时WAL机制造成内存数据页和磁盘数据页内容不一致的页称为脏页。相反的内存数据页和磁盘数据页内容一致的称为干净页。
二、同一条sql为什么变慢了?
1、内存占满导致变慢
所以从上面我们不难理解平时执行的很快的更新操作其实只是在写内存和redo log而已。而更新操作突然变慢可能是因为内存满了,我们需要先把内存上的脏页更新到磁盘上使得它变成内存的干净页再把它释放,这个过程称为刷脏页(flush)。
2、redo log写满了导致变慢
除了是内存满了这个情况以外,还有可能是因为redo log满了导致所有更新操作被迫停止,使得需要刷脏页让redo log腾出空间。
注意:除了上面的两种情况以外会刷脏页以外,系统会在其“空闲”的时候刷脏页或者MySQL关闭的时候会把内存中的所有脏页flush到磁盘里。
三、如何尽量避免刷脏页带来的性能问题?
1、设置innodb_io_capacity
通过
innodb_io_capacity
这个参数来告诉Innodb磁盘的io能力,使得InnoDB在系统“空闲”的时候可以全力刷脏页。
2、设置innodb_flush_neighbors
除此之外,MySQL里还有一个
innodb_flush_neighbors
参数也会影响刷脏页的速度,例如在一个sql的执行过程需要先刷掉一个脏页,但如果innodb_flush_neighbors
是设置为1的话,它会在刷脏页的过程中检查“邻居”是否也是脏页,如果也是的话会连带一起刷到磁盘中,这样导致这次的sql执行时间会很长,所以我们可以将innodb_flush_neighbors
设置为0来避免。
更多详细内容建议阅读原文为什么我的MySQL会“抖”一下?