Linux 里Buffer和Cache的定義及使用


Buffer 和 Cache 的介紹

查看內存使用情況

# 注意不同版本的free輸出可能會有所不同
$ free
              total        used        free      shared  buff/cache   available
Mem:        8169348      263524     6875352         668     1030472     7611064
Swap:             0           0           0

  顯然,這個界面包含了物理內存 Mem 和交換分區 Swap 的具體使用情況,比如總內存、已用內存、緩存、可用內存等。其中緩存是 Buffer 和 Cache 兩部分的總和 。

大部分指標都比較容易理解,但 Buffer 和 Cache 可能不太好區分。從字面上來說,Buffer 是緩沖區,而 Cache 是緩存,兩者都是數據在內存中的臨時存儲;

Buffer 是對磁盤數據的緩存,而 Cache 是文件數據的緩存,它們既會用在讀請求中,也會用在寫請求中。

uffer 和 Cache 分別緩存的是

對磁盤和文件系統的讀寫數據。從寫的角度來說,不僅可以優化磁盤和文件的寫入,對應用程序也有好處,應用程序可以在數據真正落盤前,就返回去做其他工作。

從讀的角度來說,不僅可以提高那些頻繁訪問數據的讀取速度,也降低了頻繁 I/O 對磁盤的壓力。

利用緩存的命中率來優化系統。

所謂緩存命中率,是指直接通過緩存獲取數據的請求次數,占所有數據請求次數的百分比。

命中率越高,表示使用緩存帶來的收益越高,應用程序的性能也就越好。實際上,緩存是現在所有高並發系統必需的核心模塊,主要作用就是把經常訪問的數據(也就是熱點數據),提前讀入到內存中。這樣,下次訪問時就可以直接從內存讀取數據,而不需要經過硬盤,從而加快應用程序的響應速度。

這些獨立的緩存模塊通常會提供查詢接口,方便隨時查看緩存的命中情況。不過 Linux 系統中並沒有直接提供這些接口,所以這里介紹一下,cachestat 和 cachetop  ,它們正是查看系統緩存命中情況的工具。

cachestat 提供了整個操作系統緩存的讀寫命中情況。

cachetop 提供了每個進程的緩存命中情況。

這兩個工具都是 bcc 軟件包的一部分,它們基於 Linux 內核的 eBPF(extended Berkeley Packet Filters)機制,來跟蹤內核中管理的緩存,並輸出緩存的使用和命中情況。

 bcc-tools 需要內核版本為 4.1 或者更新的版本,如果你用的是 CentOS,那就需要手動升級,但我升到5.8內核版本工具版本問題報錯

安裝

yum update
rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org && rpm -Uvh http://www.elrepo.org/elrepo-release-7.0-2.el7.elrepo.noarch.rpm
uname -r
yum remove kernel-headers kernel-tools kernel-tools-libs
yum -y install perl
yum --disablerepo="*" --enablerepo="elrepo-kernel" install kernel-lt kernel-lt-devel kernel-lt-headers kernel-lt-tools kernel-lt-tools-libs kernel-lt-tools-libs-devel
想要升級最新版本執行下面安裝命令
yum --disablerepo="*" --enablerepo="elrepo-kernel" install kernel-ml kernel-ml-devel kernel-ml-headers kernel-ml-tools kernel-ml-tools-libs kernel-ml-tools-libs-devel

sed -i '/GRUB_DEFAULT/s/=.*/=0/' /etc/default/grub

grub2-mkconfig -o /boot/grub2/grub.cfg
reboot
uname -r  查看內核
4.4.233-1.el7.elrepo.x86_64
yum install -y bcc-tools 安裝工具集
添加環境變量
echo 'export PATH=$PATH:/usr/share/bcc/tools' > /etc/profile.d/bcc-tools.sh
exec bash
[root@localhost ~]# cachestat 1 1
    HITS   MISSES  DIRTIES HITRATIO   BUFFERS_MB  CACHED_MB
       0        0        0    0.00%            2        302

  cachestat 的輸出其實是一個表格。每行代表一組數據,而每一列代表不同的緩存統計指標。這些指標從左到右依次表示:

TOTAL ,表示總的 I/O 次數;

MISSES ,表示緩存未命中的次數;

HITS ,表示緩存命中的次數;

DIRTIES, 表示新增到緩存中的臟頁數;

BUFFERS_MB 表示 Buffers 的大小,以 MB 為單位;

CACHED_MB 表示 Cache 的大小,以 MB 為單位。

再來看一個 cachetop 的運行界面:

