Phoenix使用SALT_BUCKETS建立預分割槽
1. 基礎知識
Phoenix Salted Table是phoenix為了防止hbase表rowkey設計為自增序列而引發熱點region讀和熱點region寫而採取的一種表設計手段。通過在建立表的時候指定SALT_BUCKETS
來實現pre-split(預分割)。如下表示建立表的時候將表預分割到20個region裡面。
CREATE TABLE SALT_TEST (a_key VARCHAR PRIMARY KEY, a_col VARCHAR) SALT_BUCKETS = 20;
預設情況下,對salted table建立二級索引,二級索引表會隨同源表切進行Salted切分,SALT_BUCKETS與源表保持一致。當然,在建立二級索引表的時候也可以自定義SALT_BUCKETS的數量,phoenix沒有強制它的數量必須跟源表保持一致。
2. 實現原理
Phoenix Salted Table的實現原理是在將一個雜湊取餘後的byte值插入到 rowkey的第一個位元組裡,並通過定義每個region的start key 和 end key 將資料分割到不同的region,以此來防止自增序列引入的熱點問題,從而達到平衡HBase叢集的讀寫效能的目的。
salted byte的計算方式大致如下:
hash(rowkey) % SALT_BUCKETS
SALT_BUCKETS的取值為1到256。
預設下salted byte將作為每個region的start key 及 end key,以此分割資料到不同的region,這樣能做到具有相同salted byte的資料能夠位於同一個region裡面。
3. SALT_BUCKET的本質
Salting能夠通過預分割槽(pre-splitting)資料到多個region中來顯著提升讀寫效能。
Salting 翻譯成中文是加鹽的意思,本質是在hbase中,rowkey的byte陣列的第一個位元組位置設定一個系統生成的byte值,這個byte值是由主鍵生成rowkey的byte陣列做一個雜湊演算法,計算得來的。Salting之後可以把資料分佈到不同的region上,這樣有利於phoenix併發的讀寫操作。關於SaltedTable的說明在 http://phoenix.apache.org/salted.html。
salted table可以自動在每一個rowkey前面加上一個位元組,這樣對於一段連續的rowkeys,它們在表中實際儲存時,就被自動地分佈到不同的region中去了。當指定要讀寫該段區間內的資料時,也就避免了讀寫操作都集中在同一個region上。
簡而言之,如果我們用Phoenix建立了一個saltedtable,那麼向該表中寫入資料時,原始的rowkey的前面會被自動地加上一個byte(不同的rowkey會被分配不同的byte),使得連續的rowkeys也能被均勻地分佈到多個regions。
4. 例項展示
CREATE TABLE SALT_TEST (a_key VARCHAR PRIMARY KEY, a_col VARCHAR) SALT_BUCKETS = 4;
UPSERT INTO SALT_TEST(a_key, a_col) VALUES('key_abc', 'col_abc');
UPSERT INTO SALT_TEST(a_key, a_col) VALUES('key_ABC', 'col_ABC');
UPSERT INTO SALT_TEST(a_key, a_col) VALUES('key_rowkey01', 'col01');
從phoenix sqlline.py查詢資料,沒感覺有什麼不同:
0: jdbc:phoenix:node1> select * from "SALT_TEST";
+---------------+----------+
| A_KEY | A_COL |
+---------------+----------+
| key_ABC | col_ABC |
| key_rowkey01 | col01 |
| key_abc | col_abc |
+---------------+----------+
3 rows selected (0.024 seconds)
通過hbase shell 觀察驗證,觀察到phoenix確實是在rowkey的第一個位元組插入一個byte位元組:
hbase(main):062:0* scan 'SALT_TEST'
ROW COLUMN+CELL
\x01key_ABC column=0:A_COL, timestamp=1539077357795, value=col_ABC
\x01key_ABC column=0:_0, timestamp=1539077357795, value=x
\x01key_abc column=0:A_COL, timestamp=1539077357747, value=col_abc
\x01key_abc column=0:_0, timestamp=1539077357747, value=x
\x03key_rowkey01 column=0:A_COL, timestamp=1539077359188, value=col01
\x03key_rowkey01 column=0:_0, timestamp=1539077359188, value=x
3 row(s) in 0.0440 seconds
這裡我們可以判斷phoenix是在寫入資料的時候做了處理,在插入資料的時候會計算一個byte欄位並將這個位元組插入到rowkey的首位置上;而在讀取資料的API裡面也相應地進行了處理,跳過(skip)第一個位元組從而讀取到正確的rowkey(注意只有salted table需要這麼處理),所以只能通過phoenix介面來獲取資料已確保拿到正確的rowkey。
5. 特別注意
可以看到,在每條rowkey前面加了一個Byte,這裡顯示為了16進位制。也正是因為添加了一個Byte,所以SALT_BUCKETS的值範圍在必須再1 ~ 256之間。而新增的這個Byte是根據什麼來分的我就不得而知了,所以最好不要使用HBase的API插入資料。
因此,在使用SALT_BUCKETS的時候需要注意以下兩點:
建立salted table後,應該使用Phoenix SQL來讀寫資料,而不要混合使用Phoenix SQL和HBase API
如果通過Phoenix建立了一個salted table,那麼只有通過Phoenix SQL插入資料才能使得被插入的原始rowkey前面被自動加上一個byte,通過HBase shell插入資料無法prefix原始的rowkey
比如使用Hbase的BulkLoad API
向SALT_BULKET
的表中插入資料中插入資料,會出現ROW_KEY
的第一個位元組在Phoenix中檢視少一位的情況,並且在Phoenix中使用ROW_KEY
查詢會出現查詢不到結果的情況。
參考:
https://blog.csdn.net/d6619309/article/details/51345637
https://www.cnblogs.com/kekukekro/p/6339587.html#wiz_toc_20