1. 程式人生 > 其它 >Cache和Buffer都是快取,主要的區別是什麼?

Cache和Buffer都是快取,主要的區別是什麼?

Cache和Buffer都是快取,主要的區別是什麼?

知乎討論話題連結:

https://www.zhihu.com/question/26190832

cache是為了彌補高速裝置和低速裝置的鴻溝而引入的中間層,最終起到加快訪問速度的作用。
而buffer的主要目的是進行流量整形,把突發的大資料較小規模的I/O整理成平穩的小數量較大規模的 I/O,以減少響應次數(比如從網上下電影,你不能下一點點資料就寫一下硬碟,而是積攢一定量的資料以後一整塊一起寫,不然硬碟都要被你玩壞了)。

這兩者還是有相關性的。首先cache是快取,buffer是緩衝,雖然翻譯有那麼一個字的不同,但這不是重點。
為了說明這個問題,讓我將他們分開來說:read cache(讀快取),read buffer(讀緩衝),write cache(寫快取),write buffer(寫緩衝)。
無論快取還是緩衝,其實本質上解決的都是讀寫速度不匹配的問題,從這個角度,他們非常相似。

首先討論讀快取跟讀緩衝。讀快取跟讀緩衝的最大區別在於,讀快取的目標資料是始終有效的,如果不從快取中讀取,也可以直接讀取實際資料,只不過實際資料讀取會慢一些,當這個資料在快取中,讀取速度將會變快。當一個快取中的資料被多次讀取,實際上就減少了該資料從慢速裝置中讀取的量,這就存在某種演算法去選擇「什麼資料需要儲存在cache中」,因為儘可能多的讓cache命中能提高效能。先進入cache的資料不一定先被讀取,甚至說進入cache的資料有可能永遠不被讀取就被清除了,因此read cache呈現出非常明顯的隨機訪問特性。

而讀緩衝buffer的資料則不是始終有效,而是實時生成的資料流,每當buffer滿或者主動flush buffer的時候觸發一次讀取,對於小資料,這樣可以減少讀取次數,對於大資料,這可以控制單次讀取的資料量。換句話說,無論資料量大還是小,單次讀取資料量都按照buffer尺寸進行歸一化了。通常來說,先餵給buffer的資料一定會先被讀取,所有buffer的資料幾乎一定會被讀取,這是很明顯的順序訪問特性。
【注】

從上面的情況看到,讀快取以及讀緩衝很明確的反應出了我所說的表面特性。而其本質特性在於cache的目標是減少讀取總量每次cache命中都減小了讀取總量。而buffer並不能減少讀取總量,只能規整化每次讀取資料的尺寸大小。

說到write cache跟write buffer?

我們先說write buffer,write buffer是read buffer的對應,對於小資料的寫入,它需要填滿write buffer再進行一次寫入,對於大資料,大資料會被分割到buffer尺寸的大小分批寫入。因此,write buffer 的用處在於使得每次寫入的資料量相對固定。如果一次寫入4k對某個裝置來說效率最高,那麼把buffer定為4k,小資料積攢到4k寫一次,大資料分割到每個碎片4k多次寫入,這樣就是write buffer的用處。最後我們來說write cache。所謂write cache,就是要設法減少寫入次數。也就是說,如果某些資料需要產生多次寫入,那麼使用cache就可以只將最終資料寫入,導致最終寫入資料減少。在實際應用中,我們有時會使用到write buffer跟write cache的合體形態。buffer本身需要規整尺寸,與此同時,buffer還允許多次隨機寫入,使得多次寫入的資料只用寫入最後一次,這屬於cache的特性。BT軟體使用的寫快取往往具有類似特性,因而這種形態它同時既是buffer又是cache。正因為在寫入場合buffer跟cache沒有那麼明顯的分界,所以才會有產生buffer跟cache究竟有啥區別的疑問。

簡單結論:

  1. Buffer不是快取,國內常用的翻譯是緩衝區。
  2. 其次,大部分場景中,Buffer是特指記憶體中臨時存放的IO裝置資料——包括讀取和寫入;而Cache的用處很多——很多IO裝置(例如硬碟、RAID卡)上都有Cache,CPU內部也有Cache,瀏覽器也有Cache。
  3. Buffer並非用於提高效能,而Cache的目的則是提高效能。
  4. 涉及到IO裝置讀寫的場景中,Cache的一部分本身就是Buffer的一種。如果說某些場合Buffer可以提升IO裝置的讀寫效能,只不過是因為Buffer本身是Cache系統的一部分,效能提升來自於Cache機制。
  5. Buffer佔用的記憶體不能回收,如果被強行回收會出現IO錯誤。Cache佔用的記憶體,除實現Buffer的部分外都可以回收,代價則是下一次讀取需要從資料的原始位置(通常是效能更低的裝置)讀取。
  6. 在IO讀寫過程中,任何資料的讀寫都必然會產生Buffer,但根據Cache演算法,可能會有相當部分資料不會被Cache。