$ cachetop
11:58:50 Buffers MB: 258 / Cached MB: 347 / Sort: HITS / Order: ascending
PID      UID      CMD              HITS     MISSES   DIRTIES  READ_HIT%  WRITE_HIT%
   13029 root     python                  1        0        0     100.0%       0.0%

  它的輸出跟 top 類似,默認按照緩存的命中次數(HITS)排序,展示了每個進程的緩存命中情況。具體到每一個指標,這里的 HITS、MISSES 和 DIRTIES ,跟 cachestat 里的含義一樣,分別代表間隔時間內的緩存命中次數、未命中次數以及新增到緩存中的臟頁數。而 READ_HIT 和 WRITE_HIT ,分別表示讀和寫的緩存命中率。

指定文件的緩存大小

除了緩存的命中率外,還有一個指標也會很感興趣,那就是指定文件在內存中的緩存大小。可以使用 pcstat 這個工具,來查看文件在內存中的緩存大小以及緩存比例。pcstat 是一個基於 Go 語言開發的工具,所以安裝它之前,你首先應該安裝 Go 語言,你可以點擊這里下載安裝。

cd /usr/bin
if [ $(uname -m) == "x86_64" ] ; then
    curl -L -o pcstat https://github.com/tobert/pcstat/raw/2014-05-02-01/pcstat.x86_64
else
    curl -L -o pcstat https://github.com/tobert/pcstat/raw/2014-05-02-01/pcstat.x86_32
fi
chmod 755 pcstat

  全部安裝完成后,可以運行 pcstat 來查看文件的緩存情況了。比如,下面就是一個 pcstat 運行的示例,它展示了 /bin/ls 這個文件的緩存情況:

[root@localhost ~]# pcstat /bin/ls
|----------+----------------+------------+-----------+---------|
| Name     | Size           | Pages      | Cached    | Percent |
|----------+----------------+------------+-----------+---------|
| /bin/ls  | 117608         | 29         | 0         | 000.000 |
|----------+----------------+------------+-----------+---------|

  這個輸出中,Cached 就是 /bin/ls 在緩存中的大小,而 Percent 則是緩存的百分比。如果看到它們都是 0,這說明 /bin/ls 並不在緩存中。

接着,如果執行一下 ls 命令,再運行相同的命令來查看的話,就會發現 /bin/ls 都在緩存中了:

[root@localhost ~]# ls
anaconda-ks.cfg  file
[root@localhost ~]# pcstat /bin/ls
|----------+----------------+------------+-----------+---------|
| Name     | Size           | Pages      | Cached    | Percent |
|----------+----------------+------------+-----------+---------|
| /bin/ls  | 117608         | 29         | 29        | 100.000 |
|----------+----------------+------------+-----------+---------|

 知道了緩存相應的指標和查看系統緩存的方法后

 

# 生成一個512MB的臨時文件
#dd if=/dev/sda1 of=file bs=1M count=512
# 清理緩存
# echo 3 > /proc/sys/vm/drop_caches
# pcstat file
|----------+----------------+------------+-----------+---------|
| Name     | Size           | Pages      | Cached    | Percent |
|----------+----------------+------------+-----------+---------|
| file     | 536870912      | 131072     | 62974     | 048.045 |
|----------+----------------+------------+-----------+---------|

echo 3 > /proc/sys/vm/drop_caches
echo 3 > /proc/sys/vm/drop_caches
echo 3 > /proc/sys/vm/drop_caches
# pcstat file
|----------+----------------+------------+-----------+---------|
| Name     | Size           | Pages      | Cached    | Percent |
|----------+----------------+------------+-----------+---------|
| file     | 536870912      | 131072     | 0         | 000.000 |
|----------+----------------+------------+-----------+---------|

  運行 pcstat 命令,確認剛剛生成的文件不在緩存中。如果一切正常,看到 Cached 和 Percent 都是 0:,如果不是0多清理一下緩存

現在運行 cachetop 命令:

# 每隔5秒刷新一次數據
$ cachetop 5

  運行 dd 命令測試文件的讀取速度:

[root@localhost ~]# dd if=file of=/dev/null bs=1M
記錄了512+0 的讀入
記錄了512+0 的寫出
536870912字節(537 MB)已復制,20.7171 秒,25.9 MB/秒

  從 dd 的結果可以看出,這個文件的讀性能是 33.4 MB/s。由於在 dd 命令運行前我們已經清理了緩存,所以 dd 命令讀取數據時,肯定要通過文件系統從磁盤中讀取。

 查看 cachetop 界面的緩存命中情況 

