利用缓存来提升程序的运行效率,即命中率越高,表示使用缓存带来的收益越高,应用程序的性能也就越好。所谓缓存命中率,是指直接通过缓存获取数据的请求次数,占所有数据请求次数的百分比。 如何查询缓存命中情况呢?
介绍两个工具;
- cachestat 提供了整个操作系统缓存的读写命中情况。
- cachetop 提供了每个进程的缓存命中情况。
使用这两个工具首先需要安装bcc软件包,要想安装bcc-tools 需要升级内核版本为 4.1 或者更新的版本, 然后yum install -y bcc-tools。
1.案例分析
运行应用及查看日志
$ docker run --privileged --name=app -itd feisky/app:io-direct
$ docker logs -f app
Reading data from disk /dev/sda2 with buffer size 33554432
Time used: 0.448797 s to read 33554432 bytes
Time used: 0.063788 s to read 33554432 bytes
Time used: 0.088653 s to read 33554432 bytes
Time used: 0.079398 s to read 33554432 bytes
发现每读取32MB数据,需要花0.4s 时间。 那1s 时间就大概能读取70MB数据
同时在终端上执行cachetop 看看进程app 的缓存情况
#每5s刷新一次缓存
cachetop 5
13:40:20 Buffers MB: 0 / Cached MB: 448 / Sort: HITS / Order: ascending
PID UID CMD HITS MISSES DIRTIES READ_HIT% WRITE_HIT%
7818 root cachetop 9 0 0 100.0% 0.0%
7671 root app 2048 0 0 100.0% 0.0%
2048次缓存全部命中,读命中率为100%,看起来全部读请求都经过了系统缓存。思考下。。。。
是不是可以得到每秒读了多少数据呢? 我们知道内存是以页进行管理的,每个页的大小为4KB, 所以计算:2048次 *4KB/1024KB / 5s = 1.6MB,也就是每秒能读取1.6MB数据。显然跟 日志中看到每秒70MB数据相差甚大。
比较疑惑的是,为什么只能看到1.6MB的HITS? 是因为cachetop 不会把直接I/O 算进来。 这个命中的少量数据是元数据。
判断应用是否用了直接i/o,简单的方式观察系统调用,我们想起来用strace工具
strace -p app进程号
munmap(0x7f8811333000, 33558528) = 0
nanosleep({tv_sec=1, tv_nsec=0}, 0x7fff13f37d90) = 0
openat(AT_FDCWD, "/dev/sda2", O_RDONLY|O_DIRECT) = 4
mmap(NULL, 33558528, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8811333000
read(4, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 33554432) = 33554432
write(1, "Time used: 0.056457 s to read 33"..., 45) = 45
close(4)
分析可以看出系统调用了openat 来打开磁盘分区/dev/sdb2,并且传入参数为O_RDONLY|O_DIRECT(只读方或者直接读取),绕过了系统缓存。
总结:
buffers和cache 提升系统的i/o性能,用缓存命中率来衡量缓存的使用效率。不过,buffers和cache是操作系统来管理的,应用程序并不能直接控制这些缓存的内容和生命周期,因此一般用专门的缓存组件如redis来提升性能。