Phoenix創建Hbase二級索引
1. 配置Hbase支持Phoenix創建二級索引
1. 添加如下配置到Hbase的Hregionserver節點的hbase-site.xml

<!-- phoenix regionserver 配置參數 --> <property> <name>hbase.regionserver.wal.codec</name> <value>org.apache.hadoop.hbase.regionserver.wal.IndexedWALEditCodec</value> </property> <property> <name>hbase.region.server.rpc.scheduler.factory.class</name> <value>org.apache.hadoop.hbase.ipc.PhoenixRpcSchedulerFactory</value> <description>Factory to create the Phoenix RPC Scheduler that uses separate queues for index and metadata updates</description> </property> <property> <name>hbase.rpc.controllerfactory.class</name> <value>org.apache.hadoop.hbase.ipc.controller.ServerRpcControllerFactory</value> <description>Factory to create the Phoenix RPC Scheduler that uses separate queues for index and metadata updates</description> </property>
2. 添加如下配置到Hbase中Hmaster節點的hbase-site.xml中

<!-- phoenix master 配置參數 --> <property> <name>hbase.master.loadbalancer.class</name> <value>org.apache.phoenix.hbase.index.balancer.IndexLoadBalancer</value> </property> <property> <name>hbase.coprocessor.master.classes</name> <value>org.apache.phoenix.hbase.index.master.IndexMasterObserver</value> </property>
可以在hbase-site.xml里配置以下調優參數

