限制 Linux 后台刷新(脏页)

kor*_*man 29 linux performance hard-drive

Linux 上的后台刷新发生在有太多写入数据待处理(可通过 /proc/sys/vm/dirty_background_ratio 调整)或达到待处理写入超时(/proc/sys/vm/dirty_expire_centisecs)。除非达到另一个限制(/proc/sys/vm/dirty_ratio),否则可能会缓存更多写入的数据。进一步的写入将被阻止。

理论上,这应该创建一个后台进程写出脏页而不会干扰其他进程。实际上,它确实会干扰任何进行非缓存读取或同步写入的进程。糟糕。这是因为后台刷新实际上以 100% 的设备速度写入,此时任何其他设备请求都将被延迟(因为路上的所有队列和写入缓存都已填满)。

有没有办法限制刷新过程每秒执行的请求数量,或者以其他方式有效地优先考虑其他设备 I/O?

kor*_*man 22

在使用 sysbench 进行了大量基准测试之后,我得出了这个结论:

为了生存(性能方面)的情况

  • 一个邪恶的复制过程淹没了脏页
  • 并且存在硬件写缓存(也可能没有)
  • 每秒同步读取或写入 (IOPS) 至关重要

只需转储所有电梯、队列和脏页缓存。脏页的正确位置是在该硬件写缓存的 RAM 中。

尽可能低地调整dirty_ratio(或新的dirty_bytes),但要注意顺序吞吐量。在我的特殊情况下,15 MB 是最佳的 ( echo 15000000 > dirty_bytes)。

这与其说是解决方案,不如说是一种黑客攻击,因为现在千兆字节的 RAM 仅用于读取缓存而不是脏缓存。为了让脏缓存在这种情况下运行良好,Linux 内核后台刷新需要平均底层设备接受请求的速度并相应地调整后台刷新。不容易。


用于比较的规格和基准:

在将dd零写入磁盘时进行测试,sysbench 显示出巨大的成功,将 10 个线程 fsync 写入从 33 提高到 700 IOPS(空闲限制:1500 IOPS),将 16 kB 的 IOPS 和单线程从 8 提高到 400 IOPS。

在没有负载的情况下,IOPS 不受影响 (~1500),吞吐量略有降低(从 251 MB/s 到 216 MB/s)。

dd 称呼:

dd if=/dev/zero of=dumpfile bs=1024 count=20485672
Run Code Online (Sandbox Code Playgroud)

对于 sysbench,test_file.0 准备好不稀疏:

dd if=/dev/zero of=test_file.0 bs=1024 count=10485672
Run Code Online (Sandbox Code Playgroud)

sysbench 调用 10 个线程:

sysbench --test=fileio --file-num=1 --num-threads=10 --file-total-size=10G --file-fsync-all=on --file-test-mode=rndwr --max-time=30 --file-block-size=16384 --max-requests=0 run
Run Code Online (Sandbox Code Playgroud)

sysbench 调用一个线程:

sysbench --test=fileio --file-num=1 --num-threads=1 --file-total-size=10G --file-fsync-all=on --file-test-mode=rndwr --max-time=30 --file-block-size=16384 --max-requests=0 run
Run Code Online (Sandbox Code Playgroud)

较小的块大小显示出更大的数字。

--file-block-size=4096 1 GB 脏字节:

sysbench 0.4.12:  multi-threaded system evaluation benchmark

Running the test with following options:
Number of threads: 1

Extra file open flags: 0
1 files, 10Gb each
10Gb total file size
Block size 4Kb
Number of random requests for random IO: 0
Read/Write ratio for combined random IO test: 1.50
Calling fsync() after each write operation.
Using synchronous I/O mode
Doing random write test
Threads started!
Time limit exceeded, exiting...
Done.

Operations performed:  0 Read, 30 Write, 30 Other = 60 Total
Read 0b  Written 120Kb  Total transferred 120Kb  (3.939Kb/sec)
      0.98 Requests/sec executed

