性能分析(7)- 未利用系统缓存致使 I/O 缓慢案例

性能分析小案例系列,能够经过下面连接查看哦html

https://www.cnblogs.com/poloyy/category/1814570.htmllinux

 

前提

前面有学到 Buffer 和 Cache 的概念:https://www.cnblogs.com/poloyy/p/13503848.htmlgit

咱们来简单复习下github

 

Buffer 和 Cache 的设计目的

为了提高系统的 I/O 性能,它们利用内存,充当起慢速磁盘和快速 CPU 之间的桥梁,能够加速 I/O 的访问速度golang

 

Buffer 和 Cache

  • BUffer:对磁盘的读写数据缓存
  • Cache:对文件系统的读写数据缓存

 

读写角度

  • 的角度来讲,不只能够优化磁盘和文件的写入,对应用程序也有好处,应用程序能够在数据真正落盘前,就返回去作其余工做
  • 的角度来讲,不只能够提升那些频繁访问数据的读取速度,也能够下降频繁 I/O 对磁盘的压力

 

引入主题

既然 Buffer 和 Cache 对系统性能有很大影响,那咱们在软件开发的过程当中,能不能利用这 一点,来优化 I/O 性能,提高应用程序的运行效率呢? 答案天然是确定的docker

 

缓存命中率

灵魂拷问

咱们想利用缓存来提高程序的运行效率,应该怎么评估这个效果呢?换句话说,有没有哪一个指标能够衡量缓存使用的好坏呢?ubuntu

 

灵魂回答

  • 缓存命中率
  • 指直接经过缓存获取数据的请求次数,占全部数据请求次数的百分比【使用缓存请求次数 / 总请求次数】
  • 命中率越高,表示使用缓存带来的收益越高,应用程序的性能也就越好

 

缓存的重要性

  • 缓存是如今全部高并发系统必需的核心模块
  • 主要做用:把常常访问的数据(热点数据),提早读入到内存中,下次访问时就能够直接从内存读取数据,而不须要通过硬盘,从而加快应用程序的响应速度

 

cachestat、cachetop

独立的缓存模块一般会提供查询接口,方便咱们随时查看缓存的命中状况vim

不过 Linux 系统中并无直接提供这些接口,因此这里要介绍一下,cachestat 和 cachetop ,它们正是查看系统缓存命中状况的工具缓存

  • cachestat 提供了整个操做系统缓存的读写命中状况
  • cachetop 提供了每一个进程的缓存命中状况

 

Ubuntu 安装工具

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

 

配置环境变量

vim /etc/profile

# 在文件结尾处添加
export PATH=$PATH:/usr/share/bcc/tools

# 保存文件后
source /etc/profile

 

cachestat

# 它以 1 秒的时间间隔,输出了 3 组缓存统计数据
cachestat 1 3

字段说明

 

cachetop

cachetop

  • 默认按照缓存的命中次数(HITS)排序,展现了每一个进程的缓存命中状况
  • 具体到每个指标,这里的 HITS、MISSES 和 DIRTIES ,跟 cachestat 里的含义 同样,分别表明间隔时间内的缓存命中次数、未命中次数以及新增到缓存中的脏页数
  • READ_HIT:读的缓存命中率
  • WRITE_HIT:写的缓存命中率

 

查看文件的缓存大小

可使用 pcstat 这个工具,来查看文件在内存中的缓存大小以及缓存比例并发

 

安装 pcstat

# 安装 go
apt install golang-go

vim /etc/profile

# 在文件结尾处添加
export GOPATH=~/go
export PATH=~/go/bin:$PATH

# 保存文件后
source /etc/profile
 
go get golang.org/x/sys/unix
go get github.com/tobert/pcstat/pcstat

 

运行 pcstat

pcstat /bin/ls

Cached 就是 /bin/ls 在缓存中的大小,而 Percent 则是缓存的百分比,看到它们都是 0,这说明 /bin/ls 并不在缓存中

 

执行 ls 命令,再来查看

ls
pcstat /bin/ls

发现都在缓存中了

 

讲案例前的准备

  • 系统:Ubuntu 18.04,固然一样适用于其余的 Linux 系 统。
  • 机器配置:2 CPU,2 GB 内存
  • 预先按照上面的步骤安装 bcc 和 pcstat 软件包,并把这些工具的安装路径添加到到 PATH 环境变量中
  • 预先安装 Docker 软件包: apt-get install docker.io 
  • 打开两个终端同时连到 Linux 上

 

案例一:经过 dd 写入读取文件

注意:没说第几个终端都是默认第一个终端执行命令哦

 

 dd 命令生成一个临时文件

