这篇文章上次修改于 763 天前,可能其部分内容已经发生变化,如有疑问可询问作者。

1 缓存命中率

缓存命中率,是指通过缓存获取数据的次数占所有数据请求次数的比例。

cachestat 提供了整个操作系统缓存的读写命中情况。

cachetop 提供了每个进程的缓存命中情况。

安装好 bcc-tools 后(bcc-tools 需要内核版本为 4.1 或者更新的版本),运行 cachestat:

$ cachestat 1 3
   TOTAL   MISSES     HITS  DIRTIES   BUFFERS_MB  CACHED_MB
       2        0        2        1           17        279
       2        0        2        1           17        279
       2        0        2        1           17        279 
  • TOTAL:总的 I/O 次数。
  • MISSES:缓存未命中的次数。
  • HITS:缓存命中的次数。
  • DIRTIES:新增到缓存中的脏页数。

运行 cachetop:

$ cachetop
11:58:50 Buffers MB: 258 / Cached MB: 347 / Sort: HITS / Order: ascending
PID      UID      CMD              HITS     MISSES   DIRTIES  READ_HIT%  WRITE_HIT%
   13029 root     python                  1        0        0     100.0%       0.0%
  • READ_HIT:读命中率。
  • WRITE_HIT:写命中率。

2 案例

当发现程序读文件变慢时,可查看缓存命中情况

# 每隔5秒刷新一次数据
$ cachetop 5 
16:39:18 Buffers MB: 73 / Cached MB: 281 / Sort: HITS / Order: ascending
PID      UID      CMD              HITS     MISSES   DIRTIES  READ_HIT%  WRITE_HIT%
   21881 root     app                  1024        0        0     100.0%       0.0% 

可以看到命中率是 100%。命中次数为 1024,内存以页为单位进行管理,一页大小为 4K,所以 5s 内命中的缓存为 1024 * 4K,再除以 5s,得到每秒的读缓存为 0.8MB,速度较低,可能是没有充分利用缓存。

判断程序是否使用了直接 I/O,需要查看系统调用,可使用 strace:

# strace -p $(pgrep app)
strace: Process 4988 attached
restart_syscall(<\.\.\. resuming interrupted nanosleep \.\.\.>) = 0
openat(AT_FDCWD, "/dev/sdb1", O_RDONLY|O_DIRECT) = 4
mmap(NULL, 33558528, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f448d240000
read(4, "8vq\213\314\264u\373\4\336K\224\25@\371\1\252\2\262\252q\221\n0\30\225bD\252\266@J"\.\.\., 33554432) = 33554432
write(1, "Time used: 0.948897 s to read 33"\.\.\., 45) = 45
close(4)                                = 0

可以看到,程序使用了 openstat 打开磁盘分区 /dev/sdb1,并且传入的标志为 O_RDONLY|O_DIRECT,O_DIRECT 会绕过系统缓存。修改后继续验证,读文件速率变快。

cachestop 没有把直接 I/O 算进来。

参考

倪朋飞. Linux 性能优化实战.