一文读懂Buffer与Cache:开启性能优化的大门

一文读懂Buffer与Cache:开启性能优化的大门

原创 往事敬秋风 深度Linux 2025年05月23日 07:31 湖南

计算机的世界里,内存管理就像是一场精密的交响乐指挥,协调着数据的流动与处理,对计算机系统的性能起着决定性作用。想象一下,计算机系统是一座繁忙的城市,内存就是城市中的交通枢纽,数据则是来来往往的车辆。如果没有良好的交通管理(内存管理),车辆(数据)就会拥堵,整个城市(计算机系统)的运行效率就会大幅下降。

而在内存管理这个大舞台上,Buffer(缓冲区)和 Cache(缓存)则是两位至关重要的角色,它们默默工作,却对系统性能有着深远影响。很多人对它们的概念感到困惑,常常将二者混为一谈,今天就让我们深入探究一下这两个神秘的存在,揭开它们的面纱,看看它们是如何在幕后助力计算机高效运行的 。

一、Buffer和Cache概述

从字面意思来看,Buffer 是缓冲区,Cache 是缓存 。它们都用于在内存中临时存储数据,但这两种 “临时存储” 又有着明显的区别。就好像你出门旅行,会带一个行李箱和一个随身小包。行李箱可以类比为 Buffer,它用来存放暂时不用,但后续可能会用到的物品,这些物品就像等待传输到其他设备的数据。而随身小包就像 Cache,里面装着你随时可能会用到的东西,比如手机、钱包,这些物品就像被频繁访问的数据,放在小包里能让你快速拿到,就像数据被缓存起来能被快速读取一样。

在 Linux 系统中,我们可以通过free命令来查看内存的使用情况 。打开终端,输入free -h(-h参数是为了让输出结果更易读),你会看到类似这样的信息:

              total        used        free      shared  buff/cache   available
Mem:           7.7G        2.0G        3.7G        113M        2.0G        5.3G
Swap:          2.0G          0B        2.0G

在这些信息中,buff/cache这一列引起了我们的注意,它就是 Buffer 和 Cache 的内存使用总和 。而实际上,free命令的统计数据是来自于/proc/meminfo这个文件 。用cat /proc/meminfo命令查看,会看到更详细的内存信息,其中Buffers和Cached这两个字段,分别对应着 Buffer 和 Cache 使用的内存大小 。

那么,Buffers、Cached和SReclaimable具体是什么含义呢?Buffers是内核缓冲区用到的内存,它主要用于缓存磁盘的数据 。比如,当我们向磁盘写入数据时,数据不会立刻被写入磁盘,而是先存储在Buffers中,等积累到一定量或者满足特定条件时,再统一写入磁盘,这样可以减少磁盘 I/O 的次数,提高写入效率 。Cached是内核页缓存和 Slab 用到的内存,主要用于缓存从文件读取的数据 。

当我们读取一个文件时,数据会被缓存到Cached中,如果下次再读取相同的文件内容,就可以直接从内存中获取,大大加快了读取速度 。SReclaimable是 Slab 的一部分,Slab 是内核中用于管理内存的一种机制,SReclaimable表示 Slab 中可回收的部分 。

二、Buffer与Cache工作原理

2.1Buffer的工作原理

Buffer 就像是数据传输过程中的一个临时停靠站 。当计算机与不同速度的设备进行数据交换时,比如内存与硬盘之间,由于硬盘的读写速度相对内存来说非常慢,如果没有 Buffer,内存就需要一直等待硬盘完成数据传输,这会极大地浪费内存的性能 。而有了 Buffer,当内存要向硬盘写入数据时,数据会先被存储到 Buffer 中 。假设我们要将一个大文件写入硬盘,文件数据会按一定大小的块依次存入 Buffer 。

当 Buffer 中的数据达到一定量(比如一个磁盘块的大小),或者满足特定的写入条件(如操作系统的写入调度策略)时,这些数据就会被一次性写入硬盘 。这样做的好处是,减少了硬盘的读写次数 。因为如果每次有少量数据就直接写入硬盘,硬盘的磁头需要频繁移动来定位数据位置,这会花费大量时间 。而通过 Buffer 的缓冲,将分散的小写入操作合并成大的写入操作,大大提高了数据传输的效率 。

同时,在数据读取时,Buffer 也起着类似的作用 。当从硬盘读取数据时,硬盘会将数据先读取到 Buffer 中,内存再从 Buffer 中读取数据,这就避免了内存直接与速度较慢的硬盘频繁交互,保证了数据传输的稳定性和高效性 。

