性能提升2.58倍!阿里最快KV存儲(chǔ)引擎揭秘
阿里妹導(dǎo)讀:阿里云智能數(shù)據(jù)庫(kù)Tair團(tuán)隊(duì)主要負(fù)責(zé)自研分布式鍵值存儲(chǔ)(KVS)系統(tǒng),幾乎涵蓋了淘寶、天貓、阿里媽媽、菜鳥、釘釘、優(yōu)酷、高德等阿里巴巴所有核心業(yè)務(wù)。十多年來(lái),始終如一為阿里業(yè)務(wù)提供著高可靠、高性能、低成本的數(shù)據(jù)存儲(chǔ)與訪問服務(wù)。
01 概 述
近日,Tair團(tuán)隊(duì)的一篇論文——HotRing: A Hotspot-Aware In-Memory Key-Value Store 被FAST'20 Research Track接收 (USENIX Conference on File and Storage Techniques (FAST),CCF A類會(huì)議,存儲(chǔ)領(lǐng)域頂會(huì),2020年接受率16%)。
HotRing是Tair團(tuán)隊(duì)的創(chuàng)新性純內(nèi)存KV存儲(chǔ)引擎設(shè)計(jì)。其引擎吞吐性能可達(dá)600M ops/s,與目前最快的KVS系統(tǒng)相比,可實(shí)現(xiàn)2.58倍的性能提升。HotRing最重要的創(chuàng)新點(diǎn)是:極大的提升了KVS引擎對(duì)于熱點(diǎn)訪問的承載能力。這對(duì)于KVS系統(tǒng)的穩(wěn)定性以及成本控制尤為關(guān)鍵。
為了方便大家更通俗全面的理解這篇論文,本文將從阿里巴巴的雙十一零點(diǎn)峰值講起,介紹峰值下數(shù)據(jù)庫(kù)整體架構(gòu)所面臨的熱點(diǎn)問題,再介紹Tair團(tuán)隊(duì)在解決熱點(diǎn)方面一次次的優(yōu)化提升,最后介紹Tair的創(chuàng)新性引擎HotRing。
02 背 景
零點(diǎn)峰值
2019年天貓雙11再次刷新世界紀(jì)錄,零點(diǎn)的訂單峰值達(dá)到54.4萬(wàn)筆/秒。有訂單就涉及到交易,有交易就需要數(shù)據(jù)庫(kù)的事務(wù)保證,因此阿里巴巴數(shù)據(jù)庫(kù)將在這時(shí)面臨巨大的沖擊。
現(xiàn)實(shí)往往更加嚴(yán)峻,在業(yè)務(wù)方面,一次訂單隨著業(yè)務(wù)邏輯在后端會(huì)放大為數(shù)十次的訪問;在客戶方面,大量的客戶只是瘋狂的訪問,并沒有生成訂單。因此,在雙11的零點(diǎn)峰值,業(yè)務(wù)實(shí)際的訪問量級(jí)是10億次/秒。
Tair作為高并發(fā)分布式的KVS系統(tǒng),在這時(shí)發(fā)揮了重要作用。如下面的邏輯圖所示,Tair作為數(shù)據(jù)庫(kù)的分布式緩存系統(tǒng),緩存了大量的熱點(diǎn)數(shù)據(jù)(例如商品,庫(kù)存,風(fēng)控信息等),為數(shù)據(jù)庫(kù)抵擋了巨大的訪問量。2019年雙11,Tair的峰值訪問為9.92億次/秒。
熱點(diǎn)問題
在業(yè)務(wù)層面,熱點(diǎn)問題很好理解,最典型的就是雙十一零點(diǎn)秒殺。這會(huì)導(dǎo)致數(shù)據(jù)訪問呈現(xiàn)嚴(yán)重傾斜的冪律分布。
我們分析了多種業(yè)務(wù)的數(shù)據(jù)訪問分布,如下圖所示,大量的數(shù)據(jù)訪問只集中在少部分的熱點(diǎn)數(shù)據(jù)中,若用離散冪率分布(Zipfian)刻畫,其θ參數(shù)約為1.22。相似地,F(xiàn)acebook的一篇論文同樣也展示了近似的數(shù)據(jù)訪問分布(參考論文[3])。
直觀上可以用下圖來(lái)解釋。以蘋果新手機(jī)發(fā)售舉例。手機(jī)的庫(kù)存等信息只存在KVS的一個(gè)節(jié)點(diǎn)中。當(dāng)新手機(jī)發(fā)售后,大量的果粉瘋狂進(jìn)行搶購(gòu)下單,業(yè)務(wù)的訪問量基本都聚集在這一個(gè)節(jié)點(diǎn)上。節(jié)點(diǎn)可能無(wú)法承載大量的熱點(diǎn)訪問,進(jìn)而引發(fā)系統(tǒng)崩潰,嚴(yán)重影響用戶體驗(yàn)。
熱點(diǎn)優(yōu)化
為了保證雙十一絲般順滑的購(gòu)物體驗(yàn),Tair針對(duì)熱點(diǎn)問題進(jìn)行了多層優(yōu)化:
- 客戶端緩存:通過預(yù)先標(biāo)記熱點(diǎn),設(shè)置客戶端層面的緩存。以上圖來(lái)理解,就是將訪問在業(yè)務(wù)層面返回,直接減小了KVS系統(tǒng)的負(fù)載壓力。
- 熱點(diǎn)散列技術(shù):通過將熱點(diǎn)數(shù)據(jù)備份到多個(gè)KVS節(jié)點(diǎn)上,分?jǐn)偀狳c(diǎn)訪問。以少量成本的資源與系統(tǒng)開銷,換取了成倍的系統(tǒng)承載力。
- RCU無(wú)鎖引擎:通過采用Read-Copy-Update的方式,實(shí)現(xiàn)內(nèi)存KV引擎的無(wú)鎖化(lock-free)訪問(參考論文[1,2])。成倍提升KVS引擎的性能,進(jìn)而提高熱點(diǎn)的承載力。
- HotRing:在RCU無(wú)鎖引擎基礎(chǔ)上,我們進(jìn)行索引結(jié)構(gòu)的熱點(diǎn)感知設(shè)計(jì),提出了一種名為HotRing的新型熱點(diǎn)感知內(nèi)存KVS。HotRing可動(dòng)態(tài)識(shí)別熱點(diǎn),并實(shí)時(shí)的進(jìn)行索引結(jié)構(gòu)的無(wú)鎖調(diào)整,對(duì)于冪律分布場(chǎng)景實(shí)現(xiàn)成倍的引擎性能提升。
經(jīng)過十年的技術(shù)沉淀,我們已將集團(tuán)Tair數(shù)據(jù)庫(kù)的緩存技術(shù)釋放到云上,普惠大眾,即“阿里云Redis企業(yè)版”。
03 HotRing
現(xiàn)有技術(shù)
現(xiàn)有的內(nèi)存KVS引擎通常采用鏈?zhǔn)焦W鳛樗饕?,結(jié)構(gòu)如下圖所示。首先,根據(jù)數(shù)據(jù)的鍵值(k)計(jì)算其哈希值h(k),對(duì)應(yīng)到哈希表(Hash table)的某個(gè)頭指針(Headi)。根據(jù)頭指針遍歷相應(yīng)的沖突鏈(Collision Chain)的所有數(shù)據(jù)(Item),通過鍵值比較,找到目標(biāo)數(shù)據(jù)。如果目標(biāo)數(shù)據(jù)不在沖突鏈中(read miss),則可在沖突鏈頭部插入該數(shù)據(jù)。
在鏈?zhǔn)焦K饕Y(jié)構(gòu)中,訪問位于沖突鏈尾部的數(shù)據(jù),需要經(jīng)過更多的索引跳數(shù),即更多次的內(nèi)存訪問。很直觀的想法是,如果可以將熱點(diǎn)數(shù)據(jù)放置在沖突鏈頭部,那么系統(tǒng)對(duì)于熱點(diǎn)數(shù)據(jù)的訪問將會(huì)有更快的響應(yīng)速度。
但是,數(shù)據(jù)在沖突鏈中的位置由數(shù)據(jù)的插入順序決定,這和數(shù)據(jù)的冷熱程度是互相獨(dú)立的。因此,如圖所示,熱點(diǎn)數(shù)據(jù)(Hot Item)在沖突鏈中的位置是完全均勻分布。
設(shè)計(jì)挑戰(zhàn)
理想的設(shè)計(jì)也很直觀,就是將所有熱點(diǎn)數(shù)據(jù)移動(dòng)到?jīng)_突鏈的頭部。但有兩方面因素使得這個(gè)問題非常難解。一方面,數(shù)據(jù)的熱度是動(dòng)態(tài)變化的,必須實(shí)現(xiàn)動(dòng)態(tài)的熱點(diǎn)感知保證熱點(diǎn)時(shí)效性。另一方面,內(nèi)存KVS的引擎性能是很敏感的(一次訪問的時(shí)延通常是100ns量級(jí)),必須實(shí)現(xiàn)無(wú)鎖的熱點(diǎn)感知維持引擎的高并發(fā)與高吞吐特性。
HotRing整體設(shè)計(jì)
HotRing在傳統(tǒng)鏈?zhǔn)焦K饕A(chǔ)上,實(shí)現(xiàn)了有序環(huán)式哈希索引設(shè)計(jì)。如下圖所示,將沖突鏈?zhǔn)孜策B接形式?jīng)_突環(huán),保證頭指針指向任何一個(gè)item都可以遍歷環(huán)上所有數(shù)據(jù)。然后,HotRing通過lock-free移動(dòng)頭指針,動(dòng)態(tài)指向熱度較高的item(或根據(jù)算法計(jì)算出的最優(yōu)item位置),使得訪問熱點(diǎn)數(shù)據(jù)可以更快的返回。
下面通過如下4方面進(jìn)行介紹:
- 設(shè)計(jì)1:為什么要實(shí)現(xiàn)為有序環(huán)?
- 設(shè)計(jì)2:如何動(dòng)態(tài)識(shí)別熱點(diǎn)并調(diào)整頭指針?
- 設(shè)計(jì)3:如何保證無(wú)鎖的并發(fā)訪問?
- 設(shè)計(jì)4:如何根據(jù)熱點(diǎn)數(shù)據(jù)量的動(dòng)態(tài)變化進(jìn)行無(wú)鎖rehash?
設(shè)計(jì)1——有序環(huán)
實(shí)現(xiàn)環(huán)式哈希索引后,第一個(gè)問題是要保證查詢的正確性。若為無(wú)序環(huán),當(dāng)一個(gè)read miss操作遍歷沖突環(huán)時(shí),它需要一個(gè)標(biāo)志來(lái)判斷遍歷何時(shí)終止,否則會(huì)形式死循環(huán)。但是在環(huán)上,所有數(shù)據(jù)都會(huì)動(dòng)態(tài)變化(更新或刪除),頭指針同樣也會(huì)動(dòng)態(tài)移動(dòng),沒有標(biāo)志可以作為遍歷的終止判斷。
利用key排序可以解決這個(gè)問題,若目標(biāo)key介于連續(xù)兩個(gè)item的key之間,說明為read miss操作,即可終止返回。由于實(shí)際系統(tǒng)中,數(shù)據(jù)key的大小通常為10~100B,比較會(huì)帶來(lái)巨大的開銷。哈希結(jié)構(gòu)利用tag來(lái)減少key的比較開銷。
如下圖所示,tag是哈希值的一部分,每個(gè)key計(jì)算的哈希值,前k位用來(lái)哈希表的定位,后n-k位作為沖突鏈中進(jìn)一步區(qū)分key的標(biāo)志。為了減小排序開銷,我們構(gòu)建字典序:order = (tag, key)。先根據(jù)tag進(jìn)行排序,tag相同再根據(jù)key進(jìn)行排序。
下圖比較了HotRing與傳統(tǒng)鏈?zhǔn)焦?。以itemB舉例,鏈?zhǔn)焦P枰闅v所有數(shù)據(jù)才能返回read miss。而HotRing在訪問itemA與C后,即可確認(rèn)B read miss。因此針對(duì)read miss操作,鏈?zhǔn)焦P枰闅v整個(gè)沖突鏈;而HotRing利用字典序,不僅可以正確終止,且平均只需遍歷1/2沖突環(huán)。
設(shè)計(jì)2——動(dòng)態(tài)識(shí)別與調(diào)整
HotRing實(shí)現(xiàn)了兩種策略來(lái)實(shí)現(xiàn)周期性的熱點(diǎn)識(shí)別與調(diào)整。每R次訪問為一個(gè)周期(R通常設(shè)置為5),第R次訪問的線程將進(jìn)行頭指針的調(diào)整。兩種策略如下:
- 隨機(jī)移動(dòng)策略:每R次訪問,移動(dòng)頭指針指向第R次訪問的item。若已經(jīng)指向該item,則頭指針不移動(dòng)。該策略的優(yōu)勢(shì)是, 不需要額外的元數(shù)據(jù)開銷,且不需要采樣過程,響應(yīng)速度極快。
- 采樣分析策略:每R次訪問,嘗試啟動(dòng)對(duì)應(yīng)沖突環(huán)的采樣,統(tǒng)計(jì)item的訪問頻率。若第R次訪問的item已經(jīng)是頭指針指向的item,則不啟動(dòng)采樣。
采樣所需的元數(shù)據(jù)結(jié)構(gòu)如下圖所示,分別在頭指針處設(shè)置Total Counter,記錄該環(huán)的訪問總次數(shù),每個(gè)item設(shè)置Counter記錄該item的訪問次數(shù)。因?yàn)閮?nèi)存指針需要分配64bits,但實(shí)際系統(tǒng)地址索引只使用其中的48bits。我們使用剩余16bits設(shè)置標(biāo)志位(例如Total Counter、Counter等),保證不會(huì)增加額外的元數(shù)據(jù)開銷。該策略的優(yōu)勢(shì)是,通過采樣分析,可以計(jì)算選出最優(yōu)的頭指針位置,穩(wěn)態(tài)時(shí)性能表現(xiàn)更優(yōu)。
這一部分的細(xì)節(jié)設(shè)計(jì)有很多:
- 采樣分析策略如何選出最優(yōu)位置;
- 針對(duì)RCU更新操作的采樣優(yōu)化,
- 熱點(diǎn)繼承防止冷啟動(dòng)。
本文不再詳細(xì)描述,有興趣請(qǐng)參考HotRing論文。
設(shè)計(jì)3——無(wú)鎖并發(fā)訪問
Tair的RCU無(wú)鎖引擎是HotRing的設(shè)計(jì)基礎(chǔ)。參考論文[1,2]對(duì)如何實(shí)現(xiàn)無(wú)鎖鏈表進(jìn)行了詳細(xì)講解,后續(xù)的所有無(wú)鎖設(shè)計(jì)基本都沿用了他們的策略。有興趣可以讀一下。這里我們舉一個(gè)典型的并發(fā)示例進(jìn)行介紹。
如下圖所示,在鏈A->B->D上,線程1進(jìn)行插入C的操作,同時(shí)線程2進(jìn)行RCU更新B的操作,嘗試更新為B'。線程1修改B的指針指向C,完成插入。而線程2修改A的指針指向B'完成更新。兩個(gè)線程并發(fā)修改不同的內(nèi)存,均可成功返回。但是這時(shí)遍歷整條鏈(A->B'->D),將發(fā)現(xiàn)C無(wú)法被遍歷到,導(dǎo)致正確性問題。
解決措施是利用上圖(Item Format)中的Occupied標(biāo)志位。當(dāng)線程2更新B時(shí),首先需要將B的Occupied標(biāo)志位置位。線程1插入C需要修改B的指針(Next Item Address),若發(fā)現(xiàn)Occupied標(biāo)志位已置位,則需要重新遍歷鏈表,嘗試插入。通過使并發(fā)操作競(jìng)爭(zhēng)修改同一內(nèi)存地址,保證并發(fā)操作的正確性。
利用相同原理,我們保證了頭指針移動(dòng)操作,與CRUD操作的并發(fā)正確性。因此實(shí)現(xiàn)了HotRing的無(wú)鎖并發(fā)訪問。
設(shè)計(jì)4——適應(yīng)熱點(diǎn)數(shù)據(jù)量的無(wú)鎖rehash
如背景所述,對(duì)于極端的冪率分布場(chǎng)景,大量的數(shù)據(jù)訪問只集中在少部分的熱點(diǎn)數(shù)據(jù)中。因此只要保證熱點(diǎn)數(shù)據(jù)可以位于頭指針位置,沖突環(huán)即使很長(zhǎng),對(duì)于引擎的性能表現(xiàn)并不影響。引擎性能的降低,必然是因?yàn)闆_突環(huán)上存在多個(gè)熱點(diǎn)。因此HotRing設(shè)計(jì)了適應(yīng)熱點(diǎn)數(shù)據(jù)量的無(wú)鎖rehash策略來(lái)解決這一問題。
HotRing利用訪問所需平均內(nèi)存訪問次數(shù)(access overhead)來(lái)替代傳統(tǒng)rehash策略的負(fù)載因子(load factor)。在冪率分布場(chǎng)景,若每個(gè)沖突環(huán)只有一個(gè)熱點(diǎn),HotRing可以保證access overhead < 2,即平均每次訪問所需內(nèi)存訪問次數(shù)小于2。因此設(shè)定access overhead閾值為2,當(dāng)大于2時(shí),觸發(fā)rehash。
rehash過程分為3步進(jìn)行,結(jié)合上面4圖進(jìn)行說明,圖一為哈希表,哈希值在rehash前后的變化。剩余三圖為rehash三個(gè)過程。
初始化(Initialization):首先,HotRing創(chuàng)建一個(gè)后臺(tái)rehash線程。該線程創(chuàng)建2倍空間的新哈希表,通過復(fù)用tag的最高一位來(lái)進(jìn)行索引。因此,新表中將會(huì)有兩個(gè)頭指針與舊表中的一個(gè)頭指針對(duì)應(yīng)。HotRing根據(jù)tag范圍對(duì)數(shù)據(jù)進(jìn)行劃分。假設(shè)tag最大值為T,tag范圍為[0,T),則兩個(gè)新的頭指針對(duì)應(yīng)tag范圍為[0,T/2)和[T/2,T)。同時(shí),rahash線程創(chuàng)建一個(gè)rehash節(jié)點(diǎn)(包含兩個(gè)空數(shù)據(jù)的子item節(jié)點(diǎn)),子item節(jié)點(diǎn)分別對(duì)應(yīng)兩個(gè)新頭指針。HotRing利用item中的Rehash標(biāo)志位識(shí)別rehash節(jié)點(diǎn)的子item節(jié)點(diǎn)。
分裂(Split):在分裂階段,rehash線程通過將rehash節(jié)點(diǎn)的兩個(gè)子item節(jié)點(diǎn)插入環(huán)中完成環(huán)的分裂。如圖(Split)所示,因?yàn)閕temB和E是tag的范圍邊界,所以子item節(jié)點(diǎn)分別插入到itemB和E之前。完成兩個(gè)插入操作后,新哈希表將激活,所有的訪問都將通過新哈希表進(jìn)行訪問。到目前為止,已經(jīng)在邏輯上將沖突環(huán)一分為二。當(dāng)我們查找數(shù)據(jù)時(shí),最多只需要掃描一半的item。
刪除(Deletion):刪除階段需要做一些首尾工作,包括舊哈希表的回收。以及rehash節(jié)點(diǎn)的刪除回收。這里需要強(qiáng)調(diào),分裂階段和刪除階段間,必須有一個(gè)RCU靜默期(transition period)。該靜默期保證所有從舊哈希表進(jìn)入的訪問均已經(jīng)返回。否則,直接回收舊哈希表可能導(dǎo)致并發(fā)錯(cuò)誤。
04 總 結(jié)
內(nèi)存鍵值存儲(chǔ)系統(tǒng)由于高性能、易擴(kuò)展等特性在云存儲(chǔ)服務(wù)中廣泛使用。其通常作為必不可少的緩存組件,以解決持久化存儲(chǔ)系統(tǒng)或分布式存儲(chǔ)系統(tǒng)中的熱點(diǎn)問題。
但分析發(fā)現(xiàn),內(nèi)存KVS內(nèi)部的熱點(diǎn)問題更加嚴(yán)重,其數(shù)據(jù)訪問分布同樣服從冪律分布,且訪問傾斜愈加嚴(yán)重?,F(xiàn)有的內(nèi)存KVS缺乏熱點(diǎn)優(yōu)化意識(shí),部分?jǐn)?shù)據(jù)節(jié)點(diǎn)可能無(wú)法承載大量的熱點(diǎn)訪問,進(jìn)而引發(fā)系統(tǒng)崩潰,嚴(yán)重影響用戶體驗(yàn)。
在本論文中,我們進(jìn)行索引結(jié)構(gòu)的熱點(diǎn)感知設(shè)計(jì),提出了一種名為HotRing的新型熱點(diǎn)感知內(nèi)存KVS,針對(duì)冪率分布的熱點(diǎn)場(chǎng)景進(jìn)行大量?jī)?yōu)化。HotRing可動(dòng)態(tài)識(shí)別熱點(diǎn),并實(shí)時(shí)的進(jìn)行索引結(jié)構(gòu)的無(wú)鎖調(diào)整,進(jìn)而提供高并發(fā)高性能的無(wú)鎖化訪問。
與傳統(tǒng)的內(nèi)存KVS索引相比,HotRing采用輕量級(jí)的熱點(diǎn)識(shí)別策略,且沒有增加元數(shù)據(jù)存儲(chǔ)開銷。但在冪律分布的應(yīng)用場(chǎng)景中,HotRing的引擎吞吐性能可達(dá)600M ops/s,與目前最快KVS相比,可實(shí)現(xiàn)2.58倍的性能提升。
參考
[1] John D Valois. Lock-free linked lists using compare-and-swap. (PODC 1995)
[2] Timothy L Harris. A Pragmatic Implementation of Non-blocking Linked-lists. (DISC 2001)
[3] Berk Atikoglu. Workload Analysis of a Large- Scale Key-Value Store. (SIGMETRICS 2012)