以硬碟讀寫操作為例的完整解釋

背景知識一:我們現在的計算機、手機都是馮諾依曼架構,CPU只能操作記憶體中的資料,無法直接操作硬碟上的資料。
背景知識二:硬碟上的資料,最小讀寫單位是扇區(Sector)。老式硬碟上一個扇區是512位元組,現代硬碟上一個扇區是4K位元組。計算機不能以單個位元組為單位訪問硬碟上的資料。現在很常見的固態硬碟,物理上最小讀寫單位是頁(Page),但大部分固態硬碟通過主控晶片模擬傳統硬碟的扇區來進行讀寫。現代硬碟常用的LBA(Logical Block Addressing,邏輯塊定址)定址方式,是把硬碟上的扇區分配從0~N-1的編號(N為硬碟上所有可用扇區數量)。

介紹完背景,假設某個應用現在需要讀取一個大小為15K位元組的檔案A。作業系統和檔案系統會把檔案路徑轉換為具體的LBA地址,可能最終轉換為讀取硬碟上從B扇區開始的4個扇區(按照每個扇區4KB計算)。然而,前面我們說了,CPU並不能直接訪問硬碟,因此需要先把這四個扇區的資料,傳輸到記憶體中。存放這四個扇區資料的記憶體,就是Buffer。忽略CPU內部的Cache機制,CPU現在可以對這一段記憶體以位元組為單位進行操作,在所有操作完成後,Buffer所佔用的記憶體會被回收。

寫入則是相反,應用程式需要先在記憶體中準備好這四個扇區的資料,然後硬碟控制器會把這些資料原樣寫入到硬碟對應的扇區上。同樣的,寫入完成後Buffer所佔用的記憶體也會被回收。

除了用於臨時存放IO裝置上的資料,Buffer通常還有其它幾種用途:

  1. 把多次小量資料傳輸合併為更少次數的批量資料傳輸, 減少傳輸過程本身的額外開銷;
  2. 為兩個不能直接交換資料的傳輸程序的提供臨時中介儲存;
  3. 成單次傳輸規定的最小單位;
  4. 對大塊資料進行組裝或者分解。

如果這個應用需要頻繁讀取檔案A,每次都從硬碟讀取顯然會很慢。如果第一次讀取完成後,不直接清空Buffer所佔用的記憶體,而是把這段記憶體保留下來或者先複製到其它記憶體地址,以後對這個檔案的讀取就可以直接從記憶體訪問,無需再次從硬碟讀取,應用程式的效能就會快很多。這才是Cache,嚴謹點來說,這是Read Cache,所以臺灣把Cache翻譯為“快取”,更多的是指Read Cache。但是,並不是所有從硬碟上讀取到Buffer的資料都會被Cache的,例如複製一個包含多個數GB的視訊檔案的資料夾,通常只有這個資料夾的資料會被Cache,而每個具體的視訊檔案的資料都不會進入Cache。

有Read Cache自然也有Write Cache。還是這個佔用四個扇區的檔案,假如應用程式需要先更改第一個扇區的內容並寫入硬碟,過一段時間再更改第三個扇區的內容並寫入硬碟。這樣需要對硬碟進行兩次寫入。但如果第一次應用要求寫入的時候,作業系統只是把這個檔案的資料寫入到記憶體中並返回寫入完成的響應,但資料並沒有真正寫入硬碟。等收到後續寫入請求的時候才真正寫入硬碟,則只需要進行一次寫入。通過這樣的方式,根據實際情況可能實現:

  1. 應用程式無需等待真正的寫入完成即可繼續後續操作,提高應用程式效能;
  2. 減少寫入次數;
  3. 把多個小資料量的寫入合併成一個大資料量的寫入;
  4. 把多個隨機寫入轉換為持續寫入.

這幾種情況中的一種或者多種,從而提高IO效能。但是,對於首次寫入來說,這個效能是必然更低的——假設直接寫入需要0.02秒,因為要等待後續的寫入請求,可能從發起首次寫入請求,到資料真正寫入硬碟用了0.5秒。這就是國內把Cache翻譯為“快取”的原因——暫快取儲。所以其實“快取”和“快取”都只是表達了一半的意思,不存在說“快取”比“快取”翻譯的更好——雖然大部分時候Read Cache比Write Cache更常見。

