一文讀懂Buffer與Cache:開啟性能優(yōu)化的大門
在計算機的世界里,內(nèi)存管理就像是一場精密的交響樂指揮,協(xié)調(diào)著數(shù)據(jù)的流動與處理,對計算機系統(tǒng)的性能起著決定性作用。想象一下,計算機系統(tǒng)是一座繁忙的城市,內(nèi)存就是城市中的交通樞紐,數(shù)據(jù)則是來來往往的車輛。如果沒有良好的交通管理(內(nèi)存管理),車輛(數(shù)據(jù))就會擁堵,整個城市(計算機系統(tǒng))的運行效率就會大幅下降。
而在內(nèi)存管理這個大舞臺上,Buffer(緩沖區(qū))和 Cache(緩存)則是兩位至關(guān)重要的角色,它們默默工作,卻對系統(tǒng)性能有著深遠影響。很多人對它們的概念感到困惑,常常將二者混為一談,今天就讓我們深入探究一下這兩個神秘的存在,揭開它們的面紗,看看它們是如何在幕后助力計算機高效運行的 。
一、Buffer和Cache概述
從字面意思來看,Buffer 是緩沖區(qū),Cache 是緩存 。它們都用于在內(nèi)存中臨時存儲數(shù)據(jù),但這兩種 “臨時存儲” 又有著明顯的區(qū)別。就好像你出門旅行,會帶一個行李箱和一個隨身小包。行李箱可以類比為 Buffer,它用來存放暫時不用,但后續(xù)可能會用到的物品,這些物品就像等待傳輸?shù)狡渌O備的數(shù)據(jù)。而隨身小包就像 Cache,里面裝著你隨時可能會用到的東西,比如手機、錢包,這些物品就像被頻繁訪問的數(shù)據(jù),放在小包里能讓你快速拿到,就像數(shù)據(jù)被緩存起來能被快速讀取一樣。
在 Linux 系統(tǒng)中,我們可以通過free命令來查看內(nèi)存的使用情況 。打開終端,輸入free -h(-h參數(shù)是為了讓輸出結(jié)果更易讀),你會看到類似這樣的信息:
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 的內(nèi)存使用總和 。而實際上,free命令的統(tǒng)計數(shù)據(jù)是來自于/proc/meminfo這個文件 。用cat /proc/meminfo命令查看,會看到更詳細的內(nèi)存信息,其中Buffers和Cached這兩個字段,分別對應著 Buffer 和 Cache 使用的內(nèi)存大小 。
那么,Buffers、Cached和SReclaimable具體是什么含義呢?Buffers是內(nèi)核緩沖區(qū)用到的內(nèi)存,它主要用于緩存磁盤的數(shù)據(jù) 。比如,當我們向磁盤寫入數(shù)據(jù)時,數(shù)據(jù)不會立刻被寫入磁盤,而是先存儲在Buffers中,等積累到一定量或者滿足特定條件時,再統(tǒng)一寫入磁盤,這樣可以減少磁盤 I/O 的次數(shù),提高寫入效率 。Cached是內(nèi)核頁緩存和 Slab 用到的內(nèi)存,主要用于緩存從文件讀取的數(shù)據(jù) 。
當我們讀取一個文件時,數(shù)據(jù)會被緩存到Cached中,如果下次再讀取相同的文件內(nèi)容,就可以直接從內(nèi)存中獲取,大大加快了讀取速度 。SReclaimable是 Slab 的一部分,Slab 是內(nèi)核中用于管理內(nèi)存的一種機制,SReclaimable表示 Slab 中可回收的部分 。
二、Buffer與Cache工作原理
2.1Buffer的工作原理
Buffer 就像是數(shù)據(jù)傳輸過程中的一個臨時停靠站 。當計算機與不同速度的設備進行數(shù)據(jù)交換時,比如內(nèi)存與硬盤之間,由于硬盤的讀寫速度相對內(nèi)存來說非常慢,如果沒有 Buffer,內(nèi)存就需要一直等待硬盤完成數(shù)據(jù)傳輸,這會極大地浪費內(nèi)存的性能 。而有了 Buffer,當內(nèi)存要向硬盤寫入數(shù)據(jù)時,數(shù)據(jù)會先被存儲到 Buffer 中 。假設我們要將一個大文件寫入硬盤,文件數(shù)據(jù)會按一定大小的塊依次存入 Buffer 。
當 Buffer 中的數(shù)據(jù)達到一定量(比如一個磁盤塊的大小),或者滿足特定的寫入條件(如操作系統(tǒng)的寫入調(diào)度策略)時,這些數(shù)據(jù)就會被一次性寫入硬盤 。這樣做的好處是,減少了硬盤的讀寫次數(shù) 。因為如果每次有少量數(shù)據(jù)就直接寫入硬盤,硬盤的磁頭需要頻繁移動來定位數(shù)據(jù)位置,這會花費大量時間 。而通過 Buffer 的緩沖,將分散的小寫入操作合并成大的寫入操作,大大提高了數(shù)據(jù)傳輸?shù)男?。
同時,在數(shù)據(jù)讀取時,Buffer 也起著類似的作用 。當從硬盤讀取數(shù)據(jù)時,硬盤會將數(shù)據(jù)先讀取到 Buffer 中,內(nèi)存再從 Buffer 中讀取數(shù)據(jù),這就避免了內(nèi)存直接與速度較慢的硬盤頻繁交互,保證了數(shù)據(jù)傳輸?shù)姆€(wěn)定性和高效性 。
當應用程序請求從磁盤讀取數(shù)據(jù)時,內(nèi)核會先檢查Buffer中是否已經(jīng)存在相應的數(shù)據(jù)塊。如果存在,內(nèi)核會直接從Buffer返回數(shù)據(jù),避免了對物理磁盤的讀取。如果數(shù)據(jù)不在Buffer中,內(nèi)核會將數(shù)據(jù)塊從磁盤讀取到Buffer中,并返回給應用程序。這樣,Buffer在一定程度上減少了對磁盤的訪問次數(shù),提高了I/O性能。
相關(guān)系統(tǒng)參數(shù)
(1)dirty_ratio
echo 20 > /proc/sys/vm/dirty_ratio
或
sysctl -w vm.dirty_ratio=20
- 作用: dirty_ratio 參數(shù)定義了系統(tǒng)內(nèi)存中臟頁(已被修改但尚未寫入磁盤)的最大比例。當臟頁的比例達到或超過此值時,系統(tǒng)將啟動同步寫入操作,將臟頁寫入磁盤。
- 影響: 控制臟頁的及時寫入,適當設置有助于避免頻繁的磁盤寫入操作。
- 配置方式(參數(shù)的單位是百分比)
(2)dirty_background_ratio
echo 10 > /proc/sys/vm/dirty_background_ratio
或
sysctl -w vm.dirty_background_ratio=10
- 作用:dirty_background_ratio 參數(shù)定義了當臟頁的比例超過此值時,系統(tǒng)會觸發(fā)后臺寫入操作。后臺寫入是指將臟頁異步地寫入磁盤,不會引起進程阻塞。
- 影響: 控制后臺寫入的啟動條件,避免系統(tǒng)過早地觸發(fā)寫入操作,從而提高系統(tǒng)性能。
- 配置方式, 可通過修改 /proc/sys/vm/dirty_background_ratio 文件或使用 sysctl 命令進行配置。(參數(shù)的單位是百分比)
2.2Cache:加速系統(tǒng)的秘密武器
Cache 的工作原理基于程序的局部性原理 ,即程序在一段時間內(nèi)訪問的數(shù)據(jù)往往集中在一個較小的區(qū)域 。它就像一個數(shù)據(jù)的 “快速通道”,將經(jīng)常訪問的數(shù)據(jù)復制到內(nèi)存中速度更快的區(qū)域 。以數(shù)據(jù)庫查詢?yōu)槔斘覀儓?zhí)行一個數(shù)據(jù)庫查詢語句時,查詢結(jié)果會被存儲在 Cache 中 。如果下次再執(zhí)行相同或相似的查詢語句,系統(tǒng)會首先檢查 Cache 中是否已經(jīng)存在該結(jié)果 。如果存在,就直接從 Cache 中讀取數(shù)據(jù)返回給用戶,而不需要再次執(zhí)行復雜的數(shù)據(jù)庫查詢操作 。這大大減少了數(shù)據(jù)訪問的時間,提高了系統(tǒng)的響應速度 。
Cache 通常采用一些替換算法,如最近最少使用(LRU)算法,來決定當 Cache 空間不足時,哪些數(shù)據(jù)需要被替換出去 。LRU 算法會將最近一段時間內(nèi)最少被訪問的數(shù)據(jù)替換掉,這樣可以保證 Cache 中始終保存著最有可能被再次訪問的數(shù)據(jù) 。除了數(shù)據(jù)庫查詢,Cache 在 CPU 與內(nèi)存之間也起著重要作用 。
由于 CPU 的運行速度遠遠快于內(nèi)存,為了減少 CPU 等待內(nèi)存數(shù)據(jù)的時間,在 CPU 和內(nèi)存之間設置了 Cache 。CPU 首先會在 Cache 中查找需要的數(shù)據(jù),如果找到(命中),就可以快速獲取數(shù)據(jù)進行處理;如果沒找到(未命中),才會從內(nèi)存中讀取數(shù)據(jù),并將讀取的數(shù)據(jù)同時存入 Cache 中,以便下次訪問時能夠更快獲取 。
相關(guān)系統(tǒng)參數(shù)
(1)vfs_cache_pressure
echo 100 > /proc/sys/vm/vfs_cache_pressure
或
sysctl -w vm.vfs_cache_pressure=100
- 作用: vfs_cache_pressure 參數(shù)用于調(diào)整內(nèi)核對 dentry 和 inode 緩存的傾向性。較大的值使內(nèi)核傾向于回收 dentry,而較小的值使內(nèi)核傾向于回收 inode。
- 影響: 控制文件系統(tǒng)緩存的回收策略,影響文件系統(tǒng)性能。較大的值有助于加速緩存的回收,從而釋放內(nèi)存。
- 配置方式: 可通過修改 /proc/sys/vm/vfs_cache_pressure 文件或使用 sysctl 命令進行配置。
例如:swappiness
echo 10 > /proc/sys/vm/swappiness
或
sysctl -w vm.swappiness=10
- 作用:swappiness 參數(shù)用于調(diào)整內(nèi)核在內(nèi)存不足時將數(shù)據(jù)移動到交換空間的傾向性。值的范圍是 0 到 100,0 表示盡量不使用交換空間,100 表示盡量使用交換空間。
- 影響: 控制系統(tǒng)對交換空間的利用,較小的值有助于減少對交換空間的使用,提高整體性能。
- 配置方式: 可通過修改 /proc/sys/vm/swappiness 文件或使用 sysctl 命令進行配置。
3.3Buffer和Cache的區(qū)別
(1)存儲內(nèi)容
- Buffer存儲的是I/O操作的數(shù)據(jù)塊,通常是對物理設備的讀寫請求的中介。
- Cache存儲的是文件系統(tǒng)的數(shù)據(jù)塊,包括文件的元數(shù)據(jù)和實際內(nèi)容。
(2)讀取方式
- Buffer主要用于減少對物理設備的讀寫次數(shù),通過緩存I/O操作提高性能。
- Cache更側(cè)重于文件系統(tǒng)的讀取,通過緩存文件數(shù)據(jù)和元數(shù)據(jù)提高文件系統(tǒng)的整體讀取速度。
(3)清理策略
- Buffer中的數(shù)據(jù)通常被操作系統(tǒng)維護,不容易手動清理。
- Cache的內(nèi)容可以通過手動或自動的方式進行清理,以釋放內(nèi)存空間。
三、實戰(zhàn)案例分析
為了更直觀地理解 Buffer 和 Cache 的工作方式,我們通過幾個實際案例來進行分析 。在 Linux 系統(tǒng)中,我們可以使用vmstat命令來實時監(jiān)控內(nèi)存和 I/O 的使用情況 。打開終端,輸入vmstat 1(1表示每秒輸出一次數(shù)據(jù)),可以看到如下信息:
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 使用的內(nèi)存大小,單位是 KB ,bi和bo分別表示塊設備讀取和寫入的大小,單位為塊 / 秒 。由于 Linux 中塊的大小是 1KB,所以這個單位也就等價于 KB/s 。
3.1磁盤和文件寫案例
我們先來模擬第一個場景;首先,在第一個終端,運行下面這個vmstat 命令:
# 每隔1秒輸出1組數(shù)據(jù)
$ 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
輸出界面里, 內(nèi)存部分的 buff 和 cache ,以及 io 部分的 bi 和 bo 就是我們要關(guān)注的重點。
- buff 和 cache 就是我們前面看到的 Buffers 和 Cache,單位是 KB。
- bi 和 bo 則分別表示塊設備讀取和寫入的大小,單位為塊/秒。因為 Linux 中塊的大小是 1KB,所以這個單位也就等價于 KB/s。
正常情況下,空閑系統(tǒng)中,你應該看到的是,這幾個值在多次結(jié)果中一直保持不變。
接下來,到第二個終端執(zhí)行 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 的輸出,我們發(fā)現(xiàn),在dd命令運行時, Cache在不停地增長,而Buffer基本保持不變。
再進一步觀察I/O的情況,你會看到,在 Cache 剛開始增長時,塊設備 I/O 很少,bi 只出現(xiàn)了一次 488 KB/s,bo 則只有一次 4KB。而過一段時間后,才會出現(xiàn)大量的塊設備寫,比如 bo 變成了122880。
當 dd 命令結(jié)束后,Cache 不再增長,但塊設備寫還會持續(xù)一段時間,并且,多次 I/O 寫的結(jié)果加起來,才是 dd 要寫的 500M 的數(shù)據(jù)。
把這個結(jié)果,跟我們剛剛了解到的Cache的定義做個對比,你可能會有點暈乎。為什么前面文檔上說 Cache 是文件讀的頁緩存,怎么現(xiàn)在寫文件也有它的份?
這個疑問,我們暫且先記下來,接著再來看另一個磁盤寫的案例。兩個案例結(jié)束后,我們再統(tǒng)一進行分析。
不過,對于接下來的案例,我必須強調(diào)一點:下面的命令對環(huán)境要求很高,需要你的系統(tǒng)配置多塊磁盤,并且磁盤分區(qū) /dev/sdb1 還要處于未使用狀態(tài)。如果你只有一塊磁盤,千萬不要嘗試,否則將會對你的磁盤分區(qū)造成損壞。
如果你的系統(tǒng)符合標準,就可以繼續(xù)在第二個終端中,運行下面的命令。清理緩存后,向磁盤分區(qū)/dev/sdb1 寫入2GB的隨機數(shù)據(jù):
# 首先清理緩存
$ echo 3 > /proc/sys/vm/drop_caches
# 然后運行dd命令向磁盤分區(qū)/dev/sdb1寫入2G數(shù)據(jù)
$ dd if=/dev/urandom of=/dev/sdb1 bs=1M count=2048
然后,再回到終端一,觀察內(nèi)存和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
從這里你會看到,雖然同是寫數(shù)據(jù),寫磁盤跟寫文件的現(xiàn)象還是不同的。寫磁盤時(也就是bo大于 0 時),Buffer和Cache都在增長,但顯然Buffer的增長快得多。
這說明,寫磁盤用到了大量的Buffer,這跟我們在文檔中查到的定義是一樣的。
對比兩個案例,我們發(fā)現(xiàn),寫文件時會用到 Cache 緩存數(shù)據(jù),而寫磁盤則會用到 Buffer 來緩存數(shù)據(jù)。所以,回到剛剛的問題,雖然文檔上只提到,Cache是文件讀的緩存,但實際上,Cache也會緩存寫文件時的數(shù)據(jù)。
3.2磁盤和文件讀案例
了解了磁盤和文件寫的情況,我們再反過來想,磁盤和文件讀的時候,又是怎樣的呢?
我們回到第二個終端,運行下面的命令;清理緩存后,從文件/tmp/file中,讀取數(shù)據(jù)寫入空設備:
# 首先清理緩存
$ echo 3 > /proc/sys/vm/drop_caches
# 運行dd命令讀取文件數(shù)據(jù)
$ dd if=/tmp/file of=/dev/null
然后,再回到終端一,觀察內(nèi)存和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 的輸出,你會發(fā)現(xiàn)讀取文件時(也就是bi大于0時),Buffer保持不變,而Cache則在不停增長。這跟我們查到的定義“Cache是對文件讀的頁緩存”是一致的。
那么,磁盤讀又是什么情況呢?
我們再運行第二個案例來看看:首先,回到第二個終端,運行下面的命令。
清理緩存后,從磁盤分區(qū) /dev/sda1中讀取數(shù)據(jù),寫入空設備:
# 首先清理緩存
$ echo 3 > /proc/sys/vm/drop_caches
# 運行dd命令讀取文件
$ dd if=/dev/sda1 of=/dev/null bs=1M count=1024
然后,再回到終端一,觀察內(nèi)存和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 的輸出,你會發(fā)現(xiàn)讀磁盤時(也就是bi大于0時),Buffer和Cache都在增長,但顯然Buffer的增長快很多。這說明讀磁盤時,數(shù)據(jù)緩存到了 Buffer 中。
當然,我想,經(jīng)過上一個場景中兩個案例的分析,你自己也可以對比得出這個結(jié)論:讀文件時數(shù)據(jù)會緩存到 Cache 中,而讀磁盤時數(shù)據(jù)會緩存到 Buffer 中。
到這里你應該發(fā)現(xiàn)了,雖然文檔提供了對Buffer和Cache的說明,但是仍不能覆蓋到所有的細節(jié)。比如說,今天我們了解到的這兩點:
- Buffer既可以用作“將要寫入磁盤數(shù)據(jù)的緩存”,也可以用作“從磁盤讀取數(shù)據(jù)的緩存”。
- Cache既可以用作“從文件讀取數(shù)據(jù)的頁緩存”,也可以用作“寫文件的頁緩存”。
這樣,我們就回答了案例開始前的兩個問題:簡單來說,Buffer是對磁盤數(shù)據(jù)的緩存,而Cache是文件數(shù)據(jù)的緩存,它們既會用在讀請求中,也會用在寫請求中。