# 生成一个 512MB 的临时文件
dd if=/dev/sda1 of=file bs=1M count=512

# 清理缓存
echo 3 > /proc/sys/vm/drop_caches

 

运行 pcstat 查看 file 文件

pcstat file

确认刚刚生成的文件不在缓存中。若是一切正常, 会看到 Cached 和 Percent 都是 0

 

运行 cachetop 

# 每隔 1 秒刷新一次数据
cachetop 1

 

第二个终端运行 dd 命令

dd if=file of=/dev/null bs=1M

  • 从 dd 的结果能够看出,这个文件的读性能是 639 MB/s
  • 因为在 dd 命令运行前咱们已经清理了缓存,因此 dd 命令读取数据时,确定要经过文件系统从磁盘中读取

 

查看 cachetop

能够看到 dd 命令并非全部的读都落到了磁盘上,读请求的缓存命中率只有 50%

 

第二个终端再次运行 dd 命令

dd if=file of=/dev/null bs=1M

磁盘的读性能蹭蹭蹭往上涨,去到了 1.6GB/s

 

再查看 cachetop

能够发现,此次读的缓存命中率是 100%

 

第二个终端运行 pcstat

pcstat  file

测试文件已经被所有缓存起来了,和刚刚 cachetop 观察到缓存命中率 100% 是一致的

 

总结

这两次结果说明,系统缓存对第二次 dd 操做有明显的加速效果,能够大大提升文件读取的性能。

 

案例二

前提

  • 这里运行了一个不可中断状态的进程
  • 功能:是每秒从磁盘分区 /dev/sda1 中读取 32MB 的数据, 并打印出读取数据花费的时间

 

查看应用日志

从这里能够看到,每读取 32 MB 的数据,就须要花 0.9 秒

 

灵魂拷问

这个时间合理吗?这也太慢了吧,那这是否是没用系统缓存致使的呢?

 

查看 cachetop

结果分析

读的命中率虽然是 100%,命中次数是 1024,看起来所有的读请求都通过了系统缓存

 

灵魂又拷问了

全都是缓存 I/O,读取速度不该该这么慢

 

深刻分析

  • 另外一个重要因素,每秒实际读取的数据大小,HITS 表明缓存的命中次数,那么每次命中能读取多少数据呢?天然是一页
  • 内存以页为单位进行管理,而每一个页的大小是 4KB
  • 因此,在 5 秒的时间间隔 里,命中的缓存为 1024*4K/1024 = 4MB,再除以 5 秒,能够获得每秒读的缓存是 0.8MB,显然跟案例应用的 32 MB/s 相差太多

 

结果猜测

这个案例估计没有充分利用系统缓存,若是系统调用设置直接 I/O 的标志,就能够绕过系统缓存

 

经过 strace 观察系统调用

strace -p $(pgrep app)

结果分析

  • 从 strace 的结果能够看到,案例应用调用了 openat 来打开磁盘分区 /dev/sdb1,而且传 入的参数为 O_RDONLY|O_DIRECT(中间的竖线表示或)
  • O_RDONLY 表示以只读方式打开
  • 而 O_DIRECT 则表示以直接读取的方式打开,这会绕过系统的缓存
  • 验证了这一点,就很容易理解为何读 32 MB 的数据就都要那么久了
  • 直接从磁盘读写的速度,天然远慢于对缓存的读写,这也是缓存存在的最大意义了

 

查看应用源码

它果真用了直接 I/O

 

修改源代码

删除 O_DIRECT 选项,让应用程序使用缓存 I/O ,而不是直接 I/O,就能够加速磁盘读取速度

 

验证修复后的应用

查看新应用的日志

结果分析

如今,每次只须要 0.03 秒,就能够读取 32MB 数据,明显比以前的 0.9 秒快多了。因此,此次应该用了系统缓存

 

再次查看 cachetop

结果分析

  • 果真,读的命中率仍是 100%,HITS (即命中数)却变成了 40960
  • 一样的方法计算一 下,换算成每秒字节数正好是 32 MB(即 40960*4k/5/1024=32M)

 

总结

  • 在进行 I/O 操做时,充分利用系统缓存能够极大地提高性能。
  • 但在观察缓存命中率时,还要注意结合应用程序实际的 I/O 大小,综合分析缓存的使用状况

 

扩展

为何优化前,经过 cachetop 只能看到不多一部分数据的所有命中,而没有观察到大量数据的未命中状况呢?

 

回答

是由于,cachetop 工具并不把直接 I/O 算进来

相关文章
相关标签/搜索