1. 程式人生 > 其它 >服務佔用page cache過高問題分析

服務佔用page cache過高問題分析

問題描述

三個星期前,測試反饋重構服務部署後cache是5.6G,跑了5個小時,cache漲到53G,增漲了47.4G。

分析處理

1.對於 free 的 cache 異常,以前沒有涉獵,查詢資料,cache 作為page cache的記憶體數量,一般作為檔案系統的cache,如果cache較大,說明用到cache的檔案較多。我的服務主要負責網路通訊,核心計算會用到檔案資源,因此懷疑是核心開啟過多檔案導致cache上漲,但測試反饋老服務使用同一資源沒有該問題,由此分析,應該跟核心沒有關係,可能是我服務錯誤使用了核心導致的。

2.還是使用排除法定位問題,比較重構和老服務的區別,先註釋jemalloc,發現沒有解決問題。

3.開始review程式碼,希望發現重構服務和老服務的區別,沒有發現問題。

4.同事反饋使用 iotop 命令發現重構服務存在大量寫操作,懷疑可能是服務寫日誌導致 page cache 增加,於是關閉日誌輸出,page cache幾乎停止增加,問題確認,由於重構日誌寫的太大,導致page cache增加

5.開始解決日誌過大問題,同事建議日誌滾動可以解決問題,但是我打算分兩步操作,第一步,精簡日誌,將一些不需要info日誌修改成debug,第二步實現滾動日誌功能。

6.精簡日誌後,有一定效果,但是精簡日誌不是最終的辦法,畢竟有些日誌還是很重要的,日誌滾動功能必須實現。

7.滾動日誌,我的想法是按大小進行滾動,當列印日誌超過一定大小,直接建立一個新的日誌檔案.

8.實現功能之後,經過測試,效果幾乎沒有,記憶體上漲還是很厲害,通過 cahce 檢測工具[nocache]發現滾動的那些日誌還在 cache 中,同事建議使用 posix_fadvise() 來將檔案從page cache 中清除。

9.實現程式碼如下,但是編譯程式出現錯誤

#include <stdio.h>
#include <fcntl.h>

FILE* fp = fopen(path, "a");
if (fp) 
{
    posix_fadvise(fileno(fp), 0, 0, POSIX_FADV_DONTNEED);
    fclose(fp);
    fp 
= NULL; }

10.因為日誌原來是第三方庫的功能,我只是增加日誌滾動功能,結果一直無法編譯通過,但是寫一個demo程式直接呼叫 posix_fadvise 函式確沒有該問題,經過比較編譯命令,最終發現是 "-std=c99"帶來的影響,註釋該屬性,編譯正常。

11.再次測試,發現 cache 仍然存在,通過 cahce 檢測工具發現滾動的那些日誌還在 cache 中,再使用lsof命令發現本應該被卸載出 cache 日誌檔案居然還是某些程序引用著,因為重構服務是多程序架構,按大小滾動日誌,那麼多個程序會重複開啟每一個日誌檔案,但是可能寫一行資料就換一個檔案,這種場景下,可能會出現某些程序沒有正常關閉日誌檔案。而且按大小滾動還存在一個問題,查詢日誌特別不方便,因為我也無法確認日誌在哪個檔案中。

12.重新設計日誌滾動方案,按小時滾動日誌,這樣多程序就不會開啟自己不需要開啟日誌檔案,並且查詢日誌更加方便快。

13.修改完經過測試完美解決 cache 上漲問題。

總結

page cache過高可能是讀取了大量檔案或者大量寫檔案導致。

附件

#include <fcntl.h>
int posix_fadvise(int fd, off_t offset, off_t len, int advice);

說明:

程式可以使用posix_fadvise()宣佈將來打算以特定模式訪問檔案資料,從而允許核心執行適當的優化。

該建議適用於從fd引用的檔案中的偏移量開始並延伸len個位元組(或者如果len為0則直到檔案末尾)的(不一定存在的)區域。該建議不具有約束力。它僅代表該應用程式的期望。

允許的建議值包括:

POSIX_FADV_NORMAL
指示該應用程式不建議給出其對指定資料的訪問模式。如果沒有建議開啟檔案,這是預設假設。
POSIX_FADV_SEQUENTIAL
該應用程式期望順序訪問指定的資料(先讀取較低的偏移量,然後讀取較高的偏移量)。
POSIX_FADV_RANDOM
指定的資料將以隨機順序訪問。
POSIX_FADV_NOREUSE
指定的資料將僅被訪問一次。
在2.6.18之前的核心中,POSIX_FADV_NOREUSE具有與POSIX_FADV_WILLNEED相同的語義。這可能是一個錯誤;從2.6.18核心開始,此標誌為空。
POSIX_FADV_WILLNEED
指定的資料將在不久的將來訪問。
POSIX_FADV_WILLNEED啟動對頁面快取中指定區域的非阻塞讀取。核心可以根據虛擬記憶體負載減少讀取的資料量。 (通常將完全滿足幾兆位元組的需求,而很少有用。)
POSIX_FADV_DONTNEED
指定的資料將在不久的將來不被訪問。
POSIX_FADV_DONTNEED嘗試釋放與指定區域關聯的快取頁面。例如,在流大檔案時,這很有用。程式可能會定期請求核心釋放已使用的快取資料,以使更有用的快取頁面不會被丟棄。
丟棄部分頁面的請求將被忽略。與丟棄不需要的資料相比,保留所需的資料更好。如果應用程式要求考慮將資料丟棄,則必須將offset和len頁面對齊。
該實現可能會嘗試寫回指定區域中的髒頁,但這不能保證。任何未寫的髒頁都不會被釋放。如果應用程式希望確保將釋放髒頁,則應首先呼叫fsync(2)或fdatasync(2)。

返回值:
成功時,返回零。出錯時,返回錯誤號。

備註:
在Linux下,POSIX_FADV_NORMAL將預讀視窗設定為備用裝置的預設大小; POSIX_FADV_SEQUENTIAL將該大小加倍,並且POSIX_FADV_RANDOM完全禁用檔案預讀。這些更改不僅會影響指定的區域,還會影響整個檔案(但不會影響同一檔案的其他開啟檔案控制代碼)。

可以通過proc(5)中描述的/ proc / sys / vm / drop_caches介面清除核心緩衝區快取記憶體的內容。

通過開啟檔案,使用mmap(2)對映檔案,然後將mincore(2)應用於對映,可以獲取快照檔案駐留在緩衝區快取記憶體中的快照。