脏页刷入磁盘

脏页刷入磁盘

学习检测

  1. 什么是脏页?

  2. 什么情况触发刷脏页行为?

  3. 刷脏页行为对性能的消耗对比?

  4. InnoDB刷脏页的策略?影响磁盘刷脏页的因素?

  5. 刷新脏页速度的策略?

  6. 为什么不直接淘汰内存,加载数据的时候,直接用redo log来校验?

  7. 什么事刷脏页 连坐行为?

  8. 脏页发现标准?

总结

  1. 当内存中的数据和磁盘中的数据不一致的时候,就是”脏页”

  2. 触发刷脏页的情况

    内存满了

    redo log 满了

    服务正常关闭

    mysql空闲时候自己刷新

  3. 内存满了

当有新数据的查询,不在buffer pool中的时候,就需要申请一个新的page页,如果没有未使用page和干净的page就需要将”脏页”刷新到磁盘,如果查询的数据较多,刷脏页的行为事件较长

redo log 满了

redo log 满了的时候系统会停止所有的写操作,影响业务

​ redo log 满了需要将checkpoint 指针向前推进,writepoint 和 checkpoint之间的就是可以写的部分

服务关闭 和 mysql “空闲” 对性能没什么影响

  1. mysql 刷脏页的策略 innodb_io_capacity 和磁盘IO能力有关,设置合理值(每秒系统IO能力),影响刷脏页的因素有buffer pool最大脏页比例 redo log 空闲空间(越大)刷的越快

  2. max(脏页比例 or redo log 可写空间的最大值的比例) 来刷脏页

  3. 数据操作是在buffer pool ,第一种情况,如果在内存中,直接操作,返回结果,不在内存,读取磁盘,加载到内存,返回结果,如果有redo log 的话,就要和redo log 进行校验在返回,redo log 也是文件形式,并且是物理逻辑日志,会更消耗性能

  4. 脏页有可能有连坐,发现自己的邻居有脏页也会刷,控制参数是

    ​ innodb_flush_neighbors

  5. 每个数据页都有头部LSN,8字节,每次修改都会变大,对比LSN跟checkoutpoint中值小的,一定是干净页,checkoutpoint 的变大说明有新事物增加了,page 小的话说明内存页是操作
    在这里插入图片描述

buffer pool 参数

在这里插入图片描述

新数据写入和flush示意图

在这里插入图片描述

flush 触发情况

redo log 满了

InnoDB的redo log 写满了,这时候系统会停止所有的更新操作,把chekpoint往前推进(redo log 日志写入磁盘),redo log 留出空间继续写
在这里插入图片描述
checkpoint 可不是随便往前修改一下位置就可以的。比如图 2 中,把 checkpoint 位置从 CP 推进到 CP’,就需要将两个点之间的日志(浅绿色部分),对应的所有脏页都 flush 到磁盘上。之后,图中从 write pos 到 CP’之间就是可以再写入的 redo log 的区域。

系统内存满了

对应的就是系统内存不足。当需要新的内存页,而内存不够用的时候,就要淘汰一些数据页,空出内存给别的数据页使用。如果淘汰的是“脏页”,就要先将脏页写到磁盘。

mysql 认为系统空闲

当然,MySQL忙起来可是会很快就能把redo log 记满的,所以要合理地安排时间,即使是忙的时候,也要见缝插针地找时间,只要有机会就刷一点“脏页”。

mysql 正常关闭的情况

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

四种情况的性能对比

第三种,在mysql”空闲的时候”,这时系统没什么压力,而第四种是数据库本来就要关闭,这两种情况下,你不会太关注”性能问题”,所以这里,我们主要分析一下前两种场景下的问题

第一种是redo log 写满,要”flush脏页”,这种情况是InnoDB要尽量避免的,因为出现这种情况,整个系统就不能接受更新了,所有的更新都必须堵住,如果你从监控上看,这时候更新数为0

第二种是”内存不够用了,先将脏页写到磁盘”,这种情况是常态,InnoDB用缓存池(buffer pool)管理内存,缓冲池中页的状态有三种

第一种,还没使用的

第二种,使用了并且是干净的

第三种,使用了并且是脏页

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

而当要读入的数据页没有在内存的时候,就必须到申请池中去申请一个数据页,这时候只能把最久不使用的数据页从内存中淘汰掉;如果要淘汰的是一个干净页,就直接释放出来复用;但如果是脏页呢,就必须将脏页先刷到磁盘,变成干净页才能复用

所以,刷脏页是常态,但是出现以下情况时候,都会明显影响性能的

  1. 一个查询要淘汰的脏页个数较多,会导致查询的响应时间明显变长

  2. 日志写满,更新全部堵住,写性能跌为0,这种情况对敏感业务来说,是补鞥被接受的

所以InnoDB需要有控制脏页比例的机制,来避免上面情况

InnoDB 刷脏页的控制策略

