1、缓存命中率

使用 bcc 软件包,基于 Linux 内核 eBPF(extended Berkeley Packet Filters)机制,来跟踪内核中管理的缓存,并输出缓存的使用和命中情况。

  • cachestat:提供了整个操作系统缓存的读写命中情况
  • cachetop:提供了每个进程的缓存命中情况

    1、安装 bcc

    1. sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 4052245BD4284CDD
    2. echo "deb https://repo.iovisor.org/apt/xenial xenial main" | sudo tee /etc/apt/sources.list.d/iovisor.list
    3. sudo apt-get update
    4. sudo apt-get install -y bcc-tools libbcc-examples linux-headers-$(uname -r)

    命令导入PATH路径

    1. export PATH=$PATH:/usr/share/bcc/tools

    2、cachestat

    image.png

  • HITS:表示缓存命中的次数

  • MISSES:表示缓存未命中的次数
  • DIRTIES:表示新增到缓存中的脏页数
  • HITRATIO:以百分比表示的命中率
  • BUFFERS_MB:表示 Buffers 的大小,以MB为单位
  • CACHED_MB:表示 Cache 的大小,以MB为单位

    3、cachetop

    image.png
    默认按照缓存的命中次数(HITS)排序

  • HITS:代表间隔时间内的缓存命中次数

  • MISSES :代表未命中次数,单位是 页
  • DIRTIES:代表新增到缓存中的脏页数。

    2、指定文件的缓存大小

    使用 pcstat 来查看文件在内存中的缓存大小以及缓存比例

    1、pcstat

    1、安装

    pcstat 是一个基于 Go 语言开发的工具,需要先安装 go 语言

    1. export GOPATH=~/go
    2. export PATH=~/go/bin:$PATH
    3. go get golang.org/x/sys/unix
    4. go get github.com/tobert/pcstat/pcstat

    image.png
    所以这里推荐直接源码安装,然后根据报错信息,手动设置包路径即可

    1. mkdir -p $GOPATH/src/golang.org/x/
    2. cd !$
    3. git clone https://github.com/golang/sys.git
    4. git clone https://github.com/golang/text.git
    1. git clone https://github.com/tobert/pcstat.git
    2. cd pcstat
    3. go build
    4. sudo cp -a pcstat /usr/local/bin
    5. pcstat /usr/local/bin/pcstat

    2、使用

    image.png

  • Cached:就是 /bin/ls 在缓存中的大小

  • Percent:缓存的百分比

    3、命中缓存demo

    1. # 生成一个512MB的临时文件
    2. dd if=/dev/sda1 of=file bs=1M count=512
    3. # 清理缓存
    4. echo 3 > /proc/sys/vm/drop_caches

    image.png
    刚刚生成的文件不在缓存中

    1、第一次读取

    运行 dd 命令测试文件的读取速度
    image.png
    发现性能只有 524 MB/s,因为我这里是固态,所以比机械磁盘要快好多。
    然后查看 cachetop 缓存命中情况,发现缓存命中率只有 49.9% 。
    image.png

    2、缓存命中

    再次执行读取文件,可以看到速度直接到了 5 GB/s,缓存命中也达到了 100%
    image.png
    image.png

    4、demo

    1、准备

    镜像,参数支持:

  • d 选项,设置要读取的磁盘或分区路径,默认是查找前缀为 /dev/sd 或者 /dev/xvd 的磁盘。

  • -s 选项,设置每次读取的数据量大小,单位为字节,默认为 33554432(也就是 32MB)。
    1. docker run --privileged --name=app -itd feisky/app:io-direct

    2、分析

    image.png

    可以看到,每读取 32 MB 的数据,就需要花费 0.02s 的时间,比较慢。
    image.png
    但是可以看出已经全部走缓存了。
    这里需要注意,每秒实际读取的数据大小。HITS 表示缓存的命中次数。每次命中读取 一页 数据。
    内存以页为单位管理。每 页 的大小是 4KB,在 5s 的时间间隔里,命中缓存为 10244K/1024 = 4MB,再除以 5s,得出 每秒读的缓存是 0.8MB,跟32MB还是差很多的。
    image.png
    通过 strace 结果可以看到,案例应用了 openat 来打开磁盘分区 /dev/sda3,并且传入参数。
    O_RDONLY 表示以只读方式打开,而 *O_DIRECT 则表示以直接读取的方式打开,这会绕过系统的缓存

    这也就是耗时的原因。
    查看源码,发现
    1. int flags = O_RDONLY | O_LARGEFILE | O_DIRECT;
    2. int fd = open(disk, flags, 0755);
    确实直接使用了 直接 I/O 。需要删除 O_DIRECT 选项。