生成大量脏页阻碍同步写入

生成大量脏页阻碍同步写入

我们有一些进程在后台执行大文件的写入。我们希望这些进程对其他进程的影响最小。

这是在 SLES11 SP4 上进行的测试。该服务器拥有大量内存,因此可以创建 4GB 的脏页。

> dd if=/dev/zero of=todel bs=1048576 count=4096
4096+0 records in
4096+0 records out
4294967296 bytes (4.3 GB) copied, 3.72657 s, 1.2 GB/s
> dd if=/dev/zero of=zer oflag=sync bs=512 count=1  
1+0 records in
1+0 records out
512 bytes (512 B) copied, 16.6997 s, 0.0 kB/s

real    0m16.701s
user    0m0.000s
sys     0m0.000s
> grep Dirty /proc/meminfo
Dirty:           4199704 kB

这是我目前的调查:

  • SLES11 SP4(3.0.101-63)
  • 类型 ext3 (rw,nosuid,nodev,noatime)
  • 截止期限调度器
  • 当时可回收内存超过 120GB
  • dirty_ratio 设置为 40%,dirty_background_ratio 设置为 10%,30 秒过期,5 秒回写

以下是我的问题:

  • 测试结束时有 4GB 脏内存,我得出结论,上面的测试中没有调用 IO 调度程序。对吗?
  • 由于缓慢持续存在第一个 dd 完成,我得出结论,这个问题与内核分配内存或当 dd 填充其缓冲区时发生的任何“写时复制”无关(dd 总是从同一个缓冲区写入)。
  • 有没有办法深入调查被阻止的内容?有什么有趣的计数器值得关注?对争用来源有什么想法吗?
  • 我们正在考虑降低 dirty_ratio 值,或者以同步模式执行第一个 dd。还有其他需要调查的方向吗?将第一个 dd 设置为同步有什么缺点吗?我担心它会优先于执行异步写入的其他“合法”进程。

也可以看看

https://www.novell.com/support/kb/doc.php?id=7010287

限制 Linux 后台刷新(脏页)

https://stackoverflow.com/questions/375576​​5/what-posix-fadvise-args-for-sequence-file-write/3756466?sgp=2#3756466

http://yarchive.net/comp/linux/dirty_limits.html


编辑:

有一个扩展2文件系统。在此设备上,完全没有冻结!唯一遇到的性能影响发生在刷新脏页期间,其中同步调用可能需要长达 0.3 秒,与我们使用 ext3 文件系统时遇到的情况相差甚远。


编辑2:

按照@Matthew Ife 的评论,我尝试进行同步写入,在没有 O_TRUNC 的情况下打开文件,你不会相信结果!

> dd if=/dev/zero of=zer oflag=sync bs=512 count=1
> dd if=/dev/zero of=todel bs=1048576 count=4096
> dd if=/dev/zero of=zer oflag=sync bs=512 count=1 conv=notrunc
1+0 records in
1+0 records out
512 bytes (512 B) copied, 0.000185427 s, 2.8 MB/s

dd 使用参数打开文件:

open("zer", O_WRONLY|O_CREAT|O_TRUNC|O_SYNC, 0666) = 3

使用 notrunc 选项进行更改,现在

open("zer", O_WRONLY|O_CREAT|O_SYNC, 0666) = 3

并且同步写入立即完成!

嗯,它并不完全满足我的用例(我正在做一个 msync这种时尚。但是我现在能够追踪 write 和 msync 的不同之处!


最后编辑:我不敢相信我遇到了这个: https://www.novell.com/support/kb/doc.php?id=7016100

事实上在 SLES11 下 dd 是使用以下方式打开文件

open("zer", O_WRONLY|O_CREAT|O_DSYNC, 0666) = 3

并且 O_DSYNC == O_SYNC!

结论:

对于我的用例我应该使用

dd if=/dev/zero of=zer oflag=dsync bs=512 count=1 conv=notrunc

在 SLES11 下,无论 strace 说了什么,运行 oflag=sync 实际上都会运行 oflag=dsync。

答案1

有几件事我很想知道结果。

  1. 首先创建大文件,fallocate然后写入其中。

  2. 将 dirty_background_bytes 设置为低得多(例如 1GiB)并使用 CFQ 作为调度程序。请注意,在此测试中,在大型运行的中间运行小型运行可能是一种更好的表现。

因此,对于选项 1,您可能会发现您避免了所有语义data=ordered,因为块分配已经完成(并且很快),因为它是通过预先分配的fallocate,并且元数据是在写入之前设置的。测试这是否真的如此会很有用。不过我有信心它会提高性能。

对于选项 2,您可以多使用 ionice。Deadline 明显比 CFQ 更快,尽管 CFQ 尝试按进程组织 IO,因此您会发现它能在每个进程中更好地共享 IO。

我在某处读到(现在找不到来源)dirty_background_ratio 将阻止对单个提交进程的写入(实际上使大进程变慢)以防止一个进程使所有其他进程都挨饿。鉴于我现在能找到的有关该行为的信息很少,我对它能否奏效不太有信心。

哦:我应该指出,这fallocate取决于范围,并且您需要使用 ext4。

答案2

我正在回答我自己的问题,但如果有人能提出更好的建议,我将非常感激:)

测试结束时有 4GB 脏内存,我得出结论,上面的测试中没有调用 IO 调度程序。对吗?

这完全是错误的。脏内存量并不是一个好的指标。只需运行 iostat 并检查在 dd oflag=sync 运行时是否发生大量写入,就可以轻松证明这一点。

有没有办法深入调查被阻止的内容?有什么有趣的计数器值得关注吗?

perf record -e 'jbd:*' -e 'block:*' -ag

对于较新的内核,用 jbd2 替换 jbd。

您对争论的根源有什么想法吗?

事实上对于具有数据=有序,日志线程负责刷新磁盘上的数据。刷新按写入的顺序进行。刷新频率可以使用犯罪挂载文件系统时的选项。

一个有趣的实验:使用 commit=60 挂载文件系统并禁用写回线程。运行第一个 dd 时,它在 2 秒内完成,并且 iostat 显示没有生成任何 IO!

当使用 oflag=sync 运行第二个 dd 时,第一个 dd 生成的所有 IO 都将刷新到磁盘。

我们正在考虑要么降低 dirty_ratio 值,要么以同步模式执行第一个 dd。

记录显示这两种解决方案都取得了良好的效果。另一个好主意是将这些大文件放在单独的文件系统上(可能使用 data=writeback 安装)

这与 SLES11 或更早的内核没有特别联系。我尝试过的所有内核都出现了同样的行为。

答案3

这是 ext3 的预期行为:在此文件系统上,同步写入会阻塞,直到全部之前的脏块被写入磁盘。这正是开发人员在过去几年中完全跳过任何同步写入的确切原因。

在这方面,Ext4、XFS、ZFS 和 BTRFS 都表现得更好。考虑到 ext4 或多或少是 ext3 的替代品,你真的应该升级你的文件系统。

相关内容