Mysql学习专栏之六(浅析Mysql中的脏页刷新)

一. InnoDB 刷脏页流程

InnoDB 在处理更新语句的时候,只做了写日志这一个磁盘操作。

这个日志叫作 redo log(重做日志),在 更新内存写完 redo log 后,就返回给客户端,本次更新成功。

但实际上,对应的数据没有被更新到磁盘,系统总要找时间把数据写入磁盘,这个写入过程的术语就是 flush

当内存数据页跟磁盘数据页内容不一致的时候,我们称这个内存页为“脏页”。内存数据写入到磁盘后,内存和磁盘上的数据页的内容就一致了,称为“干净页”。

写入过程如下图所示:
在这里插入图片描述
那么,什么情况会引发数据库的 flush 过程呢?

第一种情况是,InnoDB 的 redo log 写满了

这时候系统会停止所有更新操作,把 checkpoint 往前推进,redo log 留出空间才可以继续写。

第二种情况是,系统内存不足

当需要新的内存页,而内存不够用的时候,就要淘汰一些数据页,空出内存给别的数据页使用。

如果淘汰的是“脏页”,就要先将脏页写到磁盘。

那么,为什么我们不能直接把内存淘汰掉,下次需要请求的时候,从磁盘读入数据页,然后拿 redo log 出来直接应用?

这里其实是从性能考虑的,这样设计的效率最高。如果刷脏页一定会写盘,就保证了每个数据页有两种状态:

  • 内存里存在,内存里就肯定是正确的结果,直接返回;
  • 内存里没有数据,就可以肯定数据文件上是正确的结果,读入内存后返回。

第三种情况是,MySQL 认为系统“空闲”的时候

系统会见缝插针地找时间,只要有机会就刷一点“脏页”。

第四种情况是,MySQL 正常关闭的时候

此时,MySQL 会把内存的脏页都flush 到磁盘上,这样下次 MySQL 启动的时候,就可以直接从磁盘上读数据,启动速度会很快。

上面四种场景对性能的影响是不一样的,可以看出,后两种情况对系统的影响较小,所以我们主要来分析一下前两种情况下的性能问题。

第一种情况是 InnoDB 要尽量避免的。因为此时整个系统都不能再接受更新了,如果从监控上看,这时候更新数会跌为0。

第二种情况其实是常态。InnoDB 用 缓冲池(buffer pool) 管理内存,其中的内存页有三种状态:

  1. 未使用的页;
  2. 已使用的干净页;
  3. 已使用的脏页;

InnoDB 的策略是尽量使用内存,因此对于一个长时间运行的库来说,未被使用的页面很少。

而当要读入的数据页没有在内存的时候,就必须到缓冲池中申请一个数据页。

这时候只能把最久不使用的数据页从内存中淘汰掉:如果要淘汰的是一个干净页,就直接释放出来复用;如果是脏页,就必须将脏页先刷到磁盘,变成干净页后才能复用。

所以,刷脏页虽然是常态,但是出现以下这两种情况,都会明显影响性能:

  1. 一个查询要淘汰的脏页个数太多,会导致查询的响应时间明显变长;
  2. 日志写满,更新全部堵住,写性能跌为 0,这种情况对敏感业务来说,是不能接受的。

所以,InnoDB 需要有控制脏页比例的机制,来尽量避免上面的这两种情况。

二. InnoDB 刷脏页的控制参数

innodb_io_capacity: 表示InnoDB 所在主机的 IO 能力。

建议设置成磁盘的 IOPS,这样 InnoDB 才能知道需要全力刷脏页的时候,可以刷多快。

如果这个参数设置过小,会导致刷脏页刷得特别慢,甚至比脏页生成的速度还慢,这样就会造成脏页累积,影响查询和更新的性能。

innodb_max_dirty_pages_pct: 表示脏页比例上限,默认值是75%。

脏页比例是通过Innodb_buffer_pool_pages_dirty/Innodb_buffer_pool_pages_total 计算得到的,我们平时需要多关注脏页比例,不要让它经常接近 75%。

innodb_flush_neighbors:在刷新数据时,是否刷新邻居数据页。

在准备刷一个脏页的时候,如果这个数据页旁边的数据页刚好是脏页,就会把这个“邻居”也带着一起刷掉;而且这个把“邻居”拖下水的逻辑还可以继续蔓延,也就是对于每个邻居数据页,如果跟它相邻的数据页也还是脏页的话,也会被放到一起刷。

innodb_flush_neighbors值为 1 的时候会有上述的“连坐”机制,值为 0 时表示不找邻居,自己刷自己的。

找“邻居”这个优化在机械硬盘时代是很有意义的,可以减少很多随机 IO。机械硬盘的随机 IOPS 一般只有几百,相同的逻辑操作减少随机 IO 就意味着系统性能的大幅度提升。

而如果使用的是 SSD 这类 IOPS 比较高的设备的话,建议把innodb_flush_neighbors 的值设置成 0。因为这时候 IOPS 往往不是瓶颈,而“只刷自己”,就能更快地执行完必要的刷脏页操作,减少 SQL 语句响应时间。

猜你喜欢

转载自blog.csdn.net/j1231230/article/details/107160553