07:57:55 Buffers MB: 0 / Cached MB: 288 / Sort: HITS / Order: ascending
PID      UID      CMD              HITS     MISSES   DIRTIES  READ_HIT%  WRITE_HIT%
    1409 root     cachetop                3        0        0     100.0%       0.0%
    1435 root     dd                  27648    27648        0	   50.0%      50.0%

  從 cachetop 的結果可以發現,並不是所有的讀都落到了磁盤上,事實上讀請求的緩存命中率只有 50% 。

繼續嘗試相同的測試命令。終端2再次執行剛才的 dd 命令

[root@localhost ~]# dd if=file of=/dev/null bs=1M
記錄了512+0 的讀入
記錄了512+0 的寫出
536870912字節(537 MB)已復制,0.123877 秒,4.3 GB/秒

  磁盤的讀性能居然變成了 4.5 GB/s,比第一次的結果明顯高了太多

看看 cachetop 的情況

08:03:35 Buffers MB: 0 / Cached MB: 635 / Sort: HITS / Order: ascending
PID      UID      CMD              HITS     MISSES   DIRTIES  READ_HIT%  WRITE_HIT%
    1409 root     cachetop                1        0        0     100.0%       0.0%
    1457 root     bash                  277        0        0     100.0%       0.0%
    1457 root     dd                 131644        0        0     100.0%       0.0%

  cachetop 也有了不小的變化。可以發現,這次的讀的緩存命中率是 100.0%,也就是說這次的 dd 命令全部命中了緩存,所以才會看到那么高的性能。

終端2再次執行 pcstat 查看文件 file 的緩存情況

[root@localhost ~]# pcstat file
|----------+----------------+------------+-----------+---------|
| Name     | Size           | Pages      | Cached    | Percent |
|----------+----------------+------------+-----------+---------|
| file     | 536870912      | 131072     | 131072    | 100.000 |
|----------+----------------+------------+-----------+---------|

  pcstat 的結果可以發現,測試文件 file 已經被全部緩存了起來,這跟剛才觀察到的緩存命中率 100% 是一致的。

這兩次結果說明,系統緩存對第二次 dd 操作有明顯的加速效果,可以大大提高文件讀取的性能。但同時也要注意,如果把 dd 當成測試文件系統性能的工具,由於緩存的存在,就會導致測試結果嚴重失真。

再來看一個文件讀寫的案例

開啟兩個終端。分別 SSH 登錄到機器上后,先在第一個終端中運行 cachetop 命令:

# 每隔5秒刷新一次數據
$ cachetop 5 

  接着,再到第二個終端,執行下面的命令運行案例:

docker run --privileged --name=app -itd feisky/app:io-direct

  查看環境是否啟動完成

[root@localhost ~]# docker logs app
Reading data from disk /dev/sda2 with buffer size 33554432
Time used: 0.090524 s to read 33554432 bytes
Time used: 0.029526 s to read 33554432 bytes
Time used: 0.028942 s to read 33554432 bytes
Time used: 0.028966 s to read 33554432 bytes
Time used: 0.027196 s to read 33554432 bytes

  可以看到,每讀取 32 MB 的數據,就需要花 0.9 秒

這個輸出似乎有點意思了。1024 次緩存全部命中,讀的命中率是 100%,看起來全部的讀請求都經過了系統緩存。但是問題又來了,如果真的都是緩存 I/O,讀取速度不應該這么慢。

08:19:57 Buffers MB: 0 / Cached MB: 959 / Sort: HITS / Order: ascending
PID      UID      CMD              HITS     MISSES   DIRTIES  READ_HIT%  WRITE_HIT%
    1409 root     cachetop                3        0        0     100.0%       0.0%
    1748 root     dockerd                10        0        5	   50.0%       0.0%
    1811 root     app                  2560        0        0     100.0%       0.0%

  每秒實際讀取的數據大小。HITS 代表緩存的命中次數,那么每次命中能讀取是一頁數據。內存以頁為單位進行管理,而每個頁的大小是 4KB。所以,在 5 秒的時間間隔里,命中的緩存為 1024*4K/1024 = 4MB,再除以 5 秒,可以得到每秒讀的緩存是 0.8MB,顯然跟案例應用的 32 MB/s 相差太多。

如果為系統調用設置直接 I/O 的標志,就可以繞過系統緩存。那么,要判斷應用程序是否用了直接 I/O,最簡單的方法當然是觀察它的系統調用,查找應用程序在調用它們時的選項。還是 strace。

