天天看點

你所不知道到的NVMe從核心中NVMe IO架構來看其中存在的問題IO發送過程IO響應過程從應用本身的IO Pattern來看使用NVMe問題問題分析解決問題

NVMe SSD具有高性能、低延遲時間等優點,是目前存儲行業的研究熱點之一,但在光鮮的性能下也同樣存在一些沒有廣為人知的問題,而這些問題其實對于一個生産系統而言至關重要,例如:

QoS無法做到100%保證;

讀寫混合情況下,與單獨讀相比,性能下降嚴重,且讀長尾延遲比較嚴重;

是以如何利用好NVMe盤的性能,并更好的為業務服務,我們需要從硬體,Linux核心等多個角度去剖析和解決。

從核心中NVMe IO架構來看其中存在的問題

目前Linux核心中對NVMe SSD的通路是通過MQ架構來實作的,接入NVMe驅動後直接略過IO排程器,具體實作上來說是從block layer中的通用塊層回調make_request進而打通上下層IO通路。示意圖如下,這裡面有幾個關鍵的點:

IO發送過程

MQ的架構提升性能最主要的将鎖的粒度按照硬體隊列進行拆分,并與底層SSD的隊列進行綁定,理想的情況每一個CPU都有對應的硬體發送SQ與響應CQ,這樣可以并發同時彼此之前無影響。按照NVMe SPEC協定中的标準,硬體最多支援64K個隊列,是以理想情況下硬體隊列個數将不會是我們需要擔心的地方。但是實際情況又如何呢?由于硬體隊列的增加會給NVMe SSD帶來功耗的增加,是以不同的廠商在設計硬體隊列個數時的考量是不同的,比如intel P3600支援32個隊列,intel最新的P4500支援16384個,但是SUMSUNG PM963卻隻支援到8個。那麼當CPU的個數超過硬體的隊列個數,就會出現多個CPU共用一個硬體隊列的情況,對性能就會産生影響。

下面使用SUMSUNG PM963做一個簡單的測試:

你所不知道到的NVMe從核心中NVMe IO架構來看其中存在的問題IO發送過程IO響應過程從應用本身的IO Pattern來看使用NVMe問題問題分析解決問題

整個IOPS隻有44w,性能下降12%,主要原因是多個CPU共用硬體隊列進行發送的時候會有自旋鎖争搶的影響。是以對于共用硬體隊列的情況下,如何綁定CPU是需要根據業務的特點來确定的。

IO響應過程

IO響應過程中最主要問題是中斷的balance,由于預設linux中并沒有對NVMe的中斷進行有效的綁定,是以不同的綁定政策會帶來截然不同的性能資料。不過在我們的實際測試中,雖然我們沒有做中斷的綁定,但是貌似不管是性能還是穩定性的下降并沒有那麼嚴重,什麼原因呢?根據我們的分析,這裡面最主要的原因是(後面也會提到),我們并沒有大壓力的使用NVMe盤,是以實際的應用場景壓力以及隊列深度并不大。

從應用本身的IO Pattern來看使用NVMe問題

我們在評測一個NVMe SSD的性能的時候,往往都是通過benchmark工具,例如見1, 見2。

然而這些測試的結果與業務實際使用NVMe SSD看到的性能相比差距很大。原因是因為這些性能測試存在兩個比較大的誤區,因而并不能反映生産系統的真實情況。

1.片面誇大了NVMe盤的性能

從上面兩篇文章中的測試中我們可以看到,大多數壓測中使用的隊列深度為128,并且是用libaio這樣的異步IO模式來下發IO。但是在實際應用場景中很少有這麼大的隊列深度。在這種場景下,根據“色子效應”,并不會将NVMe盤的并發性能充分發揮出來。

你所不知道到的NVMe從核心中NVMe IO架構來看其中存在的問題IO發送過程IO響應過程從應用本身的IO Pattern來看使用NVMe問題問題分析解決問題

2.低估了NVMe的長尾延遲

然而在另外一些場景下,隊列深度又會非常高(比如到1024甚至更高),在這種情況下NVMe SSD帶來的QoS長尾延遲影響比上面的benchmark的測試又嚴重得多。

是以綜合起來看,這種評測選擇了一個看上去沒啥大用的場景做了測試,是以得出的結果也對我們實際的應用基本沒有參考價值。那麼問題出在什麼地方麼?

問題分析

首先讓我們再次強調一下一般評測文章中benchmark進行的測試場景的特點:

大多是fio工具,開啟libaio引擎增加IO壓力

隊列深度到128或者256

在這種場景下确實基本都可以将NVMe盤的壓力打滿。

在展開分析問題的原因之前,我們先看看Linux核心的IO棧。

你所不知道到的NVMe從核心中NVMe IO架構來看其中存在的問題IO發送過程IO響應過程從應用本身的IO Pattern來看使用NVMe問題問題分析解決問題

