IO偵探:多進程寫ceph-fuse單文件性能瓶頸偵查

近期接到ceph用戶報案,說是多進程direct寫ceph-fuse的單個文件,性能很低,幾乎與單進程direct寫文件的性能同樣。關乎民生,刻不容緩,筆者當即展開偵查工做~node

1、復現案情,尋蹤追記

筆者經過fio工具展開測試,分別測試了單進程和多進程direct隨機寫ceph-fuse的單個文件的性能狀況。linux

fio -filename=/mnt/fuse/file_1T -direct=1  -rw=randwrite -ioengine=sync -bs=4K -size=1G  -numjobs=1 -runtime=180 -group_reporting -name=test fio -filename=/mnt/fuse/file_1T -direct=1 -rw=randwrite -ioengine=sync -bs=4K -size=1G -numjobs=16 -runtime=180 -group_reporting -name=test 

結果顯示,不管單進程,仍是多進程,並且不管多少進程,iops結果差很少都是50左右(磁盤未開write-cache)。那麼,多進程寫多個文件呢,性能又是怎樣的?bash

這裏注意了,將fio的參數filename改爲directory後,對於多進程,fio會爲每一個進程建立一個測試文件。函數

fio -directory=/mnt/fuse/ -direct=1  -rw=randwrite -ioengine=sync -bs=4K -size=1G -numjobs=16 -runtime=180 -group_reporting -name=test 

結果顯示,iops大概是800左右,正好是單進程50的16(進程數)倍,怎麼這麼巧?工具

2、分析案情,鎖定可能的嫌疑人

寫同一個文件,單進程和多進程具備相同的iops性能,說明多進程在io路徑的某個環節變成了串行,從而致使多進程實際上也變成了單進程。性能

咱們先來看下ceph-fuse的io鏈條:測試

用戶態程序fio -> vfs -> kernel fuse -> libfuse -> ceph-fuse -> [mds] -> ceph clusterui

筆者以前看過鏈條上一些環節的代碼,但因爲代碼量太大,代碼的細節太多,因此認知並不深刻,沒法直接定位到兇手。spa

爲了幫助分析案情,這裏簡單描述下這條io鏈:
用戶態程序fio經過多進程進行direct隨機寫,經過vfs後進入kernel fuse,kernel fuse會將io請求放入到pending隊列,而後等待結果。用戶態libfuse的多個進程經過/dev/fuse設備讀取pending隊列中請求,而後調用更底層的ceph-fuse進行處理。ceph-fuse最終將請求發送給mds或者ceph集羣進行處理。code

必然是上述io鏈條中某個環節致使了案情的發生。然而咱們很難熟知於io鏈條中的每一環,因此咱們須要根據更多已知的線索來縮小io鏈條的排查範圍。

3、蒐集線索,縮小排查範圍

目前已知的線索有:

① fio多進程和單進程寫同一文件性能幾乎一致
② fio多進程寫進程數個文件的性能 = 單進程性能 * 進程數

因爲沒有更多的線索,筆者開始對io鏈條進行排查。先從熟悉的ceph-fuse、mds和ceph cluster開始。

嫌疑人 分析
ceph cluster 對比多進程寫單文件和多進程寫多文件的性能,說明出現案情時,ceph cluster不是瓶頸
mds 因爲fio測試是單客戶端的,因此多進程的寫也不會競爭fw的caps,另外fio的測試,涉及mds的請求不多,因此mds做案的可能性很小
ceph-fuse 這是筆者懷疑的重點,衆所周知,ceph-fuse有把client大鎖,經過閱讀代碼發現,ceph-fuse在發送寫請求給osd後就釋放了client鎖,並無持鎖等待結果,這雖然對寫性能有必定的影響,但不至於將多進程io串行

筆者展開更多測試,發現了一條重要的線索:

③ 16進程direct隨機寫16個文件時,經過kernel fuse的controlfs(/sys/fs/fuse/connections/39/waiting)看到等待的請求數一直是16,而16進程direct隨機寫單文件時,等待的請求數一直是1

線索③說明,在libfuse以前,多進程已經被串行化,這樣能夠基本排除libfuse、ceph-fuse、mds、ceph cluster的嫌疑了,筆者能夠把精力放在io鏈條的前面了。

用戶態程序fio -> vfs -> kernel fuse

靜下心來,思索下...
多進程寫單個文件,io被徹底串行化,憑經驗,要麼是有文件鎖,要麼有inode鎖。先朝這方向走一下,咱們須要定位下鎖在io鏈條的哪一個環節,逐個分析下每一個環節。

用戶態程序fio:
經過查看fio的man page,筆者發現fio有個lockfile的參數,當lockfile設置成exclusive或者readwrite時,多進程的寫會被串行化,然而該參數的默認值是none,因此能夠排除用戶態程序fio的嫌疑。

vfs:
vfs是內核很薄的一層,並且linux上能夠經過flock和fcntl對文件進行加鎖,在沒有用戶態程序調用的狀況下,vfs顯然不會私自對文件和inode加鎖,而且,這個案情在kernel cephfs客戶端下不存在,因此能夠排除vfs的嫌疑。

排除了其餘io環節,最後只剩下 kernel fuse ,是筆者熟悉程度相對比較低的模塊,看來須要重點招待了。

4、蒐集證據,鎖定兇手

筆者從新閱讀kernel fuse的io流程代碼,最終拿到證據,真相是如此簡單....

static ssize_t fuse_direct_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos){ ... /* Don't allow parallel writes to the same file */ mutex_lock(&inode->i_mutex); res = __fuse_direct_write(&io, &iov, 1, ppos); if (res > 0) fuse_write_update_size(inode, *ppos); mutex_unlock(&inode->i_mutex); ... } 

兇手做案手法:
多進程direct寫io到kernel fuse的該函數後,拿到inode鎖,進行io請求,io請求發送到pending隊列後,會持鎖等待io請求結束,從而致使多進程的direct寫io被串行化。

5、關注筆者

專一筆者公衆號,閱讀更多幹貨文章:)

相關文章
相關標籤/搜索