作者簡介
小輝,一線碼農,有多年豐富嵌入式操作系統開發工作經驗。先后在南大富士通,某手機大廠和初創小公司任軟件工程師,系統架構師等職務。對嵌入式系統開發和os性能優化工作很熟悉。在此特別鳴謝!
引子
自己做單機存儲性能優化工作已經一年多了,平時組內做的優化點需要系統總結一下了。
怎么能夠找到手機存儲性能優化方面的技術特性,怎么去凸顯手機存儲里面碰到的獨特性能優化問題,有了這些需求,所以我需要寫一點東西了。
因為工作繁忙,只好寫成了類似bbs和筆記的風格,不過雖然這樣,我保證下面文字是我對手機存儲性能架構方面的原創心得,網上根本搜不到下面這樣的文字。
手機安卓系統IO特性
業務方面特性
手機存儲對應的都是單機存儲架構。出存儲性能問題時,往往直觀的表現是桌面運行卡頓,
需要分析用戶上傳的bugreport,然后自己根據用戶場景做問題復現,然后通過分析內核ftrace log, 還有借助一些性能排查工具去解決該性能問題。
技術方面特性
1: buffer IO很多,異步IO,direct IO很少
手機中異步IO,direct IO操作應該很少,基本上都是buffer IO(經過page cache的IO),所以出存儲性能問題時,尋找根本原因時,往往是內存原因和IO原因交織混雜在一塊,表現出來的原因,并不是孤立的IO模塊的原因。
內存原因有內存回收,內存slow path分配,IO原因有具體的文件系統,還有塊設備層IO調度器。
2: sqlite數據庫引發的IO操作很多
這個是手機系統的一個很重要的IO特色,詳見三星公司出的那篇經典Android IO特性分析論文。
Android 手機里面大部分寫都是數據庫sqlite的單筆小量數據的頻繁同步隨機寫(就是write+fsync),而不是服務器場景中出現的文件寫。
3:手機里面大部分都是中低速存儲芯片,比如emmc, ufs。所以內核層的over head不高,需要利用到內核的文件系統,還有IO調度器
這個涉及到內核存儲架構問題了。
1)現有的存儲系統,在分布式和大數據應用場景下,通常會用些高速存儲芯片(比如nvme接口的),內核現有的存儲架構不能有效的發揮作用。
具體表現在每一筆的IO下發,在內核存儲棧產生的over head太重了,并且還不能充分地利用存儲芯片的IO帶寬。
尤其是塊設備層那些傳統的single queue設計,IO 請求排序和合并,cfq調度器,傳統文件系統自身局限性等這些內核的設計思想,都不符合高速存儲的需求。
所以現有的分布式和大數據存儲場景,都是會bypass 內核,直接做成用戶態driver去直接讀寫存儲芯片的,比如現在的dpdk, spdk架構。
所以現在內核也被迫的調整存儲架構,io_urging, 塊設備層的blk-mq多隊列設計,bfq調度器等這些存儲架構的調整都是為了跟上當前硬件上,存儲芯片性能不斷暴漲的節奏。
2)但是手機場景是比較特殊的,因為用的還是emmc,ufs這種存儲芯片速度不是特別快的場景,再加上每天的實際存儲數據寫入耗時占每天總時長的比例是比較小的(sandisk有存儲芯片打點數據分析的),再加上上面的2點IO特性,所以手機場景下,還是會用到內核傳統的文件系統,塊設備層設計思想,會用到這些傳統的內核存儲架構。
4:會頻繁異常掉電,所以對文件系統穩定性要求高
服務器場景會有ups電源,可能很少會有系統頻繁掉電的場景出現。
但是手機場景,據我們分析,就算用戶選擇的是正常關機流程,也會很多時候出現因為文件系統卸載超時導致的實際上走的也是類似于這種異常掉電流程(就是雖然不是異常掉電,但是走的流程,還有從對存儲數據穩定性影響方面評估,跟異常掉電場景區別不大)。
所以就更不用提很多時候,用戶關不了機時(系統按鍵無響應時),往往采取的強制掉電關機或者掉電重啟。
5:會有前后臺任務管理方面的工作需求
因為安卓手機通常后臺跑的做IO工作的進程數量是很多的,同時用戶在前臺操作手機時,前臺的任務不希望被后臺任務所干擾。
隨著內存越來越大,還有安卓本來就是多任務系統,所以安卓后臺跑的進程數量是比較多的。但是用戶在前臺操作app或者圖形界面時,是不希望受到后臺進程的干擾影響的。
6:手機會出現存儲碎片化厲害的場景,這樣對IO性能會影響比較大。
手機里面大部分都是存儲的小文件,但是手機存儲容量往往由于成本和具體應用原因,不會容量像大數據或者分布式的存儲容量那么大。
而且隨著手機應用場景越來越豐富,比如視頻,聊天,當做電腦一樣在應用手機,所以手機本來存儲容量就不大,再加上使用時間長和強度高后,存儲碎片化現象肯定越來越嚴重(具體表現在文件系統碎片化)。
這樣對IO性能會有大的影響的。
7:會有ext4文件系統的自身局限性帶來的一些問題
設計ext4的時候,當時都是機械硬盤為主,內存資源不充足的時代,現在的時代是以flash為主,手機已經稍微邁入了大內存時代。
ext4的局限性:
1)原地更新數據特性,會加重存儲芯片里面的IO寫放大問題。
2)ext4+jbd2的journal架構設計。
這個設計雖然是為了實現文件系統日志功能,它采用了類似c語言式的簡單直觀的設計思想(ext4社區ted有相關郵件說明),同時采取physical logging,也是為了追求復用內存,節省內存。
但是這種類c語言的設計思想,雖然當初設計時,可以做到簡單高效。但是不好的地方在于:
1> 隨著多核處理器架構的普及,這種設計思想帶來的性能瓶頸問題越來越厲害。
2> 這種設計思想容易造成內核的IO優先級倒置問題(具體見下面的fsync慢原因分析)。
3)是針對機械硬盤進行設計的,沒有突出flash - aware.
綜合這些ext4自身局限性,造成了對手機IO性能的不良影響,所以 f2fs出現了。
8:會結合emmc/ufs存儲芯片里面的ftl固件實現思想去定位性能問題
1)因為解決的是單機存儲性能問題,所以想要精確評估性能優化方案會對手機帶來多大的性能提升,需要對存儲芯片里面的固件工作原理,工作思想有所了解。
2)手機換成了f2fs文件系統后,因為f2fs文件系統的設計初衷就是flash - aware,所以更需要了解存儲芯片里面的固件后,才能準確把握和ext4相比,f2fs帶來的性能優化效果。
手機存在的存儲性能問題
針對上面羅列的Android系統的一些IO特性,手機存在的存儲性能問題如下
1:針對特性1,2,3和5,會有ext4的fsync問題
原因在于下面幾點:
1)Android系統里面sqlite寫數據時,sqlite自身的日志架構設計,會導致頻繁下發fsync。這樣后臺fsync下發多了,前臺或者system server里面下發fsync就會受到影響(fsync耗時會增加很多),
2)同時手機安卓系統還有一些原生特性,就是jbd2工作在order模式下的, 同時又開啟了ext4 延遲分配功能,這樣也會加重fsync耗時。
1> order模式加重耗時原因:
order模式下,jbd2不得不去在commit thread里面多了個等待臟iNode的數據全部刷到盤上的工作。所以jbd2 commit thread耗時增加,會帶來fsync的耗時也會增加不少,
因為fsync最后一步是要等待自身的事務被commit thread處理完成。
2> 內核存儲IO優先級倒置加重耗時原因:
CFQ I/O調度器在調度io請求時,會優先去調度同步的io請求,這樣會導致異步io請求的處理時間變長。應用調用fsync的時候,fsync自身下發的IO請求都是同步的,這是從存儲設計角度,為了追求提升fsync性能。
但是系統如果剛好正在進行內存臟頁回寫,這個時候fsync需要等待臟頁回寫對應的異步io請求完成后,fsync的工作才能算做完。這樣就會導致fsync耗時。
3> 延遲分配加重耗時原因:
延遲分配會加重單次fsync的負擔,因為系統flush thread是每隔30秒觸發的(Android系統里面),
然后flush thread 在前,fsync在后的話,容易fsync卡在order模式中的等待file data flush到disk中 這個地方,等很久
(因為之前的flush thread里面已經為臟的iNode分配了很多物理blocks,并且把這些臟iNode(每隔30s去觸發flush thread的話,是會產生很多臟iNode的)注冊到了jbd2的transaction里面,
jbd2里面多了個額外等待這些臟iNode里面臟數據被刷到磁盤上的操作)。
flush thread 在后,fsync在前的話,沒有這個問題。
3)ext4的實時discard機制也會造成fsync慢的問題。
這樣在手機Android系統里面,由于上面種種原因,造成fsync響應慢,然后就會表現出手機前臺操作卡頓或者卡死直接安卓層重啟的現象。
2:針對特性6,會有文件系統碎片化導致存儲IO性能下降的問題
手機data分區碎片化厲害后,會導致:
1)手機的寫性能(順序寫,隨機寫性能,direct寫)會下降的(之前我詳細分析過原因的,并挖掘過性能下降的具體場景)。
2)手機的sqlite數據庫IO性能也會下降很多的(之前跟存儲芯片廠商共同定位過該下降的原因)。
3:針對特性1,3和5,會有前后臺IO管理的問題
1)特殊場景出現的問題:后臺U盤拷貝時,前臺安卓桌面操作卡頓問題出現。
原因:
i:U盤拷貝數據量大時,導致內存里面臟頁變多,然后可用free內存變少。
ii:然后前臺手機操作時,會alloca_pages,這個時候會觸發內存回收,去回收臟頁,這樣io又變多,阻塞了申請內存的前臺用戶進程。
2)通用場景出現的問題:后臺做的IO任務異常活躍,干擾了前臺任務的運行,需要前后臺任務進行協作。
原因:
i:? Android的IO調度器為cfq,并未區分前后臺。這樣后臺app或者進程正在進行IO工作時,會搶占前臺app的IO帶寬和內存資源。
現在的核心優化需求是怎么保證用戶在前臺操作app,或者圖形界面時,一直都是流暢的,即使后臺跑再多的任務,對前臺也沒有影響。
4:針對特性4,會有文件系統穩定性問題
存儲性能優化會對文件系統做一些深入修改,以前的測試強度還不夠。
對文件系統深入修改后,以前的測試case還不能深入進行文件系統測試。
5:針對特性7,存在的問題上面已經做分析了
6:針對特性8,會存在需要存儲芯片廠商的支援和配合問題
ftl固件是存儲芯片廠商的專利,給我們暴露的存儲芯片使用接口太少。他們雖然很配合我們的性能優化工作,但是通常涉及到他們芯片固件實現的一些敏感問題,會回避我們。
需要我們自己去理解ftl的一些通用設計思想,另外聯合采購人員,一起去跟存儲芯片廠商進行性能問題的溝通和解決。
具體問題場景如下:
1)部署無感垃圾回收優化方案時,需要準確的評估該優化會帶來哪些IO性能提升。
2)上面的碎片整理對Android sqlite數據庫IO性能的提升的根本原因調研。
現有的性能優化措施
針對上面羅列的手機存在的若干存儲性能問題,具體優化工作如下:
問題1的優化:
1)無感垃圾回收優化,是針對上面的原因3
2)ext4 fsync專項優化,是針對上面的原因2
3)sqlite io優化,是針對上面的原因1
上面是結合手機業務場景,做的簡單有效的優化方案,最根本最徹底的優化方案是下面的:
4)社區的fast commit方案 (正在開發中,有這一個方案就可以徹底解決問題,不需要上面3個了)。
問題2的優化:
就是目前的ext4碎片整理方案。具體有兩個優化措施:
1)compact整理,解決的是上面說的寫性能下降的問題。
2)單文件碎片整理,解決的是上面說的sqlite數據IO性能下降的問題。
問題3的優化:
1)針對上面的U盤拷貝導致的性能問題,社區有相關優化方案,具體是對內存回收方面做點優化。
2)針對這個前后臺的性能問題,結合手機業務場景方面的優化有IO限速方案。
3)徹底的最根本的優化是前后臺分組,即運用cgroup v2分組方案,前臺給予比較多的IO和內存資源請求,后臺給予少點,目前我有所調研,做過一些優化方案設計。
(cgroup v2對于buffer io會有些優先級倒置問題,部分原因跟那個mmap_sem鎖整個進程地址空間的缺陷是相關的,目前盡量在高版本內核上(大于4.14的)做cgroup v2方案)
問題4的優化:
就是需要導入目前的xfstest,以便對穩定性測試方面加大測試強度。
問題5的優化:
針對上面說的ext4文件系統的局限性,需要用f2fs來代替ext4了。
大內存時代,讀性能沒有寫性能那么需要得到工程師的迫切優化,因為內存大,很多文件第一次讀完后,就緩存在內存里面了。
往往是寫性能會導致出現存儲IO性能問題。
所以f2fs針對手機寫場景,做的優化如下:
1)對數據庫寫專門做了優化(sqlite原子寫).
2)提出了將隨機寫轉換為順序寫,還有copy on write思想.
3)針對手機存儲芯片的一些物理特性,做了冷熱數據分離功能.
這樣優化措施,可以減少了flash的寫放大問題(寫放大,會導致用戶長期使用手機后,會出現flash壞塊增多,會導致存儲芯片自身固件做GC時間增多,這樣IO性能就會下降了),
可以提升Android的隨機寫性能,同時又是logical logging,所以沒有jbd2那個導致的fsync問題出現。
所有的這些f2fs優化,都是針對手機寫場景做的針對性優化,ext4是沒有的。
所以為了突破現有的ext4架構上導致的存儲性能問題缺陷,所以f2fs在手機場景應該取代ext4。
上f2fs后,有兩個性能優化點需要關注:
1)f2fs的gc效率(重點是后臺的)
2)f2fs的冷熱數據分離效果。
這兩點的優化好壞能關系到f2fs的dirty segments數目降低多少。只有dirty segments數目降低了,free segments數目增加的多了,系統的IO性能才能好起來。
問題6的優化:
1)自己可以看這本書:深入淺出ssd,仔細理解該書后,會對ftl的設計思想有所理解。
2)已經解決了上面的問題場景2,問題場景1正在解決中。
2)已經進行了幾次存儲芯片原廠舉辦的關于存儲芯片內部軟硬件實現的培訓會議,通過這些會議,已經對ftl固件,還有存儲芯片的一些物理特性有所理解。
PS:
優化點上面已經羅列出來了,具體的各個優化點實現內容,由于涉及保密方面原因,不便在文章公開了。感興趣可與作者溝通交流
評論
查看更多