1、為什么需要IO加速
傳統(tǒng)的機(jī)械磁盤在尋址時需要移動磁頭到目標(biāo)位置,移動磁頭的操作是機(jī)械磁盤性能低下的主要原因,雖然各種系統(tǒng)軟件或者IO調(diào)度器都致力于減少磁頭的移動來提高性能,但大部分場景下只是改善效果。一般,一塊SATA機(jī)械磁盤只有300左右的4K隨機(jī)IOPS,對于大多數(shù)云主機(jī)來說,300的隨機(jī)IOPS哪怕獨(dú)享也是不夠的,更何況在云計(jì)算的場景中,一臺物理宿主機(jī)上會有多臺云主機(jī)。因此,必須要有其他的方法來大幅提升IO性能。
早期SSD價格昂貴,采用SSD必然會帶來用戶使用成本的提升。于是,我們開始思考能否從技術(shù)角度來解決這個問題,通過對磁盤性能特性的分析,我們開始研發(fā)第一代IO加速方案。即使今天SSD越來越普及,機(jī)械盤憑借成本低廉以及存儲穩(wěn)定的特點(diǎn),仍然廣泛應(yīng)用,而IO加速技術(shù)能讓機(jī)械盤滿足絕大多數(shù)應(yīng)用場景的高IO性能需求。
2IO加速原理及第一代IO加速
機(jī)械磁盤的特性是隨機(jī)IO性能較差,但順序IO性能較好,如前文中提到的4K隨機(jī)IO只能有300 IOPS的性能,但其順序IO性能可以達(dá)到45000 IOPS。
IO加速的基本原理就是利用了機(jī)械磁盤的這種性能特性,首先系統(tǒng)有兩塊盤:一塊是cache盤,它是容量稍小的機(jī)械盤,用來暫時保存寫入的數(shù)據(jù);另一塊是目標(biāo)盤,它是容量較大的機(jī)械盤,存放最終的數(shù)據(jù)。
1. IO的讀寫
寫入時將上層的IO順序的寫入到cache盤上,因?yàn)槭琼樞虻姆绞綄懭胨孕阅芊浅:,然后在cache盤空閑時由專門的線程將該盤的數(shù)據(jù)按照寫入的先后順序回刷到目標(biāo)盤,使得cache盤保持有一定的空閑空間來存儲新的寫入數(shù)據(jù)。
為了做到上層業(yè)務(wù)無感知,我們選擇在宿主機(jī)內(nèi)核態(tài)的device mapper層(簡稱dm層)來實(shí)現(xiàn)該功能,dm層結(jié)構(gòu)清晰,模塊化較為方便,實(shí)現(xiàn)后對上層體現(xiàn)為一個dm塊設(shè)備,上層不用關(guān)心這個塊設(shè)備是如何實(shí)現(xiàn)的,只需要知道這是一個塊設(shè)備可以直接做文件系統(tǒng)使用。
按照上述方式,當(dāng)新的IO寫入時,dm層模塊會將該IO先寫入cache盤,然后再回刷到目標(biāo)盤,這里需要有索引來記錄寫入的IO在cache盤上的位置和在目標(biāo)盤上的位置信息,后續(xù)的回刷線程就可以利用該索引來確定IO數(shù)據(jù)源位置和目標(biāo)位置。我們將索引的大小設(shè)計(jì)為512字節(jié),因?yàn)榇疟P的扇區(qū)是512字節(jié),所以每次的寫入信息變成了4K數(shù)據(jù)+512字節(jié)索引的模式,為了性能考慮,索引的信息也會在內(nèi)存中保留一份。
讀取過程比較簡單,通過內(nèi)存中的索引判斷需要讀取位置的數(shù)據(jù)是在cache盤中還是在目標(biāo)盤中,然后到對應(yīng)的位置讀取即可。
寫入的數(shù)據(jù)一般為4K大小,這是由內(nèi)核dm層的特性決定的,當(dāng)寫入IO大于4K時,dm層默認(rèn)會將數(shù)據(jù)切分,比如寫入IO是16K,那么就會切分成4個4K的IO;如果寫入數(shù)據(jù)不是按照4K對齊的,比如只有1024字節(jié),那么就會先進(jìn)行特殊處理,首先檢查該IO所覆蓋的數(shù)據(jù)區(qū),如果所覆蓋的內(nèi)存區(qū)在cache盤中有數(shù)據(jù),那么需要將該數(shù)據(jù)先寫入目標(biāo)盤,再將該IO寫入目標(biāo)盤,這個處理過程相對比較復(fù)雜,但在文件系統(tǒng)場景中大部分IO都是4K對齊的,只有極少數(shù)IO是非對齊的,所以并不會對業(yè)務(wù)的性能造成太大影響。
2. 索引的快速恢復(fù)與備份
系統(tǒng)在運(yùn)行過程中無法避免意外掉電或者系統(tǒng)關(guān)閉等情況發(fā)生,一個健壯的系統(tǒng)必須能夠在遇到這些情況時依然能保證數(shù)據(jù)的可靠性。當(dāng)系統(tǒng)啟動恢復(fù)時,需要重建內(nèi)存中的索引數(shù)據(jù),這個數(shù)據(jù)在cache盤中已經(jīng)和IO數(shù)據(jù)一起寫入了,但因?yàn)樗饕情g隔存放的,如果每次都從cache盤中讀取索引,那么,數(shù)據(jù)的恢復(fù)速度會非常慢。
為此,我們設(shè)計(jì)了內(nèi)存索引的定期dump機(jī)制,每隔大約1小時就將內(nèi)存中的索引數(shù)據(jù)dump到系統(tǒng)盤上,啟動時首先讀取該dump索引,然后再從cache盤中讀取dump索引之后的最新1小時內(nèi)的索引,這樣,就大大提升了系統(tǒng)恢復(fù)的啟動時間。
依據(jù)上述原理,UCloud自研了第一代IO加速方案。采用該方案后,系統(tǒng)在加速隨機(jī)寫入方面取得了顯著效果,且已在線上穩(wěn)定運(yùn)行。
3. 第一代IO加速方案存在的問題
但隨著系統(tǒng)的運(yùn)行,我們也發(fā)現(xiàn)了一些問題。
1)索引內(nèi)存占用較大
磁盤索引因?yàn)樯葏^(qū)的原因最小為512字節(jié),但內(nèi)存中的索引其實(shí)沒有必要使用這么多,過大的索引會過度消耗內(nèi)存。
2)負(fù)載高時cache盤中堆積的IO數(shù)據(jù)較多
IO加速的原理主要是加速隨機(jī)IO,對順序IO因?yàn)闄C(jī)械盤本身性能較好不需要加速,但該版本中沒有區(qū)分順序IO和隨機(jī)IO,所有IO都會統(tǒng)一寫入cache盤中,使得cache堆積IO過多。
3)熱升級不友好
初始設(shè)計(jì)時對在線升級的場景考慮不足,所以第一代IO加速方案的熱升級并不友好。
4)無法兼容新的512e機(jī)械磁盤
傳統(tǒng)機(jī)械磁盤物理扇區(qū)和邏輯扇區(qū)都是512字節(jié),而新的512e磁盤物理扇區(qū)是4K了,雖然邏輯扇區(qū)還可以使用512字節(jié),但性能下降嚴(yán)重,所以第一代IO加速方案的4K數(shù)據(jù)+512字節(jié)索引的寫入方式需要進(jìn)行調(diào)整。
5)性能無法擴(kuò)展
系統(tǒng)性能取決于cache盤的負(fù)載,無法進(jìn)行擴(kuò)展。
4..第二代IO加速技術(shù)
上述問題都是在第一代IO加速技術(shù)線上運(yùn)營的過程中發(fā)現(xiàn)的。并且在對新的機(jī)械磁盤的兼容性方面,因?yàn)閭鹘y(tǒng)的512字節(jié)物理扇區(qū)和邏輯扇區(qū)的512n類型磁盤已經(jīng)逐漸不再生產(chǎn),如果不對系統(tǒng)做改進(jìn),系統(tǒng)可能會無法適應(yīng)未來需求。因此,我們在第一代方案的基礎(chǔ)上,著手進(jìn)行了第二代IO加速技術(shù)的研發(fā)和優(yōu)化迭代。
1. 新的索引和索引備份機(jī)制
第一代的IO加速技術(shù)因?yàn)楸P的原因無法沿用4K+512Byte的格式,為了解決這個問題,我們把數(shù)據(jù)和索引分開,在系統(tǒng)盤上專門創(chuàng)建了一個索引文件,因?yàn)橄到y(tǒng)盤基本處于空閑狀態(tài),所以不用擔(dān)心系統(tǒng)盤負(fù)載高影響索引寫入,同時我們還優(yōu)化了索引的大小由512B減少到了64B,而數(shù)據(jù)部分還是寫入cache盤,如下圖所示:
其中索引文件頭部和數(shù)據(jù)盤頭部保留兩個4K用于存放頭數(shù)據(jù),頭數(shù)據(jù)中包含了當(dāng)前cache盤數(shù)據(jù)的開始和結(jié)束的偏移,其后是具體的索引數(shù)據(jù),索引與cache盤中的4K數(shù)據(jù)是一一對應(yīng)的關(guān)系,也就是每個4K的數(shù)據(jù)就會有一個索引。
因?yàn)樗饕旁诹讼到y(tǒng)盤,所以也要考慮,如果系統(tǒng)盤發(fā)生了不可恢復(fù)的損壞時,如何恢復(fù)索引的問題。雖然系統(tǒng)盤發(fā)生損壞是非常小概率的事件,但一旦發(fā)生,索引文件將會完全丟失,這顯然是無法接受的。因此,我們設(shè)計(jì)了索引的備份機(jī)制,每當(dāng)寫入8個索引,系統(tǒng)就會將這些索引合并成一個4K的索引備份塊寫入cache盤中(具體見上圖中的紫色塊),不滿4K的部分就用0來填充,這樣當(dāng)系統(tǒng)盤真的發(fā)生意外也可以利用備份索引來恢復(fù)數(shù)據(jù)。
在寫入時會同時寫入索引和數(shù)據(jù),為了提高寫入效率,我們優(yōu)化了索引的寫入機(jī)制,引入了合并寫入的方式:
當(dāng)寫入時可以將需要寫入的多個索引合并到一個4K的write buffer中,一次性寫入該write buffer,這樣避免了每個索引都會產(chǎn)生一個寫入的低效率行為,同時也保證了每次的寫入是4K對齊的。
2. 順序IO識別能力
在運(yùn)營第一代IO加速技術(shù)的過程中,我們發(fā)現(xiàn)用戶在做數(shù)據(jù)備份、導(dǎo)入等操作時,會產(chǎn)生大量的寫入,這些寫入基本都是順序的,其實(shí)不需要加速,但第一代的IO加速技術(shù)并沒有做區(qū)分,所以這些IO都會被寫入在cache盤中,導(dǎo)致cache盤堆積的IO過多。為此,我們添加了順序IO識別算法,通過算法識別出順序的IO,這些IO不需要通過加速器,會直接寫入目標(biāo)盤。
一個IO剛開始寫入時是無法預(yù)測此IO是順序的還是隨機(jī)的,一般的處理方式是當(dāng)一個IO流寫入的位置是連續(xù)的,并且持續(xù)到了一定的數(shù)量時,才能認(rèn)為這個IO流是順序的,所以算法的關(guān)鍵在于如何判斷一個IO流是連續(xù)的,這里我們使用了觸發(fā)器的方式。
當(dāng)一個IO流開始寫入時,我們會在這個IO流寫入位置的下一個block設(shè)置一個觸發(fā)器,觸發(fā)器被觸發(fā)就意味著該block被寫入了,那么就將觸發(fā)器往后移動到再下一個block,當(dāng)觸發(fā)器被觸發(fā)了一定的次數(shù),我們就可以認(rèn)為這個IO流是順序的,當(dāng)然如果觸發(fā)器被觸發(fā)后一定時間沒有繼續(xù)被觸發(fā),那么我們就可以回收該觸發(fā)器。
3. 無感知熱升級
第一代的IO加速技術(shù)設(shè)計(jì)上對熱升級支持并不友好,更新存量版本時只能通過熱遷移然后重啟的方式,整個流程較為繁瑣,所以在開發(fā)第二代IO加速技術(shù)時,我們設(shè)計(jì)了無感知熱升級的方案。
由于我們的模塊是位于內(nèi)核態(tài)的dm層,一旦初始化后就會生成一個虛擬的dm塊設(shè)備,該塊設(shè)備又被上層文件系統(tǒng)引用,所以這個模塊一旦初始化后就不能卸載了。為了解決這個問題,我們設(shè)計(jì)了父子模塊的方式,父模塊在子模塊和dm層之間起到一個橋梁的作用,該父模塊只有非常簡單的IO轉(zhuǎn)發(fā)功能,并不包含復(fù)雜的邏輯,因此可以確保父模塊不需要進(jìn)行升級,而子模塊包含了復(fù)雜的業(yè)務(wù)邏輯,子模塊可以從父模塊中卸載來實(shí)現(xiàn)無感知熱升級:
上圖中的binlogdev.ko就是父模塊,cachedev.ko為子模塊,當(dāng)需要熱升級時可以將cache盤設(shè)置為只讀模式,這樣cache盤只回刷數(shù)據(jù)不再寫入,等cache盤回刷完成后,可以認(rèn)為后續(xù)的寫入IO可直接寫入目標(biāo)盤而不用擔(dān)心覆蓋cache盤中的數(shù)據(jù),這樣子模塊就可以順利拔出替換了。
這樣的熱升級機(jī)制不僅實(shí)現(xiàn)了熱升級的功能,還提供了故障時的規(guī)避機(jī)制,在IO加速技術(shù)的灰度過程中,我們就發(fā)現(xiàn)有個偶現(xiàn)的bug會導(dǎo)致宿主機(jī)重啟,我們第一時間將所有cache盤設(shè)置為只讀,以避免故障的再次發(fā)生,并爭取到了debug的時間。
4. 兼容512e機(jī)械磁盤
新一代的機(jī)械磁盤以512e為主,該類型的磁盤需要寫入IO按照4K對齊的方式才能發(fā)揮最大性能,所以原先的4K+512B的索引格式已經(jīng)無法使用,我們也考慮過把512B的索引擴(kuò)大到4K,但這樣會導(dǎo)致索引占用空間過多,且寫入時也會額外占用磁盤的帶寬效率太低,所以最終通過將索引放到系統(tǒng)盤并結(jié)合上文提到的合并寫入技術(shù)來解決該問題。
5. 性能擴(kuò)展和提升
在第一代的IO加速技術(shù)中只能使用1塊cache盤,當(dāng)這塊cache盤負(fù)載較高時就會影響系統(tǒng)的性能,在第二代IO加速中,我們設(shè)計(jì)了支持多塊cache盤,并按照系統(tǒng)負(fù)載按需插入的方式,使加速隨機(jī)IO的能力隨著盤數(shù)量的提升而提升。在本地cache盤以及網(wǎng)絡(luò)cache盤都采用SATA機(jī)械磁盤的條件下,測試發(fā)現(xiàn),隨著使用的cache盤數(shù)量的增多,隨機(jī)寫的性能也得到了大幅度的提升。
- 在只使用一塊本地cache盤時,隨機(jī)寫性能可達(dá)4.7W IOPS;
- 在使用一塊本地cache盤加一塊網(wǎng)絡(luò)cache盤時,隨機(jī)寫性能可達(dá)9W IOPS;
- 在使用一塊本地cache盤加兩塊網(wǎng)絡(luò)cache盤時,隨機(jī)寫性能可達(dá)13.6W IOPS;
目前,我們已經(jīng)大規(guī)模部署應(yīng)用了第二代IO加速技術(shù)的云主機(jī)。得益于上述設(shè)計(jì),之前備受困擾的cache盤IO堆積過多、性能瓶頸等問題得到了極大的緩解,特別是IO堆積的問題,在第一代方案下,負(fù)載較高時經(jīng)常觸發(fā)并導(dǎo)致性能損失和運(yùn)維開銷,采用第二代IO加速技術(shù)后,該監(jiān)控告警只有偶現(xiàn)的幾例觸發(fā)。
5寫在最后
云主機(jī)IO加速技術(shù)極大提升了機(jī)械盤隨機(jī)寫的處理能力,使得用戶可以利用較低的價格滿足業(yè)務(wù)需求。且該技術(shù)的本質(zhì)并不單單在于對機(jī)械盤加速,更是使系統(tǒng)層面具備了一種可以把性能和所處的存儲介質(zhì)進(jìn)行分離的能力,使得IO的性能并不受限于其所存儲的介質(zhì)。此外,一項(xiàng)底層技術(shù)在實(shí)際生產(chǎn)環(huán)境中的大規(guī)模應(yīng)用,其設(shè)計(jì)非常關(guān)鍵,特別是版本熱升級、容錯以及性能考慮等都需要仔細(xì)斟酌。希望本文可以幫助大家更好的理解底層技術(shù)的特點(diǎn)及應(yīng)用,并在以后的設(shè)計(jì)中做出更好的改進(jìn)。