線上故障了!居然是因爲Linux磁盤緩存機制導致的

徒手擼一個Spring Boot中的starter


軟負載、硬負載,這些負載均衡知識你都會了嗎?



前言



最近遇到了一起跟磁盤相關的線上故障,藉此總結一下之前不太瞭解的Linux磁盤緩存相關的知識。
總的來說磁盤緩存出現的原因大概有兩個:第一是訪問磁盤的速度遠慢於訪問內存的速度,通過在內存中緩存磁盤內容可以提高訪問速度;第二是根據程序的局部性原理,數據一旦被訪問過,就很有可能在短時間內再次被訪問,所以在內存中緩存磁盤內容可以提高程序運行速度。


局部性原理



程序局部性原理:程序在執行時呈現出局部性規律,即在一段時間內,整個程序的執行僅限於程序中的某一部分。相應地,執行所訪問的存儲空間也侷限於某個內存區域,具體來說,局部性通常有兩種形式:時間局部性和空間局部性。
時間局部性:被引用過一次的存儲器位置在未來會被多次引用。
空間局部性:如果一個存儲器的位置被引用,那麼將來他附近的位置也會被引用。


頁緩存


Linux系統中爲了減少對磁盤的IO操作,會將打開的磁盤內容進行緩存,而緩存的地方則是物理內存,進而將對磁盤的訪問轉換成對內存的訪問,有效提高程序的速度。Linux的緩存方式是利用物理內存緩存磁盤上的內容,稱爲頁緩存(page cache)。
頁緩存是由內存中的物理頁面組成的,其內容對應磁盤上的物理塊。頁緩存的大小會根據系統的內存空閒大小進行動態調整,它可以通過佔用內存以擴張大小,也可以自我收縮以緩解內存使用壓力。
在虛擬內存機制出現以前,操作系統使用塊緩存系列,但是在虛擬內存出現以後,操作系統管理IO的粒度更大,因此採用了頁緩存機制,頁緩存是基於頁的、面向文件的緩存機制。


頁緩存的讀取



Linux系統在讀取文件時,會優先從頁緩存中讀取文件內容,如果頁緩存不存在,系統會先從磁盤中讀取文件內容更新到頁緩存中,然後再從頁緩存中讀取文件內容並返回。大致過程如下:
  1. 進程調用庫函數read發起讀取文件請求
  2. 內核檢查已打開的文件列表,調用文件系統提供的read接口
  3. 找到文件對應的inode,然後計算出要讀取的具體的頁
  4. 通過inode查找對應的頁緩存,1)如果頁緩存節點命中,則直接返回文件內容;2)如果沒有對應的頁緩存,則會產生一個缺頁異常(page fault)。這時系統會創建新的空的頁緩存並從磁盤中讀取文件內容,更新頁緩存,然後重複第4步
  5. 讀取文件返回
所以說,所有的文件內容的讀取,無論最初有沒有命中頁緩存,最終都是直接來源於頁緩存。


頁緩存的寫入



因爲頁緩存的存在,當一個進程調用write時,對文件的更新僅僅是被寫到了文件的頁緩存中,讓後將對應的頁標記爲dirty,整個過程就結束了。Linux內核會在週期性地將髒頁寫回到磁盤,然後清理掉dirty標識。
由於寫操作只會把變更寫入頁緩存,因此進程並不會因此爲阻塞直到磁盤IO發生,如果此時計算機崩潰,寫操作的變更可能並沒有發生在磁盤上。所以對於一些要求比較嚴格的寫操作,比如數據系統,就需要主動調用fsync等操作及時將變更同步到磁盤上。讀操作則不同,read通常會阻塞直到進程讀取到數據,而爲了減少讀操作的這種延遲,Linux系統還是用了“預讀”的技術,即從磁盤中讀取數據時,內核將會多讀取一些頁到頁緩存中。


回寫線程



頁緩存的回寫是由內核中的單獨的線程來完成的,回寫線程會在以下3種情況下進行回寫:
  1. 空閒內存低於閾值時。當空閒內存不足時,需要釋放掉一部分緩存,由於只有不髒的頁才能被釋放,所以需要把髒頁都回寫到磁盤,使其變爲可回收的乾淨的頁。
  2. 髒頁在內存中處理時間超過閾值時。這是爲了確保髒頁不會無限期的留在內存中,減少數據丟失的風險。
  3. 當用戶進程調用sync和fsync系統調用時。這是爲了給用戶進程提供強制回寫的方法,滿足回寫要求嚴格的使用場景。

回寫線程的實現





頁緩存的回收



Linux中頁緩存的替換邏輯是一個修改過的LRU實現,也稱爲雙鏈策略。和以前不同,Linux維護的不再是一個LRU鏈表,而是維護兩個鏈表:活躍鏈表和非活躍鏈表。處於活躍鏈表上的頁面被認爲是“熱”的且不會被換出,而在非活躍鏈表上的頁面則是可以被換出的。在活躍鏈表中的頁面必須在其被訪問時就處於非活躍鏈表中。兩個鏈表都被僞LRU規則維護:頁面從尾部加入,從頭部移除,如同隊列。兩個鏈表需要維持平衡–如果活躍鏈表變得過多而超過了非活躍鏈表,那麼活躍鏈表的頭頁面將被重新移回到非活躍鏈表中,一遍能再被回收。雙鏈表策略解決了傳統LRU算法中對僅一次訪問的窘境。而且也更加簡單的實現了僞LRU語義。這種雙鏈表方式也稱作LRU/2。更普遍的是n個鏈表,故稱LRU/n。


總結



在這次遇到的線上故障中,根本原因在於在業務邏輯中使用了臨時文件做緩存,一個臨時文件創建後如果在短時間內刪除,這時候對這個文件的操作都是在頁緩存內進行,不會實際回寫到磁盤。當程序出現問題響應變慢時,臨時文件存活時間變長,就可能會使其被回寫到磁盤上,導致磁盤壓力過大,進而影響整個系統。
回覆乾貨獲取精選乾貨視頻教程
回覆加羣加入疑難問題攻堅交流羣
回覆mat獲取內存溢出問題分析詳細文檔教程
回覆賺錢獲取用java寫一個能賺錢的微信機器人
回覆副業獲取程序員副業攻略一份

都收藏了,就點個「在看」支持一下吧!


點下在看,你最好看


本文分享自微信公衆號 - 俠夢的開發筆記(xmdevnote)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章