[Phoenix] 五、二級索引


摘要: 目前HBASE只有基於字典序的主鍵索引,對於非主鍵過濾條件的查詢都會變成掃全表操作,為了解決這個問題Phoenix引入了二級索引功能。然而此二級索引又有別於傳統關系型數據庫的二級索引,本文將詳細描述Phoenix中二級索引功能、用法和原理,希望能夠對大家在業務技術選型時起到一些幫助作用。

一、概要

目前HBASE只有基於字典序的主鍵索引,對於非主鍵過濾條件的查詢都會變成掃全表操作,為了解決這個問題Phoenix引入了二級索引功能。然而此二級索引又有別於傳統關系型數據庫的二級索引,本文將詳細描述Phoenix中二級索引功能、用法和原理,希望能夠對大家在業務技術選型時起到一些幫助作用。


二、二級索引

示例表如下(為了能夠容易通過HBASE SHELL對照表內容,我們對屬性值COLUMN_ENCODED_BYTES設置為0,不對column family進行編碼):

CREATE TABLE TEST ( ID VARCHAR NOT NULL PRIMARY KEY, COL1 VARCHAR, COL2 VARCHAR ) COLUMN_ENCODED_BYTES=0; upsert into TEST values('1', '2', '3');

1. 全局索引

全局索引更多的應用在讀較多的場景。它對應一張獨立的HBASE表。對於全局索引,在查詢中檢索的列如果不在索引表中,默認的索引表將不會被使用,除非使用hint。

創建全局索引:

CREATE INDEX IDX_COL1 ON TEST(COL1)

通過HBASE SHELL觀察生成的索引表IDX_COL1。我們發現全局索引表的RowKey存儲了索引列的值和原表RowKey的值,這樣編碼更有利於提高查詢的性能。

hbase(main):001:0> scan 'IDX_COL1' ROW COLUMN+CELL 2\x001 column=0:_0, timestamp=1520935113031, value=x 1 row(s) in 0.1650 seconds

實際上全局索引的RowKey將會按照如下格式進行編碼。
Screen_Shot_2018_03_13_at_18_04_32

  • SALT BYTE: 全局索引表和普通phoenix表一樣,可以在創建索引時指定SALT_BUCKETS或者split key。此byte正是存儲着salt。
  • TENANT_ID: 當前數據對應的多租戶ID。
  • INDEX VALUE: 索引數據。
  • PK VALUE: 原表的RowKey。

2. 本地索引

因為本地索引和原數據是存儲在同一個表中的,所以更適合寫多的場景。對於本地索引,查詢中無論是否指定hint或者是查詢的列是否都在索引表中,都會使用索引表。

創建本地索引:

create local index LOCAL_IDX_COL1 ON TEST(COL1);

通過HBASE SHELL觀察表'TEST', 我們可以看到表中多了一行column為L#0:_0的索引數據。

hbase(main):001:0> scan 'TEST' ROW COLUMN+CELL \x00\x002\x001 column=L#0:_0, timestamp=1520935997600, value=_0 1 column=0:COL1, timestamp=1520935997600, value=2 1 column=0:COL2, timestamp=1520935997600, value=3 1 column=0:_0, timestamp=1520935997600, value=x 2 row(s) in 0.1680 seconds

本地索引的RowKey將會按照如下格式進行編碼:
Screen_Shot_2018_03_13_at_20_16_24

  • REGION START KEY : 當前row所在region的start key。加上這個start key的好處是,可以讓索引數據和原數據盡量在同一個region, 減小IO,提升性能。
  • INDEX ID : 每個ID對應不同的索引表。
  • TENANT ID :當前數據對應的多租戶ID。
  • INDEX VALUE: 索引數據。
  • PK VALUE: 原表的RowKey。

3. 覆蓋索引

覆蓋索引的特點是把原數據存儲在索引數據表中,這樣在查詢到索引數據時就不需要再次返回到原表查詢,可以直接拿到查詢結果。

創建覆蓋索引:

create index IDX_COL1_COVER_COL2 on TEST(COL1) include(COL2);

通過HBASE SHELL 查詢表IDX_COL1_COVER_COL2, 我們發現include的列的值被寫入到了value中。

hbase(main):003:0> scan 'IDX_COL1_COVER_COL2' ROW COLUMN+CELL 2\x001 column=0:0:COL2, timestamp=1520943893821, value=3 2\x001 column=0:_0, timestamp=1520943893821, value=x 1 row(s) in 0.0180 seconds

對於類似select col2 from TEST where COL1='2'的查詢,查詢一次索引表就能獲得結果。其查詢計划如下:

