為什么都說NFS讀寫性能差,如何進行優化?
使用基于NFS協議存儲系統的同學經常遇到的問題是在小文件比較多的情況下性能會比較差。小文件訪問性能差本身是可以理解的,但是NFS確實是太差了。不知大家是否深層次分析過,為什么NFS訪問小文件性能會這么差?
NFS文件系統與本地文件系統的差異在于多了一個網絡傳輸的過程。因此,我們從網絡傳輸方面下手,看看能不能獲取一些線索。為了能夠捕獲協議的數據,我們向共享目錄寫入一個文件(本例為tgt.c,可以根據情況改變文件名稱),具體命令如下所示。
dd if=tgt.c of=/mnt/test/sub1/sub2/sub3/sub4/sub5/test bs=1024 count=2
如下圖是通過Wireshark抓取的網絡通信的數據包,可以看出,NFS在訪問文件的時候客戶端與服務端有的交互除了WRITE之外,還有很多其它的交互,包括ACCESS、LOOKUP和SETATTR等。并且可以看出,這些請求的數量跟目錄的深度是有關系,每一級目錄都會進行LOOKUP和ACCESS操作,這就是NFS在訪問小文件的時候性能差的一個主要原因。
圖片
我們具體分析一下這個過程,稍微簡化一下,如圖所示。可以看出,對于每一級組件(component),客戶端都會發送一個LOOKUP指令和一個ACCESS指令。這兩個指令是干什么的呢?
圖片
我們可以看一下協議文檔,以NFSv3為例,其文檔為RFC1813。如下圖是LOOPUP例程的功能描述,通過該描述可以看出LOOKUP的功能是按照名稱搜索目錄中的項目,成功的情況下返回一個句柄。從功能描述來看,這個指令也是很必要的,我們在訪問一個文件的時候,顯然必須要確保路徑是真是存在的。所以,客戶端要針對每一個組件發送一條LOOKUP指令來確保整條路徑的存在性。
圖片
另外一個指令是ACCESS,該指令的描述如下圖所示,其主要作用時確認訪問的權限。也就是當我們訪問文件的時候,客戶端需要向服務端逐組件的確認該用戶是否有權限訪問當前目錄。如果沒有相應的權限,當然不允許訪問了。
具體的權限很多,如協議中的定義,包括讀、查詢、修改、擴展、刪除和執行等操作。服務端會進行權限的確認操作,如果確認失敗則客戶端會禁止應用的訪問請求。所以,對于一個長路徑進行逐級的確認也是必須的。
圖片
由上述分析可以看出,對于一次寫操作,客戶端文件系統需要經過與服務端10多次的通信交互,訪問性能自然會差很多。基于上述分析,如果向提升性能,顯然應該減少客戶端與服務端交互的次數。如果能夠絕對信任服務端的文件路徑,一個最直接的方法是修改客戶端的邏輯,避免多級查詢。但是這種方法門檻有點高,而且有一點的隱患。在不修改代碼的情況下,我們可以遵循如下原則來一定程度上提升性能。
核心原則是減少客戶端與服務端的交互次數,因此我們在訪問文件的時候應該盡量保持文件的打開狀態,避免重復打開關閉文件,這樣NFS全路徑的逐級檢查。這種方法對NFSv4以后的版本適用,但對于NFSv3及以前的版本并不適用,因為他們是無狀態的。即使你在客戶端不關閉文件,在服務端訪問完數據后也是關閉的。
減少目錄層級,前面描述已經很清楚了。NFS會檢查每一級目錄,而且每一級目錄的檢查需要客戶端與服務端交互至少2次。如果我們盡量減少目錄層級,那么可以最大化的降低客戶端與服務端交互的次數。
避免超大目錄,也就是一個目錄中文件的數量不要太多。服務端的有些文件系統變量目錄像的效率并不高,當目錄項太多時,查找將非常耗時。
盡量使用大文件,而非小文件。似乎這個并不好實現,因為文件的大小是業務決定的,我們似乎很難控制文件的大小。但是,如果是自己開發的應用程序, 在保存數據的時候盡量以大文件的形式,而非小文件的形式,這對性能是有益的。
回頭想一下,NFS的性能問題很早就暴露了,難道NFSv4、NFSv4.1和NFSv4.2發展這么多年就沒有解決嗎?我們后續會分析更新版本NFS協議,看看他們在性能方面做了哪些優化。