innodb_io_capacity flush 刷磁盘能力(系统每秒I/O数)

InnoDB IO 能力
在这里插入图片描述

查看系统磁盘的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

影响磁盘刷脏页的因素

脏页比例

redo log写盘速度

刷新脏页速度策略

计算脏页比例和redo log 日志序号和当前之日序号差值,取最大值%进行刷盘操作

innodb_max_dirty_pages_pct

​ 脏页比例上限 默认值是75%

InnoDB会根据当前的脏页比例(假设为M),算出一个范围在0–100之间的数字

伪代码

F1(M)
{
 if M>=innodb_max_dirty_pages_pct then
   return 100;
 return 100*M/innodb_max_dirty_pages_pct;
}

InnoDB 每次写入的日志都有一个序号,当前写入的序号跟 checkpoint 对应的序号之间的差值,我们假设为 N。InnoDB 会根据这个 N 算出一个范围在 0 到 100 之间的数字,这个计算公式可以记为 F2(N)。F2(N) 算法比较复杂,你只要知道 N 越大,算出来的值越大就好了。

然后,根据上述算得的 F1(M) 和 F2(N) 两个值,取其中较大的值记为 R,之后引擎就可以按照 innodb_io_capacity 定义的能力乘以 R% 来控制刷脏页的速度。

上述的计算流程比较抽象,不容易理解,所以我画了一个简单的流程图。图中的 F1、F2 就是上面我们通过脏页比例和 redo log 写入速度算出来的两个值。
在这里插入图片描述
现在你知道了,InnoDB 会在后台刷脏页,而刷脏页的过程是要将内存页写入磁盘。所以,无论是你的查询语句在需要内存的时候可能要求淘汰一个脏页,还是由于刷脏页的逻辑会占用 IO 资源并可能影响到了你的更新语句,都可能是造成你从业务端感知到 MySQL“抖”了一下的原因

要尽量避免这种情况,你就要合理地设置 innodb_io_capacity 的值,并且平时要多关注脏页比例,不要让它经常接近 75%。其中,脏页比例是通过 Innodb_buffer_pool_pages_dirty/Innodb_buffer_pool_pages_total 得到的,具体的命令参考下面的代码:

mysql> select VARIABLE_VALUE into @a from global_status where VARIABLE_NAME = 'Innodb_buffer_pool_pages_dirty';
select VARIABLE_VALUE into @b from global_status where VARIABLE_NAME = 'Innodb_buffer_pool_pages_total';
select @a/@b;

为什么不直接淘汰内存,读磁盘和redo log merge 操作

数据操作数据页有两种状态 保证内存中或者磁盘中的数据某一个是最正确的

  • 数据在内存页中,内存中就是正确的结果,直接返回

  • 数据不在内存页,文件上的为正确的,读入内存在处理,

如果直接淘汰内部存,数据页不在内存,读磁盘到内存,然后在和redo log 进行merge过程,会增加性能消耗,不能保证内存是最新的

刷脏页”连坐"控制

innodb_flush_neighbors

​ 值为1标识连坐刷脏页

​ 0位只刷自己的

一旦一个查询请求需要在执行过程中先 flush 掉一个脏页时,这个查询就可能要比平时慢了。而 MySQL 中的一个机制,可能让你的查询会更慢:在准备刷一个脏页的时候,如果这个数据页旁边的数据页刚好是脏页,就会把这个“邻居”也带着一起刷掉;而且这个把“邻居”拖下水的逻辑还可以继续蔓延,也就是对于每个邻居数据页,如果跟它相邻的数据页也还是脏页的话,也会被放到一起刷。

在 InnoDB 中,innodb_flush_neighbors 参数就是用来控制这个行为的,值为 1 的时候会有上述的“连坐”机制,值为 0 时表示不找邻居,自己刷自己的。

找“邻居”这个优化在机械硬盘时代是很有意义的,可以减少很多随机 IO。机械硬盘的随机 IOPS 一般只有几百,相同的逻辑操作减少随机 IO 就意味着系统性能的大幅度提升。而如果使用的是 SSD 这类 IOPS 比较高的设备的话,我就建议你把 innodb_flush_neighbors 的值设置成 0。因为这时候 IOPS 往往不是瓶颈,而“只刷自己”,就能更快地执行完必要的刷脏页操作,减少 SQL 语句响应时间。

在 MySQL 8.0 中,innodb_flush_neighbors 参数的默认值已经是 0 了

脏页发现机制

每个数据页头部有LSN,8字节,每次修改都会变大。

对比这个LSN跟checkpoint 的LSN,比checkpoint小的一定是干净页

因为 事务都要写redo log ,比redo log checkpoint值小的代表还没有被写入东西

发布了48 篇原创文章 · 获赞 31 · 访问量 4558

猜你喜欢

转载自blog.csdn.net/qq_39787367/article/details/103823959