[root@localhost ~]# strace -p $(pgrep app)
strace: Process 1811 attached
restart_syscall(<... resuming interrupted read ...>) = 0
openat(AT_FDCWD, "/dev/sda2", O_RDONLY|O_DIRECT) = 4
mmap(NULL, 33558528, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fbb65270000
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.039126 s to read 33"..., 45) = 45
close(4)                                = 0
munmap(0x7fbb65270000, 33558528)        = 0
nanosleep({tv_sec=1, tv_nsec=0}, 0x7ffd1dcc01c0) = 0
openat(AT_FDCWD, "/dev/sda2", O_RDONLY|O_DIRECT) = 4
mmap(NULL, 33558528, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fbb65270000
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.029105 s to read 33"..., 45) = 45
close(4)                                = 0

  從 strace 的結果可以看到,案例應用調用了 openat 來打開磁盤分區 /dev/sda2,並且傳入的參數為 O_RDONLY|O_DIRECT(中間的豎線表示或)。O_RDONLY 表示以只讀方式打開,而 O_DIRECT 則表示以直接讀取的方式打開,這會繞過系統的緩存。

驗證了這一點,就很容易理解為什么讀 32 MB 的數據就都要那么久了。直接從磁盤讀寫的速度,自然遠慢於對緩存的讀寫。這也是緩存存在的最大意義了。

對代碼做修改重新運行

[root@localhost ~]# docker rm -f app
app
[root@localhost ~]#  docker run --privileged --name=app -itd feisky/app:io-cached
Unable to find image 'feisky/app:io-cached' locally
io-cached: Pulling from feisky/app
32802c0cfa4d: Already exists 
da1315cffa03: Already exists 
fa83472a3562: Already exists 
f85999a86bef: Already exists 
2f251909225c: Retrying in 1 second 
a374aef23781: Downloading 
io-cached: Pulling from feisky/app
32802c0cfa4d: Already exists 
da1315cffa03: Already exists 
fa83472a3562: Already exists 
f85999a86bef: Already exists 
2f251909225c: Pull complete 
a374aef23781: Pull complete 
Digest: sha256:affc2e9dd8d4cecc23b918e7b536852c747ce86291eb4daecdc8903b16c461ed
Status: Downloaded newer image for feisky/app:io-cached
5843d1ee9bf07381fa81acf834d4f37dd56f77d08dab3bfd5e31d7301c6a514c
[root@localhost ~]# docker logs app
Reading data from disk /dev/sda2 with buffer size 33554432
Time used: 0.030117 s to read 33554432 bytes
Time used: 0.027294 s to read 33554432 bytes
Time used: 0.025451 s to read 33554432 bytes
Time used: 0.052026 s to read 33554432 bytes
Time used: 0.026425 s to read 33554432 bytes
Time used: 0.019488 s to read 33554432 bytes
Time used: 0.025104 s to read 33554432 bytes
Time used: 0.024904 s to read 33554432 bytes
Time used: 0.025110 s to read 33554432 bytes
Time used: 0.025644 s to read 33554432 bytes
Time used: 0.025669 s to read 33554432 bytes
Time used: 0.023755 s to read 33554432 bytes
Time used: 0.022087 s to read 33554432 bytes
Time used: 0.023663 s to read 33554432 bytes
Time used: 0.024177 s to read 33554432 bytes
Time used: 0.025311 s to read 33554432 bytes
Time used: 0.014088 s to read 33554432 bytes
Time used: 0.021050 s to read 33554432 bytes
Time used: 0.024807 s to read 33554432 bytes
Time used: 0.025297 s to read 33554432 bytes
Time used: 0.024300 s to read 33554432 bytes
Time used: 0.024217 s to read 33554432 bytes
Time used: 0.024323 s to read 33554432 bytes
Time used: 0.024950 s to read 33554432 bytes

  現在,每次只需要 0.03 秒,就可以讀取 32MB 數據,明顯比之前的 0.9 秒快多了。所以,這次應該用了系統緩存。

查看 cachetop 的輸出來確認一下

08:35:49 Buffers MB: 36 / Cached MB: 1126 / Sort: HITS / Order: ascending
PID      UID      CMD              HITS     MISSES   DIRTIES  READ_HIT%  WRITE_HIT%
    1409 root     cachetop                3        0        0     100.0%       0.0%
    1590 root     dockerd                10        1        5	   45.5%       0.0%
    2164 root     app                 40960        0        0     100.0%       0.0%

  果然,讀的命中率還是 100%,HITS (即命中數)卻變成了 40960,同樣的方法計算一下,換算成每秒字節數正好是 32 MB(即 40960*4k/5/1024=32M)。這個說明,在進行 I/O 操作時,充分利用系統緩存可以極大地提升性能。 但在觀察緩存命中率時,還要注意結合應用程序實際的 I/O 大小,綜合分析緩存的使用情況。

cachestat 和 cachetop 這兩個工具,觀察系統和進程的緩存命中情況。

其中,cachestat 提供了整個系統緩存的讀寫命中情況。

cachetop 提供了每個進程的緩存命中情況。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM