phoenix全域性索引和本地索引測試
1.測試表說明
原hbase表是隻有1個列族,算上rowkey一共6個欄位的hbase表。
一共37個regions,資料量一共3億6千4百萬
hbase中表描述
資料樣例
資料量
2.建立索引
hbase的二級索引在phoenix中建立。
建索引的語句如下,建好索引之後,有資料的變更索引資料和原始資料會實時的同步更新
create index car_index_index1 on car(hphm)
因為這張表是已經存在的大表,而且資料量也不少,直接用上面的語句建立索引時間會很長,所以這裡用批量建立索引的方式
#首先在phoenix中建立索引表資訊
create index car_index_datehphm on "car"("f1"."date","f1"."hphm") include ("f1"."coorid","f1"."cx","f1"."ys") async;
#這裡的建立索引語句加了async,非同步建立索引。另外f1是hbase中原始的列族名,這張表是原始hbase錶轉過來的,為什麼這麼寫就不解釋了,"f1"."date"就代表一個欄位。include是什麼後面再解釋
#下面啟動批量建立索引的mr任務
${HBASE_HOME}/bin/hbase org.apache.phoenix.mapreduce.index.IndexTool \
--data-table "car" --index-table CAR_INDEX_DATEHPHM \
--output-path ASYNC_IDX_HFILES
3億6千萬的資料,批量建立索引時間為1小時10分鐘
3.索引測試
上面的建立的索引是兩個欄位的複合索引。可以分別對兩個欄位進行單獨或者一起的條件過濾查詢
##車牌查詢
0: jdbc:phoenix:localhost:2181:/hbase> select * from "car" where "f1"."hphm" like '蘇E3G%' limit 10;
+-------------------------+---------+------+-----------------+----------+-----+
| ID | coorid | cx | date | hphm | ys |
+-------------------------+---------+------+-----------------+----------+-----+
| 20170101005027_蘇E3G2SA | t | 賓士 | 20170101005027 | 蘇E3G2SA | 黃 |
| 20170101011949_蘇E3GS62 | � | 奧迪 | 20170101011949 | 蘇E3GS62 | 銀 |
| 20170101014325_蘇E3G7S8 | � | 豐田 | 20170101014325 | 蘇E3G7S8 | 銀 |
| 20170101022906_蘇E3GPQB | D | 保時捷 | 20170101022906 | 蘇E3GPQB | 黃 |
| 20170101030015_蘇E3G2SA | | 賓士 | 20170101030015 | 蘇E3G2SA | 黃 |
| 20170101030157_蘇E3G2SA | � | 賓士 | 20170101030157 | 蘇E3G2SA | 黃 |
| 20170101033901_蘇E3G117 | � | 奧迪 | 20170101033901 | 蘇E3G117 | 藍 |
| 20170101040047_蘇E3G7S8 | | 豐田 | 20170101040047 | 蘇E3G7S8 | 銀 |
| 20170101040114_蘇E3G8CW | * | 寶馬 | 20170101040114 | 蘇E3G8CW | 黃 |
| 20170101041608_蘇E3G6GH | $ | 賓士 | 20170101041608 | 蘇E3G6GH | 紅 |
+-------------------------+---------+------+-----------------+----------+-----+
10 rows selected (1.767 seconds)
用時1.767秒,分析原因為車牌種類比較少,篩選出的速度會稍微慢點,如果建立二級索引第一個欄位用車牌,速度應該會快很多。
## 日期查詢
0: jdbc:phoenix:localhost:2181:/hbase> select * from "car" where "f1"."date">='20170110' and "f1"."date"<='20170210' limit 10;
+-------------------------+---------+------+-----------------+----------+-----+
| ID | coorid | cx | date | hphm | ys |
+-------------------------+---------+------+-----------------+----------+-----+
| 20170110000000_京AM7BPF | t | 豐田 | 20170110000000 | 京AM7BPF | 白 |
| 20170110000000_京C4SS4G | � | 大眾 | 20170110000000 | 京C4SS4G | 紅 |
| 20170110000000_新D5AGEG | � | 寶馬 | 20170110000000 | 新D5AGEG | 黃 |
| 20170110000000_新DFMM96 |
| 賓士 | 20170110000000 | 新DFMM96 | 黃 |
| 20170110000000_滬HB1R23 | � | 保時捷 | 20170110000000 | 滬HB1R23 | 銀 |
| 20170110000000_滬IMARPP | � | 豐田 | 20170110000000 | 滬IMARPP | 白 |
| 20170110000000_滬KR5QA2 | � | 日產 | 20170110000000 | 滬KR5QA2 | 黑 |
| 20170110000000_浙KQDWM9 | � | 日產 | 20170110000000 | 浙KQDWM9 | 黑 |
| 20170110000000_湘BMPWCM | � | 賓士 | 20170110000000 | 湘BMPWCM | 銀 |
| 20170110000000_湘HHS4E9 | � | 日產 | 20170110000000 | 湘HHS4E9 | 紅 |
+-------------------------+---------+------+-----------------+----------+-----+
10 rows selected (0.049 seconds)
用時為49毫秒。
##日期車牌查詢
0: jdbc:phoenix:localhost:2181:/hbase> select * from "car" where "f1"."date">='20170110' and "f1"."date"<='20170210' and "f1"."hphm" like '.E3G%' limit 10;
+-------------------------+---------+------+-----------------+----------+-----+
| ID | coorid | cx | date | hphm | ys |
+-------------------------+---------+------+-----------------+----------+-----+
| 20170110005537_蘇E3GS62 | � | 奧迪 | 20170110005537 | 蘇E3GS62 | 銀 |
| 20170110010344_蘇E3G21Q | K | 謳歌 | 20170110010344 | 蘇E3G21Q | 黃 |
| 20170110013131_蘇E3G21Q | � | 謳歌 | 20170110013131 | 蘇E3G21Q | 黃 |
| 20170110013318_蘇E3G21Q | � | 謳歌 | 20170110013318 | 蘇E3G21Q | 黃 |
| 20170110013452_蘇E3GQCF | C | 沃爾沃 | 20170110013452 | 蘇E3GQCF | 白 |
| 20170110034239_蘇E3G7S8 | � | 豐田 | 20170110034239 | 蘇E3G7S8 | 銀 |
| 20170110041044_蘇E3G2SA | 6 | 賓士 | 20170110041044 | 蘇E3G2SA | 黃 |
| 20170110041829_蘇E3GEM2 | m | 謳歌 | 20170110041829 | 蘇E3GEM2 | 藍 |
| 20170110045503_蘇E3G2SA | | 賓士 | 20170110045503 | 蘇E3G2SA | 黃 |
| 20170110050616_蘇E3GS62 | O | 奧迪 | 20170110050616 | 蘇E3GS62 | 銀 |
+-------------------------+---------+------+-----------------+----------+-----+
10 rows selected (0.706 seconds)
用時700多毫秒
4.索引型別
phoenix的索引大致分為兩類global index和local index,好像和星環有點類似,其實這是hbase二級索引解決方案裡面廣為人知的兩種方案,側重點不同,使用場景也不一樣。
- global index,global是預設的索引格式。官方文件翻譯過來的:Global indexing適用於多讀少寫的業務場景。使用Global indexing的話在寫資料的時候會消耗大量開銷,因為所有對資料表的更新操作(DELETE, UPSERT VALUES and UPSERT SELECT),會引起索引表的更新,而索引表是分佈在不同的資料節點上的,跨節點的資料傳輸帶來了較大的效能消耗。在讀資料的時候Phoenix會選擇索引表來降低查詢消耗的時間。在預設情況下如果想查詢的欄位不是索引欄位的話索引表不會被使用,也就是說不會帶來查詢速度的提升。
- Local index,適用於寫操作頻繁的場景。與Global index一樣,Phoenix會自動判定在進行查詢的時候是否使用索引。使用Local indexing時,索引資料和資料表的資料是存放在相同的伺服器中的避免了在寫操作的時候往不同伺服器的索引表中寫索引帶來的額外開銷。使用Local indexing的時候即使查詢的欄位不是索引欄位索引表也會被使用,這會帶來查詢速度的提升,這點跟Global indexing不同。一個數據表的所有索引資料都儲存在一個單一的獨立的可共享的表中。
官方文件上寫的太書面了,下面用幾個例子就能明白這兩種索引格式是怎麼設計以及怎樣執行原理了。
4.1 global index 測試
上面的測試例子用的是global index,後面的例子為了快,就建立一些測試表。
##建立表
create table test_global(id varchar primary key,f1 varchar,f2 varchar);
##建立global index
create index test_global_index on test_global(f1) include(f2);
##插入兩條資料
upsert into test_global values('1','2','3');
upsert into test_global values('4','5','6');
##檢視索引表資料
select * from test_global_index;
+-------+------+-------+
| 0:F1 | :ID | 0:F2 |
+-------+------+-------+
| 2 | 1 | 3 |
| 5 | 4 | 6 |
+-------+------+-------+
2 rows selected (0.037 seconds)
##檢視hbase表上面的資料
scan 'TEST_GLOBAL_INDEX'
hbase(main):002:0> scan 'TEST_GLOBAL_INDEX'
ROW COLUMN+CELL
2\x001 column=0:\x00\x00\x00\x00, timestamp=1501489856443, value=\x00\x00\x00\x00
2\x001 column=0:\x80\x0B, timestamp=1501489856443, value=3
5\x004 column=0:\x00\x00\x00\x00, timestamp=1501489860185, value=\x00\x00\x00\x00
5\x004 column=0:\x80\x0B, timestamp=1501489860185, value=6
2 row(s) in 0.0620 seconds
global index
1.以上可以看出global index的設計方式,會單獨寫一張索引表,列族為include欄位,rowkey的設計方位是:
二級索引欄位1+”\x00”+二級索引欄位2(複合索引)…+”\x00”+原表rowkey
2.查詢的時候,會直接定位到索引表,通過rowkey查到位置,然後從索引表中帶出資料
3.因為建立索引的時候還要多寫一份include欄位,讀的時候直接從索引表定位並讀出資訊。所以這種表的應用場景定位是寫的慢,讀得快
4.2 local index 測試
##建立表
create table test_local(id varchar primary key,f1 varchar,f2 varchar);
##建立local index
create local index test_local_index on test_local(f1);
##插入兩條資料
upsert into test_local values('1','2','3');
upsert into test_local values('4','5','6');
##local index 並不會建立新的表,而是在原來的表裡面寫入索引資料
##檢視hbase表中資料
hbase(main):014:0> scan 'TEST_LOCAL'
ROW COLUMN+CELL
\x00\x002\x001 column=L#0:\x00\x00\x00\x00, timestamp=1501491310774, value=\x00\x00\x00\x00
\x00\x005\x004 column=L#0:\x00\x00\x00\x00, timestamp=1501491315118, value=\x00\x00\x00\x00
1 column=0:\x00\x00\x00\x00, timestamp=1501491310774, value=x
1 column=0:\x80\x0B, timestamp=1501491310774, value=2
1 column=0:\x80\x0C, timestamp=1501491310774, value=3
4 column=0:\x00\x00\x00\x00, timestamp=1501491315118, value=x
4 column=0:\x80\x0B, timestamp=1501491315118, value=5
4 column=0:\x80\x0C, timestamp=1501491315118, value=6
4 row(s) in 0.0250 seconds
local index
1.以上可以看出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到原資料裡面獲取其它資訊。所以這種表的應用場景定位是寫的快,讀得慢
4.3 local index和global index比較
1.索引資料
- global index單獨把索引資料存到一張表裡,保證了原始資料的安全,侵入性小
- local index把資料寫到原始資料裡面,侵入性強,原表的資料量=原始資料+索引資料,使原始資料更大
2.效能方面
- global index要多寫出來一份資料,寫的壓力就大一點,但讀的速度就非常快
- local index只用寫一份索引資料,節省不少空間,但多了一步通過rowkey查詢資料,寫的速度非常快,讀的速度就沒有直接取自己的列族資料快。
4.4其它
phoenix大的方面分為這兩種索引,細的方面可以分為4種。phoenix還另外加了一種配置,根據索引是否可變,意思就是為了減少寫入的壓力,索引表只建立一次不會更改。
5.總結
用phoenix只是用hbase自身實現了hbase自己的二級索引,用hbase自己rowkey查詢的特點來設計索引資料的rowkey,效能方面完全要靠一次檢索索引資料的資料量大小了,所以具體業務具體分析。