在實際應用中,VFS提供給應用的接口,從IO的特點來分類,大緻上可以分為兩類:direct IO與buffer IO。使用direct IO的業務大多在應用本身就已經做了一層cache,不依賴OS提供的page cache。其他的情況大多使用的都是buffer IO。linux kernel中的block layer通過REQ_SYNC與~REQ_SYNC這兩種不同的标志來區分這兩類IO。大家常用的direct IO這個類型,核心要保證這次IO操作的資料落盤,并且當響應傳回以後,應用程式才能夠認為這次IO操作是完成的。是以是使用了這裡的REQ_SYNC标志,而對應的buffer IO,則大量使用了~REQ_SYNC的标志。讓我們一個一個看過去。

direct IO

由于在實際使用方式中AIO還不夠成熟,是以大多使用direct IO。但是direct IO是一種SYNC模式,并且完全達不到測試用例中128路并發AIO的效果。

這主要兩個方面原因:

direct io在下發過程中可能會使用檔案粒度的鎖inode->i_mutex進行互斥。

你所不知道到的NVMe從核心中NVMe IO架構來看其中存在的問題IO發送過程IO響應過程從應用本身的IO Pattern來看使用NVMe問題問題分析解決問題

前面說的IO SYNC模式

你所不知道到的NVMe從核心中NVMe IO架構來看其中存在的問題IO發送過程IO響應過程從應用本身的IO Pattern來看使用NVMe問題問題分析解決問題

也就是說,我們很難通過direct IO來達到壓滿NVMe盤的目的。如果一定要打滿NVMe盤,那麼一方面要提高程序并發,另外一方面還要提高多程序多檔案的并發。而這是生産系統中很難滿足的。

buffer IO

我們再來看看buffer IO的特點。下面我使用了比較簡單的fio通過buffer IO的模式下發,而且通過rate限速,我們發現平均下來每秒的資料量不到100MB,整個IO的特點如下:

你所不知道到的NVMe從核心中NVMe IO架構來看其中存在的問題IO發送過程IO響應過程從應用本身的IO Pattern來看使用NVMe問題問題分析解決問題

抓取了下submit_bio在每秒的調用次數并分析可以得出,buffer IO在下刷的時候并不會考慮QD的多少,而是類似aio那樣,kworker将需要下發的髒頁都會bio形式下發,而且不需要等待某些bio傳回。注意這裡面有一個細節從qusize觀察到IO最大值986,并沒有達到百K,或者幾十K,這個原因是由本身MQ的架構中tag機制nr_request決定,目前單Q設定預設值一般為1024。總之buffer IO這樣特點的結果就是突發量的高iops的寫入,buffer IO對于應用程式來說是不可見的,因為這是linux kernel的本身的刷髒頁行為。但是它帶給應用的影響确實可見的,筆者曾經總結過異步IO的延時對長尾的影響,如下圖所示,分别是buffer IO與direct IO在相同帶寬下延時表現,可以看出這延遲長尾比我們簡單的通過fio benchmark測試嚴重的多,特别是盤開始做GC的時候,抖動更加嚴重;而且随着盤的容量用着越來越多,GC的影響越來越大,長尾的影響也是越來越嚴重。

你所不知道到的NVMe從核心中NVMe IO架構來看其中存在的問題IO發送過程IO響應過程從應用本身的IO Pattern來看使用NVMe問題問題分析解決問題

在HDD的時代上面的問題同樣會存在,但是為什麼沒有那麼嚴重,原因主要是HDD大多使用CFQ排程器,其中一個特性是同步、異步IO隊列分離。并且在排程過程中同步優先級比較高,在排程搶占、時間片等都是同步優先。

解決問題

前面描述了使用NVMe硬碟的嚴重性,下面介紹一下如何解決這些問題。

(1)MQ綁定的問題,需要根據目前業務的特點,如果硬體的隊列小于目前CPU的個數,盡量讓核心業務上跑的程序分散在綁定不同硬體隊列的CPU上,防止IO壓力大的時候鎖資源的競争。

(2)中斷綁定CPU,建議下發的SQ的CPU與響應的CQ的CPU保持一緻,這樣各自CPU來處理自己的事情,互相業務與中斷不幹擾。

(3)解決direct IO狀态下長尾延遲,因為長尾延遲是本身NVMe SSD Controller帶來,是以解決這個問題還是要從控制器入手,使用的方法有WRR(Weight Round Robin),這個功能在目前主流廠商的最新的NVMe SSD中已經支援。

你所不知道到的NVMe從核心中NVMe IO架構來看其中存在的問題IO發送過程IO響應過程從應用本身的IO Pattern來看使用NVMe問題問題分析解決問題

(4)解決buffer IO狀态下長尾延遲,可以通過控制NVMe SSD處理的QD來解決,使用的NVME多隊列IO排程器,充分利用了MQ架構,根據同步寫、讀延遲動态調整異步IO的隊列,很好的解決buffer io帶來的長尾延遲。