HBase中的時間維度


原文鏈接:http://outerthought.org/blog/417-ot.html

原文是Bruno Dumon在一年前寫的,現在看了還是有很多啟發,因此簡單的翻譯一下,可能有理解不准確的地方,強烈推薦看原文。

————————————– 毫無理由的分割線 ———————————

HBase是一個類Bigtable系統,按照Google的論文對 Bigtable的定義是“一種稀疏的,分布式的,持久的多為維度的有序Map。這個Map由row key,column key和timestamp做為索引,Map中的值是連續的byte數組”。HBase的多維度,包括table和column family等。

不是所有維度都是等同的,不同的維度有其特殊的意義。例如,row這個維度用於region切分,所以可以增長到海量。而column則不用於分 片,和row不同的是,一個row中的多個columns的put或者delete操作是一個原子事務(當然,同一個原子事務中不可能同事put和 delete)。而這篇文章將重點關注時間維度。

1. 基本概念

在Bigtable論文中,一個{row key,column key}對應的是一個cell。每個cell可能包含多個版本的數據,以timestamp索引,這就是本文要講述的時間索引。

1.1 時間/版本維度中的key

Row key)和column key(在HBase中也稱為qualifier)是bytes類型,而時間維度的key則是long integer類型,比較典型的是使用 java.util.Date.getTime()或者System.currentTimeMillis()來做為時間維度的key。

時間維度的各個版本是倒序排列后存儲的,所以從storefile中讀取的時候,最先讀到的就是最新的時間,這個特性在系統設計的時候如果能有效利用,會非常有用。

下面我們從幾個核心操作get,put和delete來看看時間維度的具體細節。

 

1.2 Get

默認情況下,Get操作會返回每個cell的timestamp值最大的版本(但這並不一定代表是最新寫入的版本,后面會提到)。這個默認行為可以通過下面兩種方式改變:
1. 通過設置 Get.setMaxVersions()可以返回多個版本的數據。
2. 通過設置 Get.setTimeRange()可以返回其他時間范圍的版本。

如果要返回除最新版本之外的某個特定版本,可以通過組合上面兩個條件來達成,首先設置時間范圍為0到希望返回的時間,然后設置最多返回一個版本即可。

1.3 Put

每次執行Put,對應的cell都會插入一個新的版本。默認使用的是currentTimeMillis。應用也可以使用自定義的值來做為每個列的 timestamp,只需要是一個long integer的值即可,不一定是時間,如果是時間,也可以是過去或者未來某個時間,所以前面說到方Get返回的是timestamp最大值的版本,而不 一定是最新寫入的版本。

HBase的架構中,數據文件的寫入只能附加(Append)而不能修改。數據文件只有在執行compact操作是才重寫。數據文件中存儲的是鍵值 對,其中鍵是{row key,column key,timestamp}的組合鍵,每次put新的值的時候,會產生新的鍵值對附加在數據文件中,即時你使用一個已經存在的timestamp也一 樣。因此短時間內對同一行做操作可能導致大量的鍵值對存儲到文件中,根據gc的策略,下一次執行compact的時候才會回首老的版本。

1.4 Delete

關於Delete和時間維度有很多事要講。

1.4.1 GC(垃圾收集)

HBase有兩種方式回收舊的版本

1. 可以設置最多可以保留的版本數量。如果超過,則最老的版本會被拋棄,默認設置是3個版本,這個可以在創建Column Family時通過HColumnDescriptor.setMaxVersions(int versions)設置,因此這個是Column Family級別的。當然這個限制中讀取的時候是邏輯限制,即時生效,但老版本的物理刪除還是需要等到major compact操作中執行。將這個值設置為1並不是說就禁用了多版本,每次Put的時候還是同樣的會生成新的版本,只是最后只保留一個版本而已。

2. 可以設置TTL(Time To Live),如果版本存在的時間超過TTL,則會被刪除。默認的TTL是forever。通過 HColumnDescriptor.setTimeToLive(int seconds)可以設置TTL,物理清除版本還是要等到major compact,但Get/Scan等讀取操作邏輯是即時生效的。需要注意的是,如果row中所有的cell都被TTL失效以后,這一行記錄就被刪除了 (HBase中不需要顯示的建立或者刪除行,如果行中的cell有值,行就存在)。

對於上面的兩種回收方式,會有一種有趣的場景,假設你對一個cell插入了三個版本t1,t2,t3,而最大版本數設置是2,這時候請求所有版本, 則只有t2和t3會返回,這是正常的。但這時如果刪除t2或者t3,再請求所有版本,則t1可能又會出現在結果中了。當然,在major compact操作之后,就不會有這個問題了。因此major compact並不是對用戶透明的操作,可能會影響用戶的查詢結果。

1.4.2 Manule delete

中HBase中執行delete操作時,有兩種方式可以刪除某個特定的版本

1. 刪除某個timestamp之前的所有老版本
2. 刪除某個timstamp點的版本

delete操作可以針對row,column family,或者column,只有最后一種情況下可以刪除某個timestamp點的版本。而針對row和column family的delete,則只能刪除某個timestamp之前的所有版本。

舉個例子,假設我們要刪除一整行數據,你可以指定timestamp,或者使用currentTimeMillis,那“刪除某個 timestamp之前的所有老版本”意味着什么呢?HBase並不會去修改原有數據,因此刪除不是立即在原數據對應的文件中執行物理刪除或者物理標記刪 除,而是將刪除的記錄保存中所謂的tombstone中,當HBase執行major compact的時候,再根據tombstone去執行物理刪除。

如果你指定的timestamp比row中最新的版本大,則相當於刪除了整行數據。

2. 使用timestamp

從上面的描述可以看到,時間維度主要是用戶多版本的,但你也可以在應用中把它當作column外的另外一個維度來使用,不同的是它的key是長整數型,目前這種方式還存在一些bug,因此還不是很推薦。

在jira中,有兩個和時間維度有關的bug值得比較有意思

1. 利用時間維度多版本實現HBase多數據中心復制,參看HBASE-1295 (PDF附件的第四頁) 或者這個郵件
2. HBASE-2406的一個評論提到,如果要在多個表中獲得一致性數據(如二級索引表),可以通過向多個表寫入相同的timestamp版本來實現。

3. 限制

由於一些bug或者“不可預期的行為”,時間維度目前還有一些問題需要解決。

1. HBASE-1485 HBASE-2406中 提到的覆寫已經存在的timetamp版本的值的問題(在0.90版本中已經解決),換句話說,根據{row key ,column key, timestamp}修改某個cell的值,實際會產生一個新的鍵值對,對於HBase來說就會存在兩個相同timestamp的key,判斷最大 timestamp版本就會有問題。當然,通過HBASE-2649中提到的寫入到HFile的時間序可以解決這個問題。

2. HBASE-2256中 提到的delete覆蓋put(即使put操作發生在delete之后)的問題。由於delete只是記錄tombstone,物理刪除需要等到 major compact操作,因此有一種情況,你刪除了所有<=T的版數據,然后又put了一條<=T的數據,還是會被tombstone標記為刪 除。當然,如果你使用的是遞增的timestamp不會有這個問題,但如果使用系統時間,而delete和put間隔時間非常短,還是有一定幾率兩個操作 的timestamp是相同的,這時候杯具依然會存在。

 

http://hbase.info/2011/07/20/hbase-time-dimension  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM