放大缩小保证div对齐_NFS Write IO 不对齐深度分析
導讀:NFSClient 對大多數的應用寫入沒有做對齊優化,本文根據 IO 不對齊的原因給出了若干實踐建議。
作者 | 裴曉輝
背景
最近團隊小伙伴弗曼統計了線上用戶數據寫入對齊情況,通過統計數據發現了一個有趣的現象: 用戶寫入請求中近 70% 的數據塊 4K 不對齊,這也就是說 NFSClient 對大多數的應用寫入沒有做對齊優化。
下面會從 NFSClient BufferWrite 實現流程的維度解釋 IO 不對齊的原因,最后據此給出了若干實踐建議。
場景分析
應用程序一般可以使用 DirectIO 或 BufferIO 兩種方式向文件寫入數據。在 DirectIO 模式時 NFSClient 直接將用戶 IO 通過 RPC 發送給服務端,因此 DirectIO 方式寫入 NFSClient 不會做對齊,但考慮到應用程序使用 DirectIO 時一般會在應用側做對齊,因此非對齊 IO 的多數應該是 BufferIO 方式。
內核使用 struct nfs_req 對象記錄某個緩存頁更改情況,同時使用 struct page 對象的 private 字段保存,因此只需分析 BufferIO 時 nfs_req 的處理邏輯就能夠知道對齊規則。NFSClient 調用 nfs_updatepage() 更新 nfs_req 對象,其核心代碼如下:
從上圖紅色框中代碼可以看出來,NFSClient 會嘗試按照緩存頁大小對 offset/count 對齊,繼續查看 nfs_can_extent_write() 函數實現:
從上述代碼可知:
1) 若為同步寫,則不嘗試對齊,這是因為對同步寫做對齊并沒有明顯收益還會放大 IO;
2) 若緩存頁內容不是最新,則不允許對齊,否則就要讀懲罰,注意 nfs_write_begin() 函數已處理是否需要讀懲罰;
3) 若持有 Write Delegation,則允許對齊,因為 Write Delegation 保證本地緩存數據一定是最新,關于 Delegation 可參考文章《NFSClient Delegation 實現與陷阱》;
4) 若不持有文件鎖,則允許對齊,注意此處依據 NFSClient 設計哲學的一個假設:應用在不持有文件鎖寫入數據時,應用側應該保證文件不會被多客戶端修改;
5) 若持有全文件的寫鎖,則允許對齊,因為寫鎖保證了本地緩存數據肯定是有效的且不存在多客戶端更改,此外這里要求全文件鎖的原因是為了簡化代碼邏輯。
合并規則
上面提到內核使用 struct nfs_req 對象表示每個緩存頁的更改情況,考慮到 struct nfs_req 只能表示單區間,因此 BufferIO 需合并同一個緩存頁的多次更改,合并規則較為簡單:
兩個寫合并后必須是一個連續的區間。
相關代碼實現可參考 nfs_try_to_update_request() 函數,代碼較為簡單,故不再詳細描述。從合并規則還可以知道:同一個緩存頁上的兩個非連續 IO 需要兩次 RPC 寫入。
緩存頁 UpdateToDate 設置
從場景分析中知道緩存頁是否設置了 UpdateToDate 標記是同一緩存頁上的兩個 IO 能否合并的重要前提條件,那 NFSClient 是什么時候將緩存頁設置為 UpdateToDate 狀態的呢?總的來說,NFSClient 在兩個地方嘗試設置該標記:
1) 將緩存頁加入到 address space 時,實現代碼是 nfs_write_begin() 函數,相關邏輯如下:
繼續查看 nfs_want_read_modify_write() 函數實現:
上圖中藍色框中是 pNFS 相關處理暫時不做討論,紅色框則是一般情況下將緩存頁加入到 address space 時是否需要讀懲罰(讀取數據后緩存頁內容自然為最新),具體的需滿足如下幾個條件:
a) 若應用打開文件模式允許讀,則允許讀懲罰,這是因為根據數據的局部性原理,剛寫入的數據很可能再次讀;
b) 緩存頁內容不是最新,若已經是最新很顯然沒必要再次讀老數據;
c) 當前緩存頁沒有正在進行的 IO,也就是說沒有向服務端有讀寫請求,注意 nfs_req 只能表示單個 IO,顯然此時不允許讀;
d) 本次修改只是緩存頁的局部內容,顯然如果全覆蓋是沒有必要讀入老數據;
2) 當內核將待修改的數據拷貝到緩存頁后會調用 nfs_write_end() 函數通知 NFSClient 數據已經拷貝到緩存頁,此時 NFSClient 需根據寫入情況設置緩存頁是否為最新的狀態:
通過上面代碼可知:
a) 藍色框中表示緩存頁是文件變大時新追加的緩存頁,此時緩存頁內容為全零,自然可設置為最新;
b) 紅色框中表示緩存頁中所有的有效數據均被覆蓋,此時緩存頁內容必然為最新;
總之,數據寫入后若完全覆蓋該緩存頁的所有原有效數據,則設置為最新。
實踐建議
至此基本搞清楚了 NFSClient BufferWrite 的對齊實現邏輯,感興趣的同學可自行編寫測試用例驗證。結合對齊實現和測試驗證,初步的可給出如下建議:
1) O_SYNC 方式不會做緩存頁對齊;
2) 當文件被大量小塊 IO 重復覆蓋寫時,可考慮用 O_RDWR 方式打開(注意副作用是會有讀懲罰),有利于聚合同一個緩存頁的寫 IO,減少 RPC 次數;
3) 使用 O_WRONLY 方式打開時,同一緩存頁的不連續更改不會做聚合,每個 IO 都會觸發一次 RPC,降低訪問性能;
4) 使用類 MPI 方式多客戶端并發修改同一文件時,條帶大小應該做到緩存頁對齊,否則可能會導致數據被錯誤覆蓋;
5) 不恰當的使用文件鎖會導致不做緩存頁對齊;
6) 不使用文件鎖時小塊 IO 可能會緩存頁對齊,導致 IO 放大。
總結
緩存頁對齊是提高 BufferIO 訪問性能地有效手段,NFSClient 在設計上盡量會嘗試緩存頁對齊,但受限于 NFS 共享特性的約束,也只能對較為有限的情況做緩存頁對齊,這就潛在地要求應用側配合才可以達到最優性能。
總結
以上是生活随笔為你收集整理的放大缩小保证div对齐_NFS Write IO 不对齐深度分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python与机械教育初探_Python
- 下一篇: r语言的MASS包干什么的_R语言综述的