Phoenix系列:二級索引(1)


Phoenix使用HBase作為后端存儲,對於HBase來說,我們通常使用字典序的RowKey來快速訪問數據,除此之外,也可以使用自定義的Filter來搜索數據,但是它是基於全表掃描的。而Phoenix提供的二級索引是可以避開全表掃描,是在HBase中快速查找或批量檢索數據的另一個選擇。下面的例子使用如下表進行測試:

CREATE TABLE HAO1  ( id char(36) not null primary key, name varchar(50), age INTEGER, createtime DATE)

本文的SQL Client是SQuirrel 3.7.1。

覆蓋索引 Covered Indexes

Phoenix提供了一種叫Covered Index覆蓋索引的二級索引。這種索引在獲取數據的過程中,內部不需要再去HBase上獲取任何數據,你查詢需要返回的列的數據都會被存儲在索引中。要想達到這種效果,你的select 的列,where 的列都需要在索引中出現。舉個例子,如果你的SQL語句是 select name from hao1 where age=2,要最大化查詢效率和速度最快,你就需要建立覆蓋索引:

CREATE INDEX index1_c ON hao1 (age) INCLUDE(name);

注意關鍵字INCLUDE,就是包含需要返回數據結果的列。這種索引方式的最大好處就是速度快,而我們也知道,索引就是空間換時間,所以缺點也很明顯,存儲空間耗費較多。Phoenix的索引其實就是建了一張HBase的表。你可以通過HBase Shell的list命令看到。查看表index1_c,你會發現,這張表一共三列,一列就是索引,第二列是RowKey,最后一列就是Name的值。很明顯在這里記錄的RowKey,就是為了快速查找HBase中的數據。只是這里用不到,Name已經被緩存在這張索引里面了,直接返回。

image

我們來看一下執行計划,首先看一下沒有查詢條件的計划,如下圖,是一個全表掃描的計划:

image

而加了索引以后,就是下圖這樣。很明顯,已經是Range Scan,使用到了索引INDEX1_C。

image

但要注意的是,如果是 select * from hao1 where age =2 的話,還是會看原數據表,只是使用RowKey去訪問了,效率自然沒有直接從索引表中回去。因為select 的其他列不在索引表內。

 

函數索引 Functional Indexes

函數索引從4.3版本就有,這種索引的內容不局限於列,還能在表達式上建立索引。如果你使用的表達式正好就是索引的話,數據也可以直接從這個索引獲取,而不需要從數據庫獲取。比如說,在一個表達式上建立索引,這個表達式是UPPER(name) || '_test':

CREATE INDEX index2_f ON hao1 (UPPER(name) || '_test');

同樣的index2_f表會被建立,里面存儲了表達式求值后的結果,和RowKey的關系。當然也可以添加INCLUDE作為覆蓋索引,做了覆蓋索引,就不需要再去原數據表中獲取數據。但是數據會多很多。

image

 

在索引范圍上,Phoenix的索引可以分為全局索引和本地索引,兩種索引適合的場景不同。

全局索引 Global Indexes

全局索引適合那些讀多寫少的場景。如果使用全局索引,讀數據基本不損耗性能,所有的性能損耗都來源於寫數據。數據表的添加、刪除和修改都會更新相關的索引表(數據刪除了,索引表中的數據也會刪除;數據增加了,索引表的數據也會增加)。而查詢數據的時候,Phoenix會通過索引表來快速低損耗的獲取數據。默認情況下,如果你的查詢語句中沒有索引相關的列的時候,Phoenix不會使用索引。

 

本地索引 Local Indexes

本地索引適合那些寫多讀少,或者存儲空間有限的場景。和全局索引一樣,Phoenix也會在查詢的時候自動選擇是否使用本地索引。本地索引之所以是本地,只要是因為索引數據和真實數據存儲在同一台機器上,這樣做主要是為了避免網絡數據傳輸的開銷。如果你的查詢條件沒有完全覆蓋索引列,本地索引還是可以生效。因為無法提前確定數據在哪個Region上,所以在讀數據的時候,還需要檢查每個Region上的數據而帶來一些性能損耗。

如下示例,創建了本地索引,然后查看了索引表的數據。

CREATE LOCAL INDEX index3_l_name ON hao1 (name);

image

 

異步創建索引

一般我們可以使用CREATE INDEX來創建一個索引,這是一種同步的方法。但是有時候我們創建索引的表非常大,我們需要等很長時間。Phoenix 4.5以后有一個異步創建索引的方式,使用關鍵字ASYNC來創建索引:

CREATE INDEX index1_c ON hao1 (age) INCLUDE(name) ASYNC;

這時候創建的索引表中不會有數據。你還必須要單獨的使用命令行工具來執行數據的創建。當語句給執行的時候,后端會啟動一個map reduce任務,只有等到這個任務結束,數據都被生成在索引表中后,這個索引才能被使用。啟動工具的方法:

${HBASE_HOME}/bin/hbase org.apache.phoenix.mapreduce.index.IndexTool
  --schema MY_SCHEMA --data-table MY_TABLE --index-table ASYNC_IDX
  --output-path ASYNC_IDX_HFILES

這個任務不會因為客戶端給關閉而結束,是在后台運行。你可以在指定的文件ASYNC_IDX_HFILES中找到最終實行的結果。

 

索引的使用

索引定義完之后,一般來說,Phoenix會判定使用哪個索引更加有效。但是,全局索引必須是查詢語句中所有列都包含在全局索引中,它才會生效。舉個例子,下面是創建索引的語句:
CREATE INDEX index1_c ON hao1 (age)

而查詢語句是:

select name from hao1 where age = 35;

上例就不會用到索引index1_c。因為name並沒有包含在索引中。所以使用全局索引,必須要所有的列都包含在索引中。那么怎樣才能使用上索引呢?有三種方法。

1. 創建索引的時候使用覆蓋索引。

CREATE INDEX index1_c ON hao1 (age) INCLUDE(name);

這種索引會把name加到索引表里面,同時name也會隨着原數據表中的變化而變化。這種方式很明顯的缺點是索引表的大小較大,然后就是全局索引不適合寫特別多的情況。

2. 使用類似於Oracle的Hint,強制索引。

select /*+ INDEX(HAO1 index1_c)*/ name from hao1 where age = 35;

查詢引擎會使用index1_c這個索引,由於它會發現索引表中沒有name數據,所以每一行它都會去原數據表中獲取name的值。這個強制索引只有在你認為索引有比較好的選擇性的時候才是好的選擇,也就是說age等於35的行數不多。不然的話,使用Phoenix默認的全表掃描的性能也許會更好。

3. 創建本地索引

CREATE LOCAL INDEX index1_c ON hao1 (age)

本地索引和全局索引不同的是,查詢語句中,即使所有的列都不在索引定義中,它也會使用索引,這是本地索引的默認行為。Phoenix知道原數據和索引數據在同一個RegionServer上,能保證索引查找是本地的。

 

索引的刪除

通過如下命令刪除一個索引:

drop index index1_c ON hao1;

如果一個被索引的列被刪除了,那么這個索引也會被自動刪除。如果一個被覆蓋索引的列被刪除了,那么這個覆蓋列也會自動從索引中刪除。


免責聲明!

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



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