1. 程式人生 > >HBase Region級別二級索引

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