我们有一些进程在后台执行大文件的写入。我们希望这些进程对其他进程的影响最小。
这是在 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
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
有几件事我很想知道结果。
首先创建大文件,
fallocate
然后写入其中。将 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 的替代品,你真的应该升级你的文件系统。