HBase做為KeyValue結構存儲,在存儲上是依照RowKey的字典序進行排序,對於很多應用而言這可能遠遠不夠,好在HBase的數據可以存儲多個版本,並且版本可以排序,其理論上最大的版本數目Integer.MAX_VALUE,這在一定程度上簡化應用端的設計
舉個例子,假設現在有一個應用,對用戶的每次登錄信息(如:時間+IP)進行,並要求可以快速獲取指定用戶的最近登錄信息,如果選用HBase存儲則可以設計為:RowKey為用戶ID,value為IP地址,並指定timestamp為登錄時間,依照版本的保留特性,可以很容易地保存用戶近一月、近一年的登錄信息。
看起來上面的設計很不錯,畢竟用戶啥都不需要操作,HBASE可以很容易為你保留近一段時間內的數據
但是,如果一知半解,很可能會發生一些你意料之外的現象
1.先后插入兩條數據,他們擁有相同的RowKey,列,以及timestamp,不同的value
實際結果:只能獲取到第2次插入的數據,而不是兩個版本
2.先插入一條數據,版本為t1,然后刪除版本t1,再插入一條數據,版本仍為t1
實際結果:讀取版本為t1的數據時為空
3.先刪除版本小於t1的數據,再插入一條數據,版本為t2,並且t2<t1
實際結果:讀取版本為t2的數據時為空
出現這樣現象的原因可由KeyValue的大小計較 和 HBase的插入刪除邏輯解釋
a.KeyValue的大小比較規則,優先級從大到小依次為RowKey cf+cq timestamp type,
具體點比如說,在比較2個KeyValue時,先比較RowKey的大小('a' < 'b'),相同的情況下比較cf+cq的大小('cf1:q1'<'cf2:q1'<'cf2:q2'),如果還是相同的話就比較時間戳(3042211081<3042211080,注意 我沒寫錯,你沒看錯,時間戳的long值越大,表示數據越新,在從小到大的隊列中越靠前),如果上述仍然還相同則比較TYPE('DeleteFamily' < 'DeleteColumn' < 'Delete' < Put)
b.HBase的插入和刪除都是是向HBase提交一條KeyValue,而真正的物理刪除發生在compact時,所以,在客戶端,雖然相同的版本插入和刪除有先后順序,但是在服務端上,這是不可見的,相同的版本號,delete類型的KV永遠都排在put前,而讀到delete的kv后,就直接返回了
如果要避免23現象出現,則需要在插入前做compact操作,這樣才能得到想要的結果
4.HBase設計為版本數最多為Integer.MAX_VALUE,但是如果你真插入了接近該數的版本后,那可能有很大的風險在等着你
首先,compact時很有可能就out of memory
其次,單個rowkey的region再大也是不會split的