+--------------------------------------------------------------------------------------+-----------------+----------------+---+
| PLAN | EST_BYTES_READ | EST_ROWS_READ | E | +--------------------------------------------------------------------------------------+-----------------+----------------+---+ | CLIENT 1-CHUNK PARALLEL 1-WAY ROUND ROBIN RANGE SCAN OVER IDX_COL1_COVER_COL2 ['2'] | null | null | n | +--------------------------------------------------------------------------------------+-----------------+----------------+---+

4. 函數索引

函數索引的特點是能根據表達式創建索引,適用於對查詢表,過濾條件是表達式的表創建索引。例如:

//創建函數索引
CREATE INDEX CONCATE_IDX ON TEST (UPPER(COL1||COL2)) //查詢函數索引 SELECT * FROM TEST WHERE UPPER(COL1||COL2)='23'

三、什么是Phoenix的二級索引?

Phoenix的二級索引我們基本上已經介紹過了,我們回過頭來繼續看Phoenix二級索引的官方定義:Secondary indexes are an orthogonal way to access data from its primary access path。簡單理解為,在主訪問路徑(通過row key訪問)上發生正交的一種方法,更清楚的應該描述為:索引列訪問和row key訪問產生交集時的一種索引方法。我們來通過一個例子說明:

1. 對表TESTCOL1創建全局索引
CREATE INDEX IDX_COL1 ON TEST(COL1);
2. 對於如下查詢必將發生FULL SCAN。
select * from TEST where COL1='2';

以上查詢的查詢計划如下:

+----------------------------------------------------------------+-----------------+----------------+--------------+
|                              PLAN                              | EST_BYTES_READ  | EST_ROWS_READ  | EST_INFO_TS  |
+----------------------------------------------------------------+-----------------+----------------+--------------+
| CLIENT 1-CHUNK PARALLEL 1-WAY ROUND ROBIN FULL SCAN OVER TEST | null | null | null | | SERVER FILTER BY COL1 = '2' | null | null | null | +----------------------------------------------------------------+-----------------+----------------+--------------+
3. 對於以下查詢將會形成點查。因為二級索引是RowKey的交集。
select * from TEST where id='1' and COL1='2'

查詢計划如下

+---------------------------------------------------------------------------------------------+-----------------+-------------+
| PLAN | EST_BYTES_READ | EST_ROWS_RE | +---------------------------------------------------------------------------------------------+-----------------+-------------+ | CLIENT 1-CHUNK 1 ROWS 203 BYTES PARALLEL 1-WAY ROUND ROBIN POINT LOOKUP ON 1 KEY OVER TEST | 203 | 1 | | SERVER FILTER BY COL1 = '2' | 203 | 1 | +---------------------------------------------------------------------------------------------+-----------------+-------------+

對於2中所描述的查詢為什么會發生FULL SCAN? 正如Phoenix二級索引官方定義的一樣,因為“沒有和RowKey列的查詢發生正交關系”,除非使用Hint強制指定索引表。


四、索引Building

Phoenix的二級索引創建有同步和異步兩種方式。

  1. 在執行CREATE INDEX IDX_COL1 ON TEST(COL1)時會進行索引數據的同步。此方法適用於數據量較小的情況。
  2. 異步build索引需要借助MR,創建異步索引語法和同步索引相差一個關鍵字:ASYNC
//創建異步索引
CREATE INDEX ASYNC_IDX ON DB.TEST (COL1) ASYNC //build 索引數據 ${HBASE_HOME}/bin/hbase org.apache.phoenix.mapreduce.index.IndexTool --schema DB --data-table TEST --index-table ASYNC_IDX --output-path ASYNC_IDX_HFILES

五、索引問題匯總

1. 創建同步索引超時怎么辦?

在客戶端配置文件hbase-site.xml中,把超時參數設置大一些,足夠build索引數據的時間。

<property> <name>hbase.rpc.timeout</name> <value>60000000</value> </property> <property> <name>hbase.client.scanner.timeout.period</name> <value>60000000</value> </property> <property> <name>phoenix.query.timeoutMs</name> <value>60000000</value> </property>

2. 索引表最多可以創建多少個?

建議不超過10個

3. 為什么索引表多了,單條寫入會變慢?

索引表越多寫放大越嚴重。寫放大情況可以參考下圖。

Screen_Shot_2018_03_13_at_21_36_26

References

轉自:https://yq.aliyun.com/articles/536850

 


交流

如果大家對HBase有興趣,致力於使用HBase解決實際的問題,歡迎加入Hbase技術社區群交流:

微信HBase技術社區群,假如微信群加不了,可以加秘書微信: SH_425 ,然后邀請您。

 

 

​  釘釘HBase技術社區群


免責聲明!

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



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