Test execution summary:
      total time:                          30.4642s
      total number of events:              30
      total time taken by event execution: 30.4639
      per-request statistics:
           min:                                 94.36ms
           avg:                               1015.46ms
           max:                               1591.95ms
           approx.  95 percentile:            1591.30ms

Threads fairness:
      events (avg/stddev):           30.0000/0.00
      execution time (avg/stddev):   30.4639/0.00
Run Code Online (Sandbox Code Playgroud)

--file-block-size=4096 有 15 MB 脏字节:

sysbench 0.4.12:  multi-threaded system evaluation benchmark

Running the test with following options:
Number of threads: 1

Extra file open flags: 0
1 files, 10Gb each
10Gb total file size
Block size 4Kb
Number of random requests for random IO: 0
Read/Write ratio for combined random IO test: 1.50
Calling fsync() after each write operation.
Using synchronous I/O mode
Doing random write test
Threads started!
Time limit exceeded, exiting...
Done.

Operations performed:  0 Read, 13524 Write, 13524 Other = 27048 Total
Read 0b  Written 52.828Mb  Total transferred 52.828Mb  (1.7608Mb/sec)
    450.75 Requests/sec executed

Test execution summary:
      total time:                          30.0032s
      total number of events:              13524
      total time taken by event execution: 29.9921
      per-request statistics:
           min:                                  0.10ms
           avg:                                  2.22ms
           max:                                145.75ms
           approx.  95 percentile:              12.35ms

Threads fairness:
      events (avg/stddev):           13524.0000/0.00
      execution time (avg/stddev):   29.9921/0.00
Run Code Online (Sandbox Code Playgroud)

--file-block-size=4096 在空闲系统上有 15 MB 脏字节:

sysbench 0.4.12:多线程系统评估基准

Running the test with following options:
Number of threads: 1

Extra file open flags: 0
1 files, 10Gb each
10Gb total file size
Block size 4Kb
Number of random requests for random IO: 0
Read/Write ratio for combined random IO test: 1.50
Calling fsync() after each write operation.
Using synchronous I/O mode
Doing random write test
Threads started!
Time limit exceeded, exiting...
Done.

Operations performed:  0 Read, 43801 Write, 43801 Other = 87602 Total
Read 0b  Written 171.1Mb  Total transferred 171.1Mb  (5.7032Mb/sec)
 1460.02 Requests/sec executed

Test execution summary:
      total time:                          30.0004s
      total number of events:              43801
      total time taken by event execution: 29.9662
      per-request statistics:
           min:                                  0.10ms
           avg:                                  0.68ms
           max:                                275.50ms
           approx.  95 percentile:               3.28ms

Threads fairness:
      events (avg/stddev):           43801.0000/0.00
      execution time (avg/stddev):   29.9662/0.00
Run Code Online (Sandbox Code Playgroud)

测试系统:

  • Adaptec 5405Z(带保护的 512 MB 写缓存)
  • 英特尔至强 L5520
  • 6 GiB RAM @ 1066 MHz
  • 主板 Supermicro X8DTN(5520 芯片组)
  • 12 个希捷 Barracuda 1 TB 磁盘
    • 10 在 Linux 软件 RAID 10
  • 内核 2.6.32
  • 文件系统 xfs
  • Debian 不稳定

总而言之,我现在确信这种配置在空闲、高负载甚至满负载情况下都能很好地处理数据库流量,否则这些流量会被顺序流量耗尽。顺序吞吐量高于两个千兆位链路无论如何都可以提供的吞吐量,因此稍微减少一点也没有问题。

  • 只是想说谢谢你让我走上正轨。XenServer 节点有一些类似的问题。原来是 PHP-FPM/APC 缓存导致脏页。调整 APC 缓存内存模型为我们解决了这个问题。DiskIO 的利用率从 20% 变为 0。 (2认同)