当应用程序请求从磁盘读取数据时,内核会先检查Buffer中是否已经存在相应的数据块。如果存在,内核会直接从Buffer返回数据,避免了对物理磁盘的读取。如果数据不在Buffer中,内核会将数据块从磁盘读取到Buffer中,并返回给应用程序。这样,Buffer在一定程度上减少了对磁盘的访问次数,提高了I/O性能。

相关系统参数

(1)dirty_ratio

echo 20 > /proc/sys/vm/dirty_ratio
或
sysctl -w vm.dirty_ratio=20
  • 作用: dirty_ratio 参数定义了系统内存中脏页(已被修改但尚未写入磁盘)的最大比例。当脏页的比例达到或超过此值时,系统将启动同步写入操作,将脏页写入磁盘。

  • 影响: 控制脏页的及时写入,适当设置有助于避免频繁的磁盘写入操作。

  • 配置方式(参数的单位是百分比)

(2)dirty_background_ratio

echo 10 > /proc/sys/vm/dirty_background_ratio
或
sysctl -w vm.dirty_background_ratio=10
  • 作用:dirty_background_ratio 参数定义了当脏页的比例超过此值时,系统会触发后台写入操作。后台写入是指将脏页异步地写入磁盘,不会引起进程阻塞。

  • 影响: 控制后台写入的启动条件,避免系统过早地触发写入操作,从而提高系统性能。

  • 配置方式, 可通过修改 /proc/sys/vm/dirty_background_ratio 文件或使用 sysctl 命令进行配置。(参数的单位是百分比)

2.2Cache:加速系统的秘密武器

Cache 的工作原理基于程序的局部性原理 ,即程序在一段时间内访问的数据往往集中在一个较小的区域 。它就像一个数据的 “快速通道”,将经常访问的数据复制到内存中速度更快的区域 。以数据库查询为例,当我们执行一个数据库查询语句时,查询结果会被存储在 Cache 中 。如果下次再执行相同或相似的查询语句,系统会首先检查 Cache 中是否已经存在该结果 。如果存在,就直接从 Cache 中读取数据返回给用户,而不需要再次执行复杂的数据库查询操作 。这大大减少了数据访问的时间,提高了系统的响应速度 。

Cache 通常采用一些替换算法,如最近最少使用(LRU)算法,来决定当 Cache 空间不足时,哪些数据需要被替换出去 。LRU 算法会将最近一段时间内最少被访问的数据替换掉,这样可以保证 Cache 中始终保存着最有可能被再次访问的数据 。除了数据库查询,Cache 在 CPU 与内存之间也起着重要作用 。

由于 CPU 的运行速度远远快于内存,为了减少 CPU 等待内存数据的时间,在 CPU 和内存之间设置了 Cache 。CPU 首先会在 Cache 中查找需要的数据,如果找到(命中),就可以快速获取数据进行处理;如果没找到(未命中),才会从内存中读取数据,并将读取的数据同时存入 Cache 中,以便下次访问时能够更快获取 。

相关系统参数

(1)vfs_cache_pressure

echo 100 > /proc/sys/vm/vfs_cache_pressure
或
sysctl -w vm.vfs_cache_pressure=100
  • 作用: vfs_cache_pressure 参数用于调整内核对 dentry 和 inode 缓存的倾向性。较大的值使内核倾向于回收 dentry,而较小的值使内核倾向于回收 inode。

  • 影响: 控制文件系统缓存的回收策略,影响文件系统性能。较大的值有助于加速缓存的回收,从而释放内存。

  • 配置方式: 可通过修改 /proc/sys/vm/vfs_cache_pressure 文件或使用 sysctl 命令进行配置。

例如:swappiness

echo 10 > /proc/sys/vm/swappiness
或
sysctl -w vm.swappiness=10
  • 作用:swappiness 参数用于调整内核在内存不足时将数据移动到交换空间的倾向性。值的范围是 0 到 100,0 表示尽量不使用交换空间,100 表示尽量使用交换空间。

  • 影响: 控制系统对交换空间的利用,较小的值有助于减少对交换空间的使用,提高整体性能。

  • 配置方式: 可通过修改 /proc/sys/vm/swappiness 文件或使用 sysctl 命令进行配置。

3.3Buffer和Cache的区别

(1)存储内容

  • Buffer存储的是I/O操作的数据块,通常是对物理设备的读写请求的中介。

  • Cache存储的是文件系统的数据块,包括文件的元数据和实际内容。

(2)读取方式

  • Buffer主要用于减少对物理设备的读写次数,通过缓存I/O操作提高性能。

  • Cache更侧重于文件系统的读取,通过缓存文件数据和元数据提高文件系统的整体读取速度。

