Accordion :一種HBase內(nèi)存壓縮算法
現(xiàn)如今,人們對基于HBase的產(chǎn)品的讀寫速度要求越來越高。在理想情況下,人們希望HBase 可以在保證其可靠的持久存儲的前提下能并擁有內(nèi)存數(shù)據(jù)讀寫的速度。為此,在HBase2.0中引入Accordion算法。
Hbase RegionServer 負責將數(shù)據(jù)劃分到多個Region中。RegionServer 內(nèi)部(垂直)的可伸縮性能對于最終用戶體驗以及整個系統(tǒng)的利用率至關(guān)重要。Accordion 算法通過提高對RAM利用來提升RegionServer擴展性。這樣就使得內(nèi)存中可以存放更多數(shù)據(jù),從而降低了對磁盤的讀取頻率(即降低了HBase中磁盤占用和寫入方法,更多的讀寫RAM,降低了對于磁盤的IO訪問)。在HBase2.0之前,這些指標是不能同時滿足的,并且相互限制,在引入Accordion之后,這一狀況得到了改善。
Accordion算法來源于HBase核心架構(gòu)LSM算法。在HBase Region 中,數(shù)據(jù)是按照key-value形式映射為可查找的存放,其中put進來的新數(shù)據(jù)以及一些topmost(靠前)數(shù)據(jù)存放在內(nèi)存中(MemStore),其余的為不變的HDFS文件,即HFile。當MemStore寫滿時,數(shù)據(jù)被flush到硬盤里,生成新的HFile文件。HBase采用多版本并發(fā)控制,MemStore將所有修改后的數(shù)據(jù)存儲為獨立版本。一條數(shù)據(jù)的多個版本可能同時存儲在MemStore和HFile中。當讀取一條多版本數(shù)據(jù)時,根據(jù)key從HBase掃描BlockCache中的HFile獲取最新的版本數(shù)據(jù)。為了降低對磁盤的訪問頻率,HFiles在后臺合并(即壓縮過程,刪除多余的cells,創(chuàng)建更大的文件)。
LSM通過將隨機讀寫轉(zhuǎn)換為順序讀寫,從而提高了寫入性能。之前的設(shè)計并未采用壓縮內(nèi)存數(shù)據(jù),主要原因是在LSM樹設(shè)計當初,RAM還是非常緊缺的資源,因此MemStore的容量很小。隨著硬件不斷提升,RegionServer管理的整個MemStore可能為數(shù)千兆字節(jié),這就為HBase優(yōu)化留下了大量空間。
Accordion算法重新將LSM應(yīng)用于MemStore,以便當數(shù)據(jù)仍在RAM中時可以消除冗余和其他開銷。這樣做可以減少flush到HDFS的頻率,從而降低了寫入放大和磁盤占用。 隨著flush次數(shù)的減少,MemStore寫入磁盤的頻率會降低,進而提高HBase寫入性能。磁盤上的數(shù)據(jù)較少也意味著對塊緩存的壓力較小,提高了讀取的響應(yīng)時間。最終,減少對磁盤寫入也意味著在后臺壓縮次數(shù)降低,即讀取和寫入周期將縮短??偠灾?,內(nèi)存壓縮算法的效果可以被看作是一個催化劑,它使整個系統(tǒng)的運行速度更快。
目前Accordion提供了兩個級別的內(nèi)存壓縮:basic 級別和 eager 級別。前者適用于所有數(shù)據(jù)更新的優(yōu)化,后者對于高數(shù)據(jù)流的應(yīng)用非常有用,如生產(chǎn)-消費隊列,購物車,共享計數(shù)器等。所有這些使用案例都會對rowkey進行頻繁更新,生成多個冗余版本的數(shù)據(jù),這些情況下Accordion算法將發(fā)揮其價值。但另一方面,eager 級壓縮優(yōu)化可能會增加計算開銷(更多內(nèi)存副本和垃圾收集),這可能會影響數(shù)據(jù)寫入的響應(yīng)時間。如果MemStore使用堆內(nèi)MemStore-本地分配緩沖區(qū)(MSLAB),這會導(dǎo)致開銷增大。所以建議不要將此配置與eager級壓縮結(jié)合使用。
如何使用
內(nèi)存壓縮可以在全局和列族級別配置。目前支持三種級別配置:none(傳統(tǒng)實現(xiàn)),basic和eager。默認情況下,所有表都是basic內(nèi)存壓縮。此配置可以在hbase-site.xml中修改,如下所示:
- <property>
 - <name> hbase.hregion.compacting.memstore.type </name>
 - <value> <none|basic|eager> </value>
 - </property>
 
也可在HBase shell中為每個列族進行單獨配置,如下所示:
- create '<tablename>',
 - {NAME =>'<cfname>',
 - IN_MEMORY_COMPACTION =>' <NONE|BASIC|EAGER>' }
 