需要另外提一下的是,Write Cache同時也是Buffer的一種形式,在資料寫入到硬碟前,是不能被回收的。
最後,Read Cache和Write Cache並不是嚴格分離的。很多時候Write Cache同時也可以作為Read Cache使用,但在分散式系統中,則需要考慮Cache一致性的問題。

在Linux上如何清除記憶體的Cache、Buffer和Swap

如何在Linux中清除快取Cache?

每個Linux系統中有三種選項來清除快取而不需要中斷任何程序或服務

Cache:快取,指CPU和記憶體之間快取記憶體
Buffer:緩衝區,指在寫入磁碟前的儲存在記憶體中的內容

  • 僅清除頁面快取(PageCache)
sync;echo 1 > /proc/sys/vm/drop_caches
  • 清除目錄和inode
sync;echo 2 > /proc/sys/vm/drop_caches
  • 清除頁面快取,目錄項和inode
sync;echo 3 > /proc/sys/vm/drop_caches

說明:
sync 將重新整理檔案系統緩衝區(buffer),命令通過“;”分隔,順序執行,shell在執行序列中的下一個命令之前會等待命令的終止。正如核心文件中提到的,寫入到drop_cache將清空快取而不會殺死任何應用程式/服務,echo命令做寫入檔案的工作。

如果你必須清除磁碟快取記憶體,第一個命令在企業和生產環境中是最安全,
"...echo 1> ..."
只會清除頁面快取。 在生產環境中不建議使用上面的第三個選項
"...echo 3 > ..."
,除非你明確自己在做什麼,因為它會清除快取頁,目錄項和inodes。

在Linux上釋放也許被核心所使用的緩衝區(Buffer)和快取(Cache)是否是個好主意?

當你設定許多設定想要檢查效果時,如果它實際上是專門針對 I/O 範圍的基準測試,那麼你可能需要清除緩衝區和快取。你可以如上所示刪除快取,無需重新啟動系統(即無需停機)。
Linux被設計成它在尋找磁碟之前到磁碟快取尋找的方式。如果它發現該資源在快取中,則該請求不會發送到磁碟。如果我們清理快取,磁碟快取就起不到作用了,系統會到磁碟上尋找資源。
此外,當清除快取後它也將減慢系統執行速度,系統會將每一個被請求的資源再次載入到磁碟快取中。
現在,我們將建立一個 shell 指令碼,通過一個 cron 排程任務在每天下午2點自動清除RAM快取。如下建立一個 shell 指令碼 clearcache.sh 並在其中新增以下行

#!/bin/bash
# 注意,我們這裡使用了 "echo 3",但是不推薦使用在產品環境中,應該使用 "echo 1"
echo "echo 3 > /proc/sys/vm/drop_caches"

給clearcache.sh檔案設定執行許可權

# chmod 755 clearcache.sh

現在,當你需要清除記憶體快取時只需要呼叫指令碼。
現在設定一個每天下午2點的定時任務來清除RAM快取,開啟crontab進行編輯。

crontab -e

新增以下行,儲存並退出。

0 3 * * * /path/to/clearcache.sh

在生產環境的伺服器上自動清除RAM是否是一個好主意?

不!它不是。想想一個情況,當你已經預定指令碼在每天下午2點來清除記憶體快取。那麼其時該指令碼會執行並重新整理你的記憶體快取。在某一天由於某些原因,可能您的網站的線上使用者會超過預期地從你的伺服器請求資源。
而在這時,按計劃排程的指令碼運行了,並清除了快取中的一切。當所有的使用者都從磁碟讀取資料時,這將導致伺服器崩潰並損壞資料庫。因此,清除快取僅在必要時並且在你的預料之中,否則你就是個呆瓜系統管理員。

如何清除Linux的交換空間

swapoff -a && swapon -a

此外,瞭解有關風險後,您可以將上面的命令新增到cron中。
現在,我們將上面兩種命令結合成一個命令,寫成正確的指令碼來同時清除RAM快取和交換空間。

echo 3 > /proc/sys/vm/drop_caches && swapoff -a && swapon -a && printf '\n%s\n' 'Ram-cache and Swap Cleared'

su -c 'echo 3 > /proc/sys/vm/drop_caches' && swapoff -a && swapon -a && printf '\n%s\n' 'Ram-cache and Swap Cleared'