分析高分页率

这些是粗略的笔记。
一个服务团队正在调试一个性能问题,并注意到它与高分页率相吻合。我被要求提供帮助,并使用了各种 Linux 性能工具来解决这个问题,包括使用 eBPF 和 Ftrace 的工具。这是一篇粗略的帖子,用于分享使用这些工具的这个古老但很好的案例研究,并帮助证明它们的进一步发展。没有编辑、拼写检查或评论。大部分是截图。

  1. 问题陈述
    微服务管理和处理大文件,包括对它们进行加密,然后将它们存储在 S3 上。问题是大文件(例如 100 GB)似乎需要很长时间才能上传。小时。较小的文件(如 40 GB)相对较快,只需几分钟。
    云范围的监控工具 Atlas 显示了较大文件上传的高分页率:

蓝色是pageins(page ins)。Pageins 是一种磁盘 I/O,其中从磁盘读取一页内存,对于许多工作负载来说是正常的。
您也许可以仅从问题陈述中猜出问题所在。

# iostat -xz 1
Linux 4.4.0-1072-aws (xxx)  12/18/2018  _x86_64_    (16 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
           5.03    0.00    0.83    1.94    0.02   92.18

Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
xvda              0.00     0.29    0.21    0.17     6.29     3.09    49.32     0.00   12.74    6.96   19.87   3.96   0.15
xvdb              0.00     0.08   44.39    9.98  5507.39  1110.55   243.43     2.28   41.96   41.75   42.88   1.52   8.25

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          14.81    0.00    1.08   29.94    0.06   54.11

Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
xvdb              0.00     0.00  745.00    0.00 91656.00     0.00   246.06    25.32   33.84   33.84    0.00   1.35 100.40

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          14.86    0.00    0.89   24.76    0.06   59.43

Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
xvdb              0.00     0.00  739.00    0.00 92152.00     0.00   249.40    24.75   33.49   33.49    0.00   1.35 100.00

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          14.95    0.00    0.89   28.75    0.06   55.35

Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
xvdb              0.00     0.00  734.00    0.00 91704.00     0.00   249.87    24.93   34.04   34.04    0.00   1.36 100.00

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          14.54    0.00    1.14   29.40    0.06   54.86

Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
xvdb              0.00     0.00  750.00    0.00 92104.00     0.00   245.61    25.14   33.37   33.37    0.00   1.33 100.00

^C

我特别关注 r_await 列:读取的平均等待时间(以毫秒为单位)。阅读通常有应用程序在等待它们;写入可能不会(回写缓存)。33 ms 的 r_wait 有点高,可能是由于排队 (avgqu-sz)。它们是较大的 I/O,大约 128 KB(将 rkB/s 除以 r/s)。

  1. biolatency

bcc,这个 eBPF 工具显示了磁盘 I/O 的延迟直方图。我正在运行它以防平均值隐藏异常值,这可能是设备问题:

# /usr/share/bcc/tools/biolatency -m
Tracing block device I/O... Hit Ctrl-C to end.
^C
     msecs               : count     distribution
         0 -> 1          : 83       |                                        |
         2 -> 3          : 20       |                                        |
         4 -> 7          : 0        |                                        |
         8 -> 15         : 41       |                                        |
        16 -> 31         : 1620     |*******                                 |
        32 -> 63         : 8139     |****************************************|
        64 -> 127        : 176      |                                        |
       128 -> 255        : 95       |                                        |
       256 -> 511        : 61       |                                        |
       512 -> 1023       : 93       |                                        |

这看起来还不错。大多数 I/O 在 16 到 127 毫秒之间。一些异常值达到 0.5 到 1.0 秒的范围,但同样,在早期的 iostat(1) 输出中可以看到这里有相当多的排队。我不认为这是设备问题。我认为这是工作量。

  1. bitesize

因为我认为这是一个工作负载问题,所以我想更好地了解 I/O 大小,以防出现一些奇怪的情况:

# /usr/share/bcc/tools/bitesize 
Tracing... Hit Ctrl-C to end.
^C
Process Name = java
     Kbytes              : count     distribution
         0 -> 1          : 0        |                                        |
         2 -> 3          : 0        |                                        |
         4 -> 7          : 0        |                                        |
         8 -> 15         : 31       |                                        |
        16 -> 31         : 15       |                                        |
        32 -> 63         : 15       |                                        |
        64 -> 127        : 15       |                                        |
       128 -> 255        : 1682     |****************************************|

正如 iostat(1) 输出所预期的那样,I/O 主要位于 128 到 255 KB 的存储桶中。这里没有什么奇怪的。

  1. free

同样来自 60 秒清单:

# free -m
              total        used        free      shared  buff/cache   available
Mem:          64414       15421         349           5       48643       48409
Swap:             0           0           0

剩下的内存不多了,349 MB,但更有趣的是缓冲区/页面缓存中的数量:48,643 MB(48 GB)。这是一个 64 GB 的内存系统,48 GB 位于页面缓存(文件系统缓存)中。

连同问题陈述中的数字,这给了我一个理论:100-Gbyte 文件是否破坏了页面缓存,而 40-Gbyte 文件适合?

  1. 缓存数据

cachestat是我开发的一个实验性工具,它使用 Ftrace 并已被移植到 bcc/eBPF。它显示页面缓存的统计信息:

# /apps/perf-tools/bin/cachestat
Counting cache functions... Output every 1 seconds.
    HITS   MISSES  DIRTIES    RATIO   BUFFERS_MB   CACHE_MB
    1811      632        2    74.1%           17      48009
    1630    15132       92     9.7%           17      48033
    1634    23341       63     6.5%           17      48029
    1851    13599       17    12.0%           17      48019
    1941     3689       33    34.5%           17      48007
    1733    23007      154     7.0%           17      48034
    1195     9566       31    11.1%           17      48011
[...]

这显示了许多缓存未命中,命中率在 6.5% 到 74% 之间变化。我通常喜欢在上世纪 90 年代看到这一点。这就是“缓存破坏”。100 GB 的文件不适合 48 GB 的页面缓存,所以我们有很多页面缓存未命中,会导致磁盘 I/O 和相对较差的性能。

最快的解决方法是迁移到一个能够容纳 100 GB 文件的更大内存实例。开发人员还可以在考虑内存限制的情况下重新编写代码以提高性能(例如,处理文件的部分内容,而不是对整个文件进行多次遍历)。

最快的解决方法是迁移到一个能够容纳 100 GB 文件的更大内存实例。开发人员还可以在考虑内存限制的情况下重新编写代码以提高性能(例如,处理文件的部分内容,而不是对整个文件进行多次遍历)。

  1. 小文件测试

为了进一步确认,我为 32 GB 的文件上传收集了相同的输出。
cachestat 显示了 ~100% 的缓存命中率:

# /apps/perf-tools/bin/cachestat
Counting cache functions... Output every 1 seconds.
    HITS   MISSES  DIRTIES    RATIO   BUFFERS_MB   CACHE_MB
   61831        0      126   100.0%           41      33680
   53408        0       78   100.0%           41      33680
   65056        0      173   100.0%           41      33680
   65158        0       79   100.0%           41      33680
   55052        0      107   100.0%           41      33680
   61227        0      149   100.0%           41      33681
   58669        0       71   100.0%           41      33681
   33424        0       73   100.0%           41      33681
^C

这种较小的大小允许服务完全从内存中处理文件(不管它需要多少遍),而无需从磁盘重新读取它。

free(1) 显示它适合页面缓存:

# free -m
              total        used        free      shared  buff/cache   available
Mem:          64414       18421       11218           5       34773       45407
Swap:             0           0           0

正如预期的那样,iostat(1) 显示的磁盘 I/O 很少:

# iostat -xz 1
Linux 4.4.0-1072-aws (xxx)  12/19/2018  _x86_64_    (16 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          12.25    0.00    1.24    0.19    0.03   86.29

Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
xvda              0.00     0.32    0.31    0.19     7.09     4.85    47.59     0.01   12.58    5.44   23.90   3.09   0.15
xvdb              0.00     0.07    0.01   11.13     0.10  1264.35   227.09     0.91   82.16    3.49   82.20   2.80   3.11

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          57.43    0.00    2.95    0.00    0.00   39.62

Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          53.50    0.00    2.32    0.00    0.00   44.18

Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
xvdb              0.00     0.00    0.00    2.00     0.00    19.50    19.50     0.00    0.00    0.00    0.00   0.00   0.00

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          39.02    0.00    2.14    0.00    0.00   58.84

Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
[...]
]
  1. 最后的笔记
    cachestat 是这里的杀手级工具,但我应该强调它仍然是实验性的。我为 Ftrace 编写了它,其约束条件是它必须是低开销的并且仅使用 Ftrace 函数分析器。正如我在波多黎各的LSFMMBPF 2019 主题演讲中提到的那样,Linux mm 内核工程师在场,我认为 cachestat 统计数据是如此普遍,以至于它们应该在 /proc 中而不需要我的实验工具。他们指出了正确提供它们的挑战,我认为任何强大的解决方案都需要他们的帮助和专业知识。我希望这个案例研究有助于说明为什么它是有用的并且值得付出努力。
    直到内核确实支持页面缓存统计信息(可能永远不会:它们是热路径,所以添加计数器不是免费的)我们可以使用我的 cachestat 工具,即使它确实需要经常维护才能继续工作。

转载翻译自: https://www.brendangregg.com