HBase 0.94 --之數據壓縮(DataBlock compression,HLog compression)

衆所周知,HBase 0.94對性能做了很多優化,記錄一下個人對其實現細節及如何更好應用的理解。 


0.94引入了兩個在HBase層的數據壓縮: 

一.DataBlock compression 

1.1 作用 
DataBlock compression指的是對HFile v2中的Data Block進行壓縮,Data Block既存儲在Disk上(HDFS中),也會存在於LRU Cache中,所以此壓縮特性支持: 
a)Disk、Cache上都不開啓 
b)Disk、Cache上都開啓 
c)Cache上開啓,Disk上開啓 
但不支持 
d)Cache上不開啓,Disk開啓 

由此可見,此壓縮特性的作用是: 
a)節省LRU Cache的空間使用,提升cache命中率,優化讀性能 
b)節省Disk空間使用 

當然,前者是主要目的,後者我們有對HFile的全局壓縮算法,如LZO 

1.2 副作用 
目前,DataBlock壓縮特性默認是關閉的,顯然,其能優化某些業務場景,但盲然使用肯定會帶來副作用: 
a)Data Block解壓縮,(如果一個Block(默認64KB)中包含很多KV,這個開銷會比較大) 
b)某些場景,壓縮後數據更大 

1.3 壓縮算法 
a)PREFIX 
向Block中寫入KV時,和前一條KV比較Key(這裏的Key=Row+Family+Qualifier+Timestamp+Type,具體可見附件),對於相同的前綴byte, 則不再重複寫入 
根據KeyValue結構(見附件),某KV具體壓縮後的內容: 
*KeyLength壓縮爲1~5個字節(使用7-bit encoding,見附註) 
*ValueLength壓縮爲1~5個字節 
*和上個KV比較Key的內容,寫入相同的前綴byte的長度,1~5個字節 
*除相同前綴部分,剩餘的Key內容 
*Value內容 

在最壞情況下,一條KV經過此算法壓縮後會大小會增加3個字節 

b)DIFF 
向Block中寫入KV時,會和前一條KV進行全面比較(如Key長度,value長度,Row,timestamp,type),對於相同的信息則不再寫入,同時KV中的family內容只寫一次(因爲同個Block中,他們的family肯定是相同的) 
某KV具體壓縮後的內容: 
*一個字節的flag(這個flag的作用後面解釋) 
*如果和上個KV的Key長度不一樣,則寫入1~5個字節的KeyLength 
*如果和上個KV的Value長度不一樣,則寫入1~5個字節的KeyLength 
*和上個KV比較Key的內容,寫入相同的前綴byte的長度,1~5個字節(此比較不包含timestamp和type,而PREFIX算法中是包含的) 
*剩餘的Row內容(如果相同前綴比較少) 
*如果是第一條KV,寫入family內容 
*剩餘的qualifier內容 
*寫入1~8字節的timestamp或者與上個KV的timestamp的差(是原值還是寫與上個KV的差,取決於哪個字節更小) 
*如果和上個KV的type不一樣,則寫入1字節的type 
*Value內容 

在解壓縮時,怎麼判斷和上個KV的Key長度是否一樣,Value長度是否一樣,寫入的timestamp是原值還是差呢等等,這些都是通過最早寫入的1個字節的flag來實現的, 
這個字節中的8位bit,含義是: 
第0位,如果爲1,Key長度一樣 
第1位,如果爲1,Value長度一樣 
第2位,如果爲1,type一樣 
第3位,如果爲1,則寫入的timestamp是差值 
第4,5,6位,這3位組合起來的值(能表示0~7),表示寫入的timestamp或差值的字節數 
第7位,如果爲1,表示寫入的timestamp或差值取反了(比如timestamp差值爲負數,爲了節省空間,會寫入絕對值) 

由此可見,DIFF算法是針對KeyValue的結構而量身設計的 

c)FAST_DIFF 
FAST_DIFF與DIFF的原理一樣,只是減小了壓縮量,提升了壓縮速度, 
某KV具體壓縮後的內容: 
*一個字節的flag 
*如果和上個KV的Key長度不一樣,則寫入1~5個字節的KeyLength 
*如果和上個KV的Value長度不一樣,則寫入1~5個字節的KeyLength 
*和上個KV比較Key的內容,寫入相同的前綴byte的長度,1~5個字節(此比較不包含timestamp和type,而PREFIX算法中是包含的) 
*剩餘的Row內容(如果相同前綴比較少) 
*如果是第一條KV,寫入family內容 
*剩餘的qualifier內容 
*寫入1~8字節的timestamp 
*如果和上個KV的type不一樣,則寫入1字節的type 
*如果和上個KV的Value內容不一樣,則寫入Value內容 