性能提高
通過利用YCSB(Yahoo Cloud Service Benchmark)對HBase進行了全面測試。試驗中采用數(shù)據(jù)集大小為100-200 GB,結(jié)果表明Accordion算法對于HBase性能有顯著的提升。
Heavy-tailed (Zipf)分布:在測試負載中國,rowkey遵循大多數(shù)現(xiàn)實生活場景中出現(xiàn)的Zipf分布。在這種情況下,當100%的操作是寫入操作時,Accordion實現(xiàn)寫入放大率降低30%,寫入吞吐量提高20%,GC降低22%。當50%的操作是讀取時,tail讀取延遲降低12%。
均勻分布:第二個測試中rowkey都均衡分布。當100%的操作是寫入操作時,Accordion的寫入放大率降低25%,寫入吞吐量提高50%,GC降低36%。tail讀取延遲不受影響(由于沒有本地化)。
Accordion如何工作
High Level設(shè)計:
Accordion引入了MemStore的內(nèi)部壓縮(CompactingMemStore)實現(xiàn)方法。與默認的MemStore相比,Accordion將所有數(shù)據(jù)保存在一個整的數(shù)據(jù)結(jié)構(gòu)中用segment來管理。最新的segment,稱為active segment,是可變的,可用來接收Put操作,若active segment達到overflow條件(默認情況下32MB,MemStore的25%大?。鼈儗灰频絠n-memory pipeline 中,并設(shè)為不可變segment,我們稱這一過程為in-memory flush。Get操作通過掃描這些 segment和HFiles 取數(shù)據(jù)(后者操作通過塊緩存進行訪問,與平常訪問HBase一樣)。
CompactingMemStore 可能會不時在后臺合并多個不可變segment,從而形成更大的segment。因此,pipeline是“會呼吸的”(擴張和收縮),類似于手風琴波紋管,所以我們也將Accordion 譯為手風琴。
當RegionServer 刷入一個或多個MemStore到磁盤釋放內(nèi)存時,它會刷入 CompactingMemStore中已經(jīng)移入pipeline中的segment到磁盤?;驹硎茄娱LMemStore有效管理內(nèi)存的生命周期,以減少整體I/O。當flush發(fā)生時,pipeline中所有的segment 段將被移出合成一個快照, 通過合并和流式傳輸形成新的HFile。圖1展示了CompactingMemStore與傳統(tǒng)設(shè)計的結(jié)構(gòu)。
圖1. CompactingMemStore與DefaultMemStore
Segment結(jié)構(gòu):
與默認的MemStore類似,CompactingMemStore在單元存儲之上維護一個索引,這樣可以通過key快速搜索。兩者不同的是,MemStore索引實現(xiàn)是通過Java skiplist (ConcurrentSkipListMap--一種動態(tài)但奢侈的數(shù)據(jù)結(jié)構(gòu))管理大量小對象。CompactingMemStore 則在不可變的segment 索引之上實現(xiàn)了高效且節(jié)省空間的扁平化布局。這種優(yōu)化可以幫助所有壓縮策略減少RAM開銷,甚至可以使數(shù)據(jù)幾乎不存在冗余。當將一個Segment加入pipeline中,CompactingMemStore 就將其索引序列化為一個名為CellArrayMap 的有序數(shù)組,該數(shù)組可以快速進行二進制搜索。
CellArrayMap既支持從Java堆內(nèi)直接分配單元,也支持MSLAB的自定義分配(堆內(nèi)或堆外),實現(xiàn)差異通過被索引引用的KeyValue對象抽象出來(圖2)。CellArrayMap本身始終分配在堆內(nèi)。
圖2.具有扁平CellArrayMap索引和MSLAB單元存儲的不可變Segment
壓縮算法:
內(nèi)存中壓縮算法在pipeline中的Segment上維護了一個單一的扁平化索引。這樣的設(shè)計節(jié)省了存儲空間,尤其是當數(shù)據(jù)項很小時,可以及時將數(shù)據(jù)刷入磁盤。單個索引可使搜索操作在單一空間進行,因此縮短了tail讀取延遲。
當一個active segment被刷新到內(nèi)存時,它將排列到壓縮pipeline中,并會立即觸發(fā)一個異步合并調(diào)度任務(wù)。該調(diào)度任務(wù)將同時掃描pipeline中的所有Segment(類似于磁盤上的壓縮)并將它們的索引合并為一個。basic和eager 壓縮策略之間的差異體現(xiàn)在它們處理單元數(shù)據(jù)的方式上。basic壓縮不會消除冗余數(shù)據(jù)版本以避免物理復(fù)制,它只是重新排列KeyValue對象的引用。eager壓縮則相反,它會過濾出冗余數(shù)據(jù),但這是以額外的計算和數(shù)據(jù)遷移為代價的。例如,在MSLAB存儲器中,surviving 單元被復(fù)制到新創(chuàng)建的MSLAB中。
未來的壓縮可能會在basic壓縮策略和eager壓縮策略之間實現(xiàn)自動選擇。例如,該算法可能會在一段時間內(nèi)嘗試eager壓縮,并根據(jù)所傳遞的值(如:數(shù)據(jù)被刪除的比例)安排下一次壓縮。這種方法可以減輕系統(tǒng)管理員的先驗決定,并適應(yīng)不斷變化的訪問模式。

















 
 
 













 
 
 
 