(3)清理策略

  • Buffer中的数据通常被操作系统维护,不容易手动清理。

  • Cache的内容可以通过手动或自动的方式进行清理,以释放内存空间。

三、实战案例分析

为了更直观地理解 Buffer 和 Cache 的工作方式,我们通过几个实际案例来进行分析 。在 Linux 系统中,我们可以使用vmstat命令来实时监控内存和 I/O 的使用情况 。打开终端,输入vmstat 1(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 999960   2464 135840    0    0     1     2    5   29  0  0 100  0  0

这里的buff和cache分别对应 Buffer 和 Cache 使用的内存大小,单位是 KB ,bi和bo分别表示块设备读取和写入的大小,单位为块 / 秒 。由于 Linux 中块的大小是 1KB,所以这个单位也就等价于 KB/s 。

3.1磁盘和文件写案例

我们先来模拟第一个场景;首先,在第一个终端,运行下面这个vmstat 命令:

# 每隔1秒输出1组数据
$ 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 7743608   1112  92168    0    0     0     0   52  152  0  1 100  0  0
 0  0      0 7743608   1112  92168    0    0     0     0   36   92  0  0 100  0  0

输出界面里, 内存部分的 buff 和 cache ,以及 io 部分的 bi 和 bo 就是我们要关注的重点。

  • buff 和 cache 就是我们前面看到的 Buffers 和 Cache,单位是 KB。

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

正常情况下,空闲系统中,你应该看到的是,这几个值在多次结果中一直保持不变。

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

$ dd if=/dev/urandom of=/tmp/file bs=1M count=500

然后再回到第一个终端,观察Buffer和Cache的变化情况:

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 7499460   1344 230484    0    0     0     0   29  145  0  0 100  0  0
 1  0      0 7338088   1752 390512    0    0   488     0   39  558  0 47 53  0  0
 1  0      0 7158872   1752 568800    0    0     0     4   30  376  1 50 49  0  0
 1  0      0 6980308   1752 747860    0    0     0     0   24  360  0 50 50  0  0
 0  0      0 6977448   1752 752072    0    0     0     0   29  138  0  0 100  0  0
 0  0      0 6977440   1760 752080    0    0     0   152   42  212  0  1 99  1  0
...
 0  1      0 6977216   1768 752104    0    0     4 122880   33  234  0  1 51 49  0
 0  1      0 6977440   1768 752108    0    0     0 10240   38  196  0  0 50 50  0

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

再进一步观察I/O的情况,你会看到,在 Cache 刚开始增长时,块设备 I/O 很少,bi 只出现了一次 488 KB/s,bo 则只有一次 4KB。而过一段时间后,才会出现大量的块设备写,比如 bo 变成了122880。

当 dd 命令结束后,Cache 不再增长,但块设备写还会持续一段时间,并且,多次 I/O 写的结果加起来,才是 dd 要写的 500M 的数据。

把这个结果,跟我们刚刚了解到的Cache的定义做个对比,你可能会有点晕乎。为什么前面文档上说 Cache 是文件读的页缓存,怎么现在写文件也有它的份?

这个疑问,我们暂且先记下来,接着再来看另一个磁盘写的案例。两个案例结束后,我们再统一进行分析。

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

如果你的系统符合标准,就可以继续在第二个终端中,运行下面的命令。清理缓存后,向磁盘分区/dev/sdb1 写入2GB的随机数据:

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

然后,再回到终端一,观察内存和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
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也会缓存写文件时的数据。

3.2磁盘和文件读案例

了解了磁盘和文件写的情况,我们再反过来想,磁盘和文件读的时候,又是怎样的呢?

我们回到第二个终端,运行下面的命令;清理缓存后,从文件/tmp/file中,读取数据写入空设备:

# 首先清理缓存
$ echo 3 > /proc/sys/vm/drop_caches
# 运行dd命令读取文件数据
$ 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中读取数据,写入空设备:

# 首先清理缓存
$ echo 3 > /proc/sys/vm/drop_caches
# 运行dd命令读取文件
$ 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 中。

当然,我想,经过上一个场景中两个案例的分析,你自己也可以对比得出这个结论:读文件时数据会缓存到 Cache 中,而读磁盘时数据会缓存到 Buffer 中。

到这里你应该发现了,虽然文档提供了对Buffer和Cache的说明,但是仍不能覆盖到所有的细节。比如说,今天我们了解到的这两点:

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

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

这样,我们就回答了案例开始前的两个问题:简单来说,Buffer是对磁盘数据的缓存,而Cache是文件数据的缓存,它们既会用在读请求中,也会用在写请求中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值