index.builder.threads.max o 為主表更新操作建立索引的最大線程數 o Default: 10 index.builder.threads.keepalivetime o 上面線程的超時時間 o Default: 60 index.writer.threads.max o 將索引寫到索引表的最大線程數 o Default: 10 index.writer.threads.keepalivetime o 上面線程的超時時間 o Default: 60 hbase.htable.threads.max o 同時最多有這么多線程往索引表寫入數據 o Default: 2,147,483,647 hbase.htable.threads.keepalivetime o 上面線程的超時時間 o Default: 60 index.tablefactory.cache.size o 緩存10個往索引表寫數據的線程 o Default: 10
3. 常見問題匯總:
1)注意:網上配置文檔里有這一條,但在實際測試中(測試環境hbase-1.3.1,網上0.98.6),加入該條的regionserver會在hbase啟動時失敗,對應節點上沒有HregionServer進程,去掉該配置后正常啟動,且能正常創建local index。
<property> <name>hbase.coprocessor.regionserver.classes</name> <value>org.apache.hadoop.hbase.regionserver.LocalIndexMerger</value> </property>
2)hbase-site.xml的zookeeeper的配置信息不能加2181,否則在創建local index的時候會報以下異常:
正常配置:
<property> <name>hbase.zookeeper.quorum</name> <value>hadoop101,hadoop102,hadoop103</value> </property>
2. 創建索引
phoenix的索引分類
1)全局索引 global index是默認的索引格式。
適用於多讀少寫的業務場景。寫數據的時候會消耗大量開銷,因為索引表也要更新,而索引表是分布在不同的數據節點上的,跨節點的數據傳輸帶來了較大的性能消耗。
在讀數據的時候Phoenix會選擇索引表來降低查詢消耗的時間。如果想查詢的字段不是索引字段的話索引表不會被使用,也就是說不會帶來查詢速度的提升。
全局索引必須是查詢語句中所有列都包含在全局索引中,它才會生效。
CREATE INDEX MY_INDEX ON STUDENT("info"."dt");
查詢語句:
select "dt" from STUDENT where "dt" >= '2018-08-08'; #可以使用到索引表MY_INDEX select "name","age","dt" from STUDENT where "dt" >= '2018-08-08'; #這樣子就不會用到索引表MY_INDEX,因為name和age不在索引表中;
所以使用全局索引,必須要所有的列都包含在索引中。那么怎樣才能使用上索引呢?有三種方法:
①. 創建索引的時候使用覆蓋索引。
CREATE INDEX MY_INDEX ON STUDENT("info"."dt") INCLUDE("info"."name", "info"."age"); //INCLUED之前的索引,只能建立一個才會走range scan索引,不然就是full scan
這種索引會把name加到索引表里面,同時name也會隨着原數據表中的變化而變化。這種方式很明顯的缺點是索引表的大小較大,然后就是全局索引不適合寫特別多的情況。
Covered Index覆蓋索引的二級索引。這種索引在獲取數據的過程中,內部不需要再去HBase上獲取任何數據,你查詢需要返回的列的數據都會被存儲在索引中。要想達到這種效果,你的select 的列,where 的列都需要在索引中出現。
注意關鍵字INCLUDE(注意跟異步索引的區別),就是包含需要返回數據結果的列。這種索引方式的最大好處就是速度快,而我們也知道,索引就是空間換時間,所以缺點也很明顯,存儲空間耗費較多。Phoenix的索引其實就是建了一張HBase的表。你可以通過HBase Shell的list命令看到。
②. 使用類似於Oracle的Hint,強制索引。
select /*+ INDEX(STUDENT MY_INDEX)*/ "name","age" from STUDENT where "dt" >= '2018-08-08';
查詢引擎會使用MY_INDEX這個索引,由於它會發現索引表中沒有name,age數據,所以每一行它都會去原數據表中獲取name,age的值。這個強制索引只有在你認為索引有比較好的選擇性的時候才是好的選擇,也就是說dt大於2018-08-08的行數不多。不然的話,使用Phoenix默認的全表掃描的性能也許會更好。
③. 創建本地索引
CREATE LOCAL INDEX MY_INDEX ON STUDENT("info"."dt");
本地索引和全局索引不同的是,查詢語句中,即使所有的列都不在索引定義中,它也會使用索引,這是本地索引的默認行為。Phoenix知道原數據和索引數據在同一個RegionServer上,能保證索引查找是本地的。
global index的設計方式,會單獨寫一張索引表,列族為include字段,rowkey的設計方位是:
二級索引字段1+"\x00"+二級索引字段2(復合索引)…+"\x00"+原表rowkey
2.查詢的時候,會直接定位到索引表,通過rowkey查到位置,然后從索引表中帶出數據
3.因為建立索引的時候還要多寫一份include字段,讀的時候直接從索引表定位並讀出信息。所以這種表的應用場景定位是寫的慢,讀得快
2)本地索引 Local index適用於寫操作頻繁的場景。
本地索引適合那些寫多讀少,或者存儲空間有限的場景。和全局索引一樣,Phoenix也會在查詢的時候自動選擇是否使用本地索引。本地索引之所以是本地,只要是因為
索引數據和真實數據存儲在同一台機器上,這樣做主要是為了避免網絡數據傳輸的開銷。如果你的查詢條件沒有完全覆蓋索引列,本地索引還是可以生效。因為無法提前確定數據在哪個Region上,所以在讀數據的時候,還需要檢查每個Region上的數據而帶來一些性能損耗。
如下示例,創建了本地索引
CREATE LOCAL INDEX my_index ON my_table (my_index)
local index的設計方式,索引數據直接寫在原表rowkey中,列族不寫任何實際信息,local index的rowkey的設計方位是:
原數據region的start key+"\x00"+二級索引字段1+"\x00"+二級索引字段2(復合索引)…+"\x00"+原rowkey
第一條信息"原數據region的start key",這樣做的目的是保證索引數據和原數據在一個region上,定位到二級索引后根據原rowkey就可以很快在本region上獲取到其它信息,減少網絡開銷和檢索成本。
2.查詢的時候,會在不同的region里面分別對二級索引字段定位,查到原rowkey后在本region上獲取到其它信息
3.因為這種索引設計方式只寫索引數據,省了不少空間的占用,根據索引信息拿到原rowkey后再根據rowkey到原數據里面獲取其它信息。所以這種表的應用場景定位是寫的快,讀得慢
local index和global index比較
1.索引數據
global index單獨把索引數據存到一張表里,保證了原始數據的安全,侵入性小
local index把數據寫到原始數據里面,侵入性強,原表的數據量=原始數據+索引數據,使原始數據更大
2.性能方面
global index要多寫出來一份數據,寫的壓力就大一點,但讀的速度就非常快
local index只用寫一份索引數據,節省不少空間,但多了一步通過rowkey查找數據,寫的速度非常快,讀的速度就沒有直接取自己的列族數據快。
3)異步創建索引
一般我們可以使用CREATE INDEX來創建一個索引,這是一種同步的方法。但是有時候我們創建索引的表非常大,我們需要等很長時間。Phoenix 4.5以后有一個異步創建索引的方式,使用關鍵字ASYNC來創建索引:
create index MY_INDEX on FRUITS("info"."name") include ("info"."color") async;
這時候創建的索引表中不會有數據。你還必須要單獨的使用命令行工具來執行數據的創建。當語句給執行的時候,后端會啟動一個map reduce任務,只有等到這個任務結束,數據都被生成在索引表中后,這個索引才能被使用。啟動工具的方法:
${HBASE_HOME}/bin/hbase org.apache.phoenix.mapreduce.index.IndexTool \ > --data-table FRUITS --index-table MY_INDEX \ > --output-path ASYNC_IDX_HFILES
注: --schema MY_SCHEMA,不加這個就是默認的schema
注: 上邊盡量不要用小寫,即使""雙引號里邊寫的是小寫它也不識別;
2019-10-10 15:04:01,579 INFO [main] index.IndexTool: Loading HFiles from ASYNC_IDX_HFILES/MY_INDEX 2019-10-10 15:04:01,723 WARN [main] mapreduce.LoadIncrementalHFiles: Skipping non-directory hdfs://hadoop101:9000/user/kris/ASYNC_IDXEX/_SUCCESS 2019-10-10 15:04:01,909 INFO [LoadIncrementalHFiles-0] hfile.CacheConfig: Created cacheConfig: CacheConfig:disabled 2019-10-10 15:04:01,985 INFO [LoadIncrementalHFiles-0] mapreduce.LoadIncrementalHFiles: Trying to load hfile=hdfs://hadoop101:9000/usIDX_HFILES/MY_INDEX/info/6b69a7babd4e4c629b6bee988cf15c5d first=water\x001005 last=watermelon\x001006 2019-10-10 15:04:02,136 INFO [main] index.IndexToolUtil: Updated the status of the index MY_INDEX to ACTIVE
這個任務不會因為客戶端給關閉而結束,是在后台運行。你可以在指定的文件ASYNC_IDX_HFILES中找到最終實行的結果。
在Hbase中查看:
測試:
綜上三種提升效率查詢方式:
1) CREATE INDEX my_index ON my_table (v1) INCLUDE (v2) 2) SELECT /*+ INDEX(my_table my_index) */ v2 FROM my_table WHERE v1 = 'foo' 3) CREATE LOCAL INDEX my_index ON my_table (v1)
如何刪除索引
use "test"再刪索引 drop index "my_index" on "student"不行,必須前邊加schema,默認的不需要加
DROP INDEX "my_index" ON "test"."student"
用phoenix只是用hbase自身實現了hbase自己的二級索引,用hbase自己rowkey查詢的特點來設計索引數據的rowkey,性能方面完全要靠一次檢索索引數據的數據量大小了。