Linux的磁盘缓存和刷脏页

Posted LinuxProbe19

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux的磁盘缓存和刷脏页相关的知识,希望对你有一定的参考价值。

导读 无论您选择哪种路线,您都应该始终收集硬数据来支持您的更改,并帮助您确定是正在改进还是使事情变得更糟。在这种情况下,您可以从许多不同的位置获取数据,包括应用程序本身、/proc/vmstat、/proc/meminfo、iostat、vmstat 以及 /proc/sys/vm 中的许多内容。

我们讨论了Linux客户机上的内存如何用于操作系统本身(内核,缓冲区等),应用程序以及文件缓存。文件缓存是一项重要的性能改进,在大多数情况下,读取缓存是一个明显的胜利,与直接使用 RAM 的应用程序相比,这是平衡的。写入缓存更棘手。Linux 内核将磁盘写入暂存到缓存中,并随着时间的推移将它们异步刷新到磁盘。这在加速磁盘 I/O 方面有很好的效果,但存在风险。当数据未写入磁盘时,丢失数据的可能性会增加。也有可能大量 I/O 也会使缓存不堪重负。是否曾经一次将大量数据写入磁盘,并在尝试处理所有这些数据时看到系统上出现大量暂停?这些暂停是缓存决定有太多数据要异步写入(作为非阻塞后台操作,让应用程序进程继续)并切换到同步写入(阻塞并使进程等待 I/O 提交到磁盘)的结果。当然,文件系统还必须保留写入顺序,因此当它开始同步写入时,它首先必须转储缓存。因此长时间停顿。好消息是,这些都是可控的选项,根据您的工作负载和数据,您可以决定如何设置它们。一起来看看:

$ sysctl -a | grep dirty
vm.dirty_background_ratio = 10
vm.dirty_background_bytes = 0
vm.dirty_ratio = 20
vm.dirty_bytes = 0
vm.dirty_writeback_centisecs = 500
vm.dirty_expire_centisecs = 3000

​vm.dirty_background_ratio是在 pdflush/flush/kdmflush 后台进程启动将其写入磁盘之前,可以用“脏”页(仍需要写入磁盘的内存页)填充的空闲内存和缓存和的百分比。我的例子是 10%,所以如果我的虚拟服务器有 32 GB 的内存,那就是 3.2 GB 的数据,可以在完成某些操作之前驻留在 RAM 中。vm.dirty_ratio 是在必须将所有内容提交到磁盘之前可以用脏页填充的绝对最大系统内存量。当系统到达此时,所有新的 I/O 块,直到脏页已写入磁盘。这通常是长时间 I/O 暂停的根源,但可以防止在内存中不安全地缓存过多数据。vm.dirty_background_bytes和vm.dirty_bytes是指定这些参数的另一种方法。如果设置_bytes版本,则_ratio版本将变为 0,反之亦然。vm.dirty_expire_centisecs是某些内容在需要写入之前可以在缓存中存在多长时间。在本例中为 30 秒。当 pdflush/flush/kdmflush 进程启动时,它们将检查脏页的年龄,如果它早于此值,它将异步写入磁盘。由于在内存中保存脏页是不安全的,这也是防止数据丢失的一种保护措施。vm.dirty_writeback_centisecs是 pdflush/flush/kdmflush 进程唤醒并检查是否需要完成工作的频率。​您还可以在 /proc/vmstat 中查看有关页面缓存的统计信息:

$ cat /proc/vmstat | egrep "dirty|writeback"
nr_dirty 878
nr_writeback 0
nr_writeback_temp 0

就我而言,我有 878 个脏页等待写入磁盘。方法 1:减少缓存与计算机世界中的大多数事情一样,如何调整这些取决于您要做什么。在许多情况下,我们有快速磁盘子系统,它们有自己的大型电池备份 NVRAM 缓存,因此将内容保留在操作系统页面缓存中是有风险的。让我们尝试更及时地将 I/O 发送到阵列,并减少我们的本地操作系统(借用服务行业的一句话)“陷入困境”的可能性。为此,我们通过向 /etc/sysctl.conf 添加新数字并使用 “sysctl –p” 重新加载来降低vm.dirty_background_ratio和vm.dirty_ratio:

vm.dirty_background_ratio = 5
vm.dirty_ratio = 10

