主要原因:就是此时MySQL在执行flush操作(将数据持久化到磁盘中)。
一个很形象的比喻:掌柜的记忆是内存,临时记账的黑板是redo log,账本是磁盘。
flush:就是将redo log中的内容更新到账本上,不一定是等店铺打样了才这么做。或者是将内存中脏页数据持久化到磁盘中,将其变为干净页或者释放其在内存中占有的空间。执行redo log操作和刷脏页不是同一个操作。
脏页:指的是存在内存中的数据页,但是还没有持久化到磁盘中(记录在redo log中,还没有记录在bin log中,有可能丢失)。
四种情况下MySQL会进行flush操作:
-
redo log满了,MySQL会停止所有更新,将redo log中的数据持久化后再接受数据更新操作—整个数据库停止更新(业务不能接受)—— 黑板记满了;
-
内存不够用了,将脏页写到磁盘并释放内存中该页的数据。(由于内存中存在干净页和脏页,MySQL是将最久没用过的数据页内存空间释放,所以不一定每次内存满了都刷脏页,可能只是释放干净页内存空间,实际中很普遍的现象)——掌柜的脑袋记不住了;
ps:
这里评论区有个好问题:如果是内存中取出最久没有被访问过的数据脏页,将其进行持久化,那么对应的是执行redo log中某个随机位置的sql语句来写入磁盘,这就违背了redo log将MySQL原来的随机io优化为顺序io的目的,会造成redo log空间的磁盘空间碎片以及分页与合并操作?解答是:在MySQL刷脏页的时候只是从磁盘中取出脏页中的数据到内存中,然后更新磁盘中的数据,再释放内存中的脏页空间,该过程redo log不会改变,当后续MySQL读取redo log的时候,读到已经被刷新过的脏页的记录时会跳过该记录,因此redo log仍然保证了MySQL数据更新的顺序性(呼应了文章开头说的redo log持久化和刷脏页持久化是两个不同的操作);
-
当系统运行负担不大的时候,MySQL一有空就会执行flush ——店铺营业中但没有新顾客;
-
MySQL关闭时——店铺打样后。
MySQL性能调优之刷脏页控制策略:
-
通过设置innodb_io_capacity参数来使得MySQL尽可能的调用主机的IO能力(避免出现数据库写入能力低,但主机此时IO压力小的情况),主要考虑内存中脏页占据的比例(不超过75%)以及redo log的占用率(计算复杂就不说了);
-
通过设置innodb_flush_neighbors为0(8.0以后默认为零),这个参数为1的话,在内存中刷脏页的时候,如果该脏页边上也是脏页,则会连带边上的脏页一起删除(所以可能本来只要刷一张脏页,结果连带刷了好几张脏页),固态硬盘时代完全不需要这种机制。