1個字節的flag的8位bit含義: 
第0,1,2位,這3位組合起來的值(能表示0~7),表示寫入的timestamp的字節數 
第3位,如果爲1,Key長度一樣 
第4位,如果爲1,Value長度一樣 
第5位,如果爲1,type一樣 
第6位,如果爲1,Value內容一樣 
第7位,閒置... 

由此可見,相比DIFF,FAST_DIFF只是減少了對timestamp的壓縮,但是增加了Value內容是否一樣的判斷,在某些情景(如value只是用來計數,或者value是一種類型選項值),那麼可以大幅減少相同value內容的重複寫入 

1.4 應用場景及如何配置 
上面介紹了3種壓縮算法的原理,由此可簡單分析下他們的應用場景: 
1)PREFIX,這是一個比較公用的壓縮算法,其壓縮過程也比較簡單快速,通用於存在相同的Row前綴的情景 

2)DIFF, 相比於PREFIX,同樣是需要應用在存在相同的Row前綴的情景,但其只寫一份family內容,寫入timestamp的差值,比較長度是否一樣等特性,使其壓縮幅度會更大,當然壓縮的CPU開銷也會稍大 

3)FAST_DIFF,和DIFF類似,但是對於存在相同Value內容的場景,那肯定是使用它了 

空口無憑,實驗爲據,具體場景,數據爲準 

如何配置? 
Data Block的壓縮算法選擇是表中的一個列族屬性,你可以通過api HColumnDescriptor#setDataBlockEncoding來開啓或關閉或更改壓縮算法,另外在開啓壓縮時,你可以通過HColumnDescriptor#setEncodeOnDisk決定是否在寫入Disk時也採用壓縮 


二.HLog compression 

2.1 爲什麼使用HLog壓縮 
爲了保證數據安全性,每put一次,都會寫入一條hlog記錄,每一條hlog記錄都會包含:table name、region name、row、family、qualifier等,如果數據寫入集中在某表、某region乃至某row、某family,那麼HLog的文件中就會存在大量重複的內容,壓縮由此而生 

2.2實現原理 
HLog壓縮採用的是詞典壓縮法,其寫入一條log記錄時的壓縮行爲: 
*查找table是否在table詞典中,如果不是,該table name寫入到hlog 文件中,並加入到table詞典中,對應一個short值;如果是,則hlog文件中寫入一個short值,以代表該table name 
*region family qualifier row,也是按照上述方法寫入到hlog文件中 
所以,寫一個hlog文件時,會維護5個詞典,即table詞典、region詞典、family詞典、qualifier詞典、row詞典 


2.3 作用 
壓縮的作用自然是減小hlog記錄的寫入大小,加快寫hlog的速度,提升寫性能;但是,個人認爲,單條hlog記錄壓縮後減少幾十或上百字節,對於其本身就不大而言,並不能提高多少HBase的寫數據速度。 
不過,其還存在着一個側面作用,增加單個hlog文件的數據記錄量,這會一定程度上提高memstore的內存使用率,從而提升全局的flush效率、compaction效率。 

2.4如何配置及應用場景 
Server端修改配置hbase.regionserver.wal.enablecompression爲true(默認false)開啓HLog壓縮特性 

應用場景: 
*數據寫入比較分散在各個表,各個region的,就不要考慮了 
*數據寫入比較集中的可以考慮開啓,但測試數據爲準,如果寫性能沒啥優化,那還是別嘗這個鮮了,新特性免不了會有風險 


以上只是個人見解,歡迎指正 

附註: 
7-bit 壓縮法,針對Integer數據的一種壓縮算法,可以將4個字節的Int型數據壓縮爲1~5個字節,數值越小,壓縮後的字節數越少,所以對值小的Int型數據會有比較好的壓縮效果,其具體壓縮原理,簡單介紹如下: 
1.將一個Int型數據轉換成32位; 
2.0~7位,按原值寫入 
3.如果原值的第8位及以後存在1,則寫入1,否則寫入0,結束寫入(即壓縮爲了1個字節) 
4.如果沒結束,則寫入原值的第8~14位,如果原值的第15位及以後存在1,則寫入1,否則寫入0,結束寫入 
5...重複上述行爲,直到寫完 

KV結構圖: 
 

HFileV2結構 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章