这是虚拟机以及基于 Linux 的虚拟机管理程序的典型方法。我不建议将这些参数设置为零,因为某些后台 I/O 可以将应用程序性能与磁盘阵列和 SAN 上短时间的高延迟(“峰值”)分离。方法 2:增加缓存在某些情况下,显著提高缓存会对性能产生积极影响。在这些情况下,Linux 来宾上包含的数据并不重要并且可能会丢失,并且通常是应用程序重复或以可重复的突发方式写入相同的文件。从理论上讲,通过允许内存中存在更多的脏页,您将在缓存中一遍又一遍地重写相同的块,并且只需要每隔一段时间对实际磁盘进行一次写入。为此,我们提高了参数:

vm.dirty_background_ratio = 50
vm.dirty_ratio = 80

有时人们还会增加 vm.dirty_expire_centisecs 参数以允许缓存中更多时间。除了数据丢失的风险增加之外,如果缓存已满并需要转储,还面临长时间 I/O 暂停的风险,因为在大型 VM 上,缓存中会有大量数据。方法3:双向在某些情况下,系统必须处理不频繁的突发流量到慢速磁盘(整点顶部的批处理作业、午夜、写入 Raspberry Pi 上的 SD 卡等)。在这种情况下,一种方法可能是允许将所有写入 I/O 存放在缓存中,以便后台刷新操作可以随着时间的推移异步处理它:

vm.dirty_background_ratio = 5
vm.dirty_ratio = 80

在这里,后台进程在达到 5% 的上限时将立即开始写入,但系统不会强制同步 I/O,直到它达到 80% 满。从那里,您只需调整系统RAM的大小,vm.dirty_ratio就可以消耗所有写入的数据。同样,磁盘上的数据一致性需要权衡,这转化为数据风险。购买 UPS 并确保您可以在 UPS 断电之前转储缓存。:)无论您选择哪种路线,您都应该始终收集硬数据来支持您的更改,并帮助您确定是正在改进还是使事情变得更糟。在这种情况下,您可以从许多不同的位置获取数据,包括应用程序本身、/proc/vmstat、/proc/meminfo、iostat、vmstat 以及 /proc/sys/vm 中的许多内容。可以看到这个脏页配置也是一种艺术,需要根据实际情况下调整,注意vm.dirty_background_ratio配置的比例是缓存和空闲内存的比例,vm.dirty_ratio是总内存的比例。

本文地址:https://www.linuxprobe.com/linux-disk-io.html

mysql的刷脏

脏页:内存数据页跟磁盘上数据页内容不一致,将内存页称为脏页

干净页:内存数据写入磁盘后,内存页跟磁盘页数据一致,称内存页为干净页

 

刷脏页的俩种情况:

1、redo log满了,这时候系统不能再接受更新了,所有的更新都必须堵住,这时候的更新数为0

2、内纯不够用了,要先将脏页写到磁盘。这种情况是常态

innodb用缓冲池(buffer pool)管理内存,缓冲池中的内存页有三种状态:

还有没使用的;innodb的策略是尽量使用内存,对于一个长时间运行的数据库来说,未被使用的内存页很少

使用了并且是干净页

使用了并且是脏页

3、数据库空余的时候

4、mysql正常关闭的时候

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

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

一个查询要淘汰的脏页个数太多,会导致查询的响应时间明显变长   #查询会导致从redo log 将数据flush到磁盘上,然后返回查询结果

日志写满,需要flush到磁盘,这时候更新全部堵住,写性能跌为 0,这种情况对敏感业务来说,是不能接受的。

 

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

innodb_io_capacity  #告诉innodb磁盘的IO性能为多少,一般设置为磁盘的IOPS

磁盘的 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

 

innodb_io_capacity  设置过小导致的问题: 

数据库写入速度很慢,TPS很低,IO压力却很小,原因是innodb以为磁盘性能就那么小,刷脏比生产脏页还慢,脏页积累,影响查询更新性能

 

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 了。

 

以上是关于Linux的磁盘缓存和刷脏页的主要内容,如果未能解决你的问题,请参考以下文章

mysql的刷脏

Mysql的刷脏页问题

mysql之抖动

Mysql - 脏页

MySQL的WAL(Write-Ahead Logging)机制

大厂Java面试总结+详细解答!极其重要