HBase Region級別二級索引
我們會經常談及二級索引,這是對全表資料進行另外一種方式的組織儲存,是針對table級別的。如果要為HBase上的表實現一個強一致性的二級索引,那麼就無法逃避分散式事務,而這一直是使用者最期待的功能。 而即使只需要保證最終一致性,這個索引也並不好實現,因為你需要額外的表以儲存過程資料,需要解決宕機恢復問題等
撇開分散式事務,我們是否可以考慮對索引的要求進行降級,比如把Region看成是全表下的子表,實現一套Region級別的索引,通過功能上的犧牲以換取實現的簡易及穩定。
在某些存在使用者概念的場景下,比如消費記錄,我們總是會在確定的使用者下,進行資料查詢。這意味著,在此類場景中,我們只需要一個使用者級別的索引。
舉個例子,對於一筆交易記錄,我們至少會有這麼幾個維度:
使用者Id,交易時間,交易金額,交易狀態(還會有交易名稱,交易號ID,對方ID等)
當儲存於HBase時,一般可以這麼組織:
RowKey= 使用者Id+交易時間
列1=交易金額
列2=交易狀態
所以當我們要讀取某個使用者的在某段時間內的交易記錄的時候,我們可以設定一個Scan:
startRow=使用者Id+開始時間
stopRow=使用者Id+結束時間
如果我們要增加查詢條件,進行過濾,比如要讀取某個使用者在某段時間內交易狀態為取消的交易記錄,我們可以為上述Scan設定一個Filter,來過濾不符合查詢條件的結果。
如果這是一個大商戶,某段時間內的交易記錄數巨多,通過設定Filter來過濾的方式就顯得效率低下,開銷巨大。
為了優化此類查詢,業務只能自建索引表,可以如下組織:
RowKey= 使用者Id+交易狀態+交易時間
列1=交易金額
由此產生的問題時,當產生一筆交易記錄的時候,我們需要向2張表中寫入資料,不用說原子性,為了保證最終一致性,也得會花費不少的力氣
彼之痛,己之痛,或許一個Region級別的索引儲存能有一定的療效。
什麼是Region級別的索引儲存
我們知道在HBase的結構中,一個Region可以包含多個Store,而索引儲存則也是Region下面的一個Store,我們稱其為Assistant Store,但它會有一些不同點:
a.Assistant Store中的資料由Regionserver按照使用者配置的規則自動寫入,是源資料的一份拷貝,但是擁有不同的組織方式
b.Assistant Store中的資料可以不遵守Region的Row範圍限制
c.Assistant Store中的資料由使用者主動選擇讀取(不會智慧的自動利用)
d.Assistant Store中的資料在Split時,遵守與源資料對應的原則
(可以先看例子)
一個簡單的例子
假設現在表只有一個Region,往表寫入以下6行資料:
r1/c1:q1/v1
r2/c1:q1/v2
r3/c1:q1/v1
r4/c1:q1/v2
r5/c1:q1/v1
r6/c1:q1/v2
如果我們已為這個表配置了一個簡單的索引儲存,該Assistant Store命名為c2,那麼除了上面的資料,Region中還會包含以下資料:
v1/c2:q1/r1
v1/c2:q1/r3
v1/c2:q1/r5
v2/c2:q1/r2 (在插入源資料的時候自動生成,存在Assistant Store中)
v2/c2:q1/r4
v2/c2:q1/r6
顯然,這些是簡單的倒置索引資料(可以由使用者定義生成的資料如何組織),當你對錶進行正常的scan時候,你只能見到源資料,即r1,r2,…,r6。 但是你可以通過某種方式,訪問Assistant Store中的資料,即v1,v2,以加快條件查詢
Region分裂處理
如果我們將上面這個例子中的Region進行Split,Split row為’r4′,那麼源資料就會被分落在兩個子Region中,Daughter_A 和 Daughter_B;
Daughter_A 包含如下源資料:
r1/c1:q1/v1
r2/c1:q1/v2
r3/c1:q1/v1
Daughter_B 包含如下源資料:
r4/c1:q1/v2
r5/c1:q1/v1
r6/c1:q1/v2
Assistant Store中的生成資料會遵守與源資料對應的原則,
Daughter_A 的Assistant Store中的索引資料為:
v1/c2:q1/r1
v1/c2:q1/r3
v2/c2:q1/r2
Daughter_B 的Assistant Store中的索引資料為:
v1/c2:q1/r5
v2/c2:q1/r4
v2/c2:q1/r6
原子性和一致性
解決了資料組織的問題,我們來看看如何保證源資料和生成資料間的原子性和一致性。
從上面的例子描述中,我們知道,設定了索引儲存後,當我們寫入一行資料時,實際上會儲存多行資料,但這多行資料都是在同個Region中,這意味著可以用一個本地事務解決這多行資料的事務寫入。或許有些使用者不知道,HBase-0.94版本早就實現了本地Region的多行事務。
回看Region級別的索引儲存的特點
a.Assistant Store中的資料由Regionserver按照使用者配置的規則自動寫入,是源資料的一份拷貝,但是擁有不同的組織方式
使用者可以通過擴充套件類Assistant,來生成自己定義的資料格式,儲存到Assistant Store中,
比如對於r1/c1:q1/v1,你可以生成一行v1/c1:q1/r1, 也可以生成一行v1r1/c1:q1/r1,也可以生成多行,但是生成的資料有一個限制,就是value值必須為源資料中的row值,這是為了保證源資料與生成資料之間能對應起來,當Region進行分裂的時候,索引資料和源資料仍然是對應的
b.Assistant Store中的資料可以不遵守Region的Row範圍限制
從上面的例子中,我們可以看出,Assistant Store中的資料的Row是由使用者自定義的,所以其Row是任意的,不會在Region的Row範圍內
c.Assistant Store中的資料由使用者主動選擇讀取(不會智慧的自動利用)
Assistant Store中的資料的寫入用系統自動控制,但是目前的設計中,讀取由使用者主動發起
d.Assistant Store中的資料在Split時,遵守與源資料對應的原則
優劣分析
優點:
1.設計簡單,實現方便
2.加速條件Scan ,提高效率
3.相比於不設定索引儲存,寫入效能幾乎不受影響,因為多行資料只會寫一次Log
(無論是分散式事務,或者使用者自己寫入多張表,都無法避免寫入多行資料時要多次寫Log)
缺點:
1.額外儲存空間
2.相比於全域性意義上的二級索引,使用上會有侷限性
重新思考上面的交易記錄的案例
如果有了Region級別的索引儲存,我們可以為交易記錄表設定1個或多個Assistant Store,
源資料的組織仍然同上:
RowKey= 使用者Id+交易時間
列1=交易金額
列2=交易狀態
Assistant Store中的資料組織為:
RowKey= 使用者Id+交易狀態+交易時間
列1=交易金額
雖然結構上和使用者寫多張表一樣,但是不需要為解決原子性和一致性而煩惱。
當然細心的讀者,會發現從Assistant Store中掃描出來的資料無法做到ordered by 源資料中的Row,要做到ordered by Assistant Store中的Row也得花一定的力氣。
怎麼使用索引儲存?
如何讓目前的HBase使用者平滑使用,也是一個不小的難題,主要是有這麼幾點。
1.API使用
按照目前的設計,使用者需要通過Scan方式主動的去讀取索引儲存,示例
01 | //從源資料的Row上 限制掃描範圍 |
02 | Scan scan = new Scan(); |
03 | scan.setStartRow(‘r1′); |
04 | scan.setStopRow(‘r7′); |
05 |
06 | //建立在Assistant Store執行的Scan,從v2 到 v2+ |
07 | Scan assistantScan = new Scan().setStartRow(‘v2′).setStopRow(‘v2′+’(byte)0×00′); |
08 | //設定這個以後,Region在解析的時候,會在Assistant Store上執行這個Scan |
09 | scan.setAssistantScan(assistantScan); |
10 |
11 | scanner = htable.getScanner(scan); |
12 | for(Result result:scanner){ |
13 | //輸出 |
14 | v2/c2:q1/r2 |
15 | v2/c2:q1/r4 |
16 | v2/c2:q1/r6 |
17 | } |
2.Ordered by特性保證
目前實現中沒有,準備後續再新增
3.已有資料的索引追加
目前實現中沒有,準備後續再新增
轉自:http://zjushch.iteye.com/blog/1910218