16-基础篇:怎么理解内存中Buffer和Cache?





free数据的来源

# 注意不同版本的free输出可能会有所不同
# centos7版本
[root@local_sa_192-168-1-6 ~]# free
       total   used    free     shared  buff/cache  available
Mem:   8169348 263524  6875352  668     1030472     7611064
Swap:  0       0       0

Buffer和Cache不太好区分
Buffer是缓冲区,而Cache是缓存,两者都是数据在内存中的临时存储
用man命令查询free的文档,就可以找到对应指标的详细说明

buffers
    Memory used by kernel buffers (Buffers in /proc/meminfo)
cache 
    Memory used by the page cache and slabs (Cached and SReclaimable in /proc/
buff/cache
    Sum of buffers and cache

Buffers是内核缓冲区用到的内存,对应的是/proc/meminfo中的Buffers值
Cache是内核页缓存和Slab用到的内存,对应的是/proc/meminfo中的Cached与SReclaimable之和



proc文件系统

/proc是Linux内核提供的一种特殊文件系统,是用户跟内核交互的接口
用户可以从/proc中查询内核的运行状态和配置选项, 查询进程的运行状态、统计数据等
也可以通过/proc来修改内核的配置

proc文件系统同时也是很多性能工具的最终数据来源
比如free ,就是通过读取/proc/meminfo ,得到内存的使用情况

执行man proc,可以得到proc文件系统的详细文档

Buffers %lu
    Relatively temporary storage for raw disk blocks that shouldn't get tremendously lar
Cached %lu
    In-memory cache for files read from the disk (the page cache). Doesn't include SwapC
...
SReclaimable %lu (since Linux 2.6.19)
    Part of Slab, that might be reclaimed, such as caches.
SUnreclaim %lu (since Linux 2.6.19)
    Part of Slab, that cannot be reclaimed on memory pressure.

通过这个文档,可以看到

  1. Buffers是对原始磁盘块的临时存储,也就是用来缓存磁盘的数据,通常不会特别大 (20MB 左右)
    这样,内核就可以把分散的写集中起来,统一优化磁盘的写入,比如可以把多次小的写合并成单次大的写等等
  2. Cached是从磁盘读取文件的页缓存,也就是用来缓存从文件读取的数据
    这样,下次访问这些文件数据时,就可以直接从内存中快速获取,而不需要再次访问缓慢的磁盘
  3. SReclaimable是Slab的一部分。Slab包括两部分,其中的可回收部分,用SReclaimable 记录
    而不可回收部分,用SUnreclaim 记录

产生的疑问:

  1. Buffer的文档没有提到这是磁盘读数据还是写数据的缓存,而在很多网络搜索的结果中都会提到Buffer只是对将要写入磁盘数据的缓存。
    那反过来说,它会不会也缓存从磁盘中读取的数据呢?
  2. Cache是对从文件读取数据的缓存,那么它是不是也会缓存写文件的数据呢?


案例

准备

实验环境
# 服务端(192.168.1.6)
配置:2CPU,4G内存,centos7.6_64     
预先安装docker、sysstat、等工具(yum install sysstat -y)

为了减少缓存的影响,运行下面的命令来清理系统缓存:
# 清理文件页、目录项、Inodes 等各种缓存
[root@local_sa_192-168-1-6 ~]# echo 3 > /proc/sys/vm/drop_caches

这里的/proc/sys/vm/drop_caches,就是通过proc文件系统修改内核行为的一个示例,
写入3表示清理文件页、目录项、Inodes等各种缓存。


场景 1:磁盘和文件写案例

文件写的案例

  1. 在第一个终端,运行 vmstat 命令

    [root@local_sa_192-168-1-6 ~]# vmstat 1
    procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
     r  b   swpd   free   buff  cache     si   so    bi    bo  in   cs   us  sy  id   wa st
     1  0      0 3030208  12    318020    0    0     0     0   7    5    0   0   100  0  0
     0  0      0 3030208  12    318020    0    0     0     0   61   102  0   0   100  0  0
     0  0      0 3030208  12    318020    0    0     0     0   75   107  1   0   99   0  0
     0  0      0 3030208  12    318020    0    0     0     0   37   69   0   0   100  0  0
     0  0      0 3030208  12    318020    0    0     0     0   46   75   0   0   100  0  0
    

​ buff和cache就是前面看到的Buffers和Cache,单位是KB

​ bi和bo则分别表示块设备读取和写入的大小,单位为块 / 秒。因为Linux中块的大小是1KB,所以这个单位也就等价于KB/s

​ 正常情况下,空闲系统中,这几个值在多次结果中一直保持不变


  1. 到第二个终端执行dd命令,通过读取随机设备,生成一个500MB大小的文件

    [root@local_sa_192-168-1-6 ~]# dd if=/dev/urandom of=/tmp/file bs=1M count=500
    记录了500+0 的读入
    记录了500+0 的写出
    524288000字节(524 MB)已复制,2.05113 秒,256 MB/秒
    

    同时,观察第一个终端,观察Buffer和Cache的变化情况

    [root@local_sa_192-168-1-6 ~]# vmstat 1
    procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
     r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
     0  0      0 3023392     12 318244    0    0     0     0   63  114  0  0 99  0  0
     1  0      0 3000776     12 340384    0    0    76     0  216  187  0  5 94  1  0
     1  0      0 2743380     12 597688    0    0     0     0 1067  118  0 50 50  0  0
     0  1      0 2496184     12 845864    0    0     0 97796 1402  415  0 48  8 41  2
     0  1      0 2496264     12 845832    0    0     0 32768  179  118  0  1 50 49  0
     0  1      0 2496296     12 845800    0    0     0 16384  122  117  0  0 50 50  0
     0  1      0 2496168     12 845932    0    0     0 49152  248   90  0  1 50 49  0
     0  0      0 2496712     12 845340    0    0     0 16896  256  102  0  1 83 16  0
    

    通过观察vmstat的输出,我们发现,在dd命令运行时, Cache在不停地增长,而Buffer基本保持不变

    再进一步观察 I/O 的情况

    1. 在Cache刚开始增长时,块设备I/O很少,bi只出现了一次76KB/s
      而过一段时间后,才会出现大量的块设备写,比如bo变成了 97796
    2. 当dd命令结束后,Cache不再增长,但块设备写还会持续一段时间
      并且,多次 I/O 写的结果加起来,才是dd要写的500M的数据

    Cache 对文件的写入会缓存


磁盘写的案例

注意:下面的命令对环境要求很高,需要系统配置多块磁盘,并且磁盘分区/dev/sdb1还要处于未使用状态
如果只有一块磁盘,千万不要尝试,否则将会对你的磁盘分区造成损坏

清理缓存后,向磁盘分区/dev/sdb1写入2GB的随机数据

# 首先清理缓存
[root@local_sa_192-168-1-6 ~]# echo 3 > /proc/sys/vm/drop_caches
# 然后运行 dd 命令向磁盘分区 /dev/sdb1 写入 2G 数据
[root@local_sa_192-168-1-6 ~]# dd if=/dev/urandom of=/dev/sdb1 bs=1M count=2048

同时,观察内存和I/O的变化情况

[root@local_sa_192-168-1-6 ~]# vmstat 1
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free     buff     cache    si  so  bi   bo     in  cs us sy id wa st
 1  0   0      7584780  153592   97436    0   0   684  0      31  423 1 48 50 2  0
 1  0   0      7418580  315384   101668   0   0   0    0      32  144 0 50 50 0  0
 1  0   0      7253664  475844   106208   0   0   0    0      20  137 0 50 50 0  0
 1  0   0      7093352  631800   110520   0   0   0    0      23  223 0 50 50 0  0
 1  1   0      6930056  790520   114980   0   0   0    12804  23  168 0 50 42 9  0
 1  0   0      6757204  949240   119396   0   0   0    183804 24  191 0 53 26 21 0
 1  1   0      6591516  1107960  123840   0   0   0    77316  22  232 0 52 16 33 0

从这里可以看到,虽然同是写数据,写磁盘跟写文件的现象还是不同的
写磁盘时(也就是bo大于0时),Buffer和Cache都在增长,但显然Buffer的增长快得多
这说明,写磁盘用到了大量的Buffer。


小结

对比两个案例,可以知道,写文件时会用到Cache缓存数据,而写磁盘则会用到Buffer来缓存数据
所以,虽然文档上只提到,Cache是文件读的缓存,但实际上,Cache也会缓存写文件时的数据



场景 2:磁盘和文件读案例

文件读的案例

清理缓存后,从文件/tmp/file中,读取数据写入空设备

# 首先清理缓存
[root@local_sa_192-168-1-6 ~]# echo 3 > /proc/sys/vm/drop_caches
# 运行 dd 命令读取文件数据
[root@local_sa_192-168-1-6 ~]# dd if=/tmp/file of=/dev/null

然后,观察内存和 I/O 的变化情况

procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
0 1 0 7724164 2380 110844 0 0 16576 0 62 360 2 2 76 21 0
0 1 0 7691544 2380 143472 0 0 32640 0 46 439 1 3 50 46 0
0 1 0 7658736 2380 176204 0 0 32640 0 54 407 1 4 50 46 0
0 1 0 7626052 2380 208908 0 0 32640 40 44 422 2 2 50 46 0

观察vmstat的输出,发现读取文件时(也就是 bi 大于 0 时),Buffer保持不变
而Cache则在不停增长。这跟查到的定义“Cache 是对文件读的页缓存”是一致的


磁盘读的案例

清理缓存后,从磁盘分区/dev/sda1中读取数 据,写入空设备

# 首先清理缓存
[root@local_sa_192-168-1-6 ~]# echo 3 > /proc/sys/vm/drop_caches
# 运行 dd 命令读取文件
[root@local_sa_192-168-1-6 ~]# dd if=/dev/sda1 of=/dev/null bs=1M count=1024

然后,观察内存和 I/O 的变化情况

procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r b swpd free buff cache si so bi bo in cs us sy id wa st
0 0 0 7225880 2716 608184 0 0 0 0 48 159 0 0 100 0 0
 0 1 0 7199420 28644 608228 0 0 25928 0 60 252 0 1 65 35 0
 0 1 0 7167092 60900 608312 0 0 32256 0 54 269 0 1 50 49 0
 0 1 0 7134416 93572 608376 0 0 32672 0 53 253 0 0 51 49 0
 0 1 0 7101484 126320 608480 0 0 32748 0 80 414 0 1 50 49 0

观察vmstat的输出,发现读磁盘时(也就是 bi 大于 0 时),Buffer和Cache都在增长,但显然Buffer的增长快很多
这说明读磁盘时,数据缓存到了Buffer中



结论

Buffer 既可以用作“将要写入磁盘数据的缓存”,也可以用作“从磁盘读取数据的缓存”

Cache 既可以用作“从文件读取数据的页缓存”,也可以用作“写文件的页缓存”

Buffer是对磁盘数据的缓存,而Cache是文件数据的缓存,它们既会用在读请求中,也会用在写请求中



小结

Buffer 和 Cache 分别缓存磁盘和文件系统的读写数据

  1. 从写的角度来说,不仅可以优化磁盘和文件的写入,对应用程序也有好处,应用程序可以在数据真正落盘前,就返回去做其他工作
  2. 从读的角度来说,既可以加速读取那些需要频繁访问的数据,也降低了频繁 I/O 对磁盘的压力

posted @ 2021-12-01 17:37  李成果  阅读(453)  评论(0编辑  收藏  举报