Phoenix 非同步建立索引
date: 2020-11-07 15:34:00
updated: 2020-11-07 19:16:00
Phoenix 非同步建立索引
當表資料量過大的時候,建立索引會報錯,可以修改伺服器端的 hbase.rpc.timeout
,預設是1分鐘,可以自定義時間。也可以非同步建立索引,通過在語句後面新增async
關鍵字。
需要注意的是:
- 非同步建立索引只支援全域性索引
- 執行async語句只是第一步,還需要通過執行jar包來保證索引真正的建立
1. 為什麼只支援全域性索引?
首先是本地索引和全域性索引的一些概念和區別
- 本地索引
- 適合寫多讀少的情況
- 索引資料直接寫在原表裡,不會新建一張表。在
phoenix-sqlline
!tables
的確會發現建立的本地索引表,但是那個只是一個對映,並不是單獨存在的。由於索引資料直接寫在表裡,所以原表的資料量=原始資料+索引資料。 - 本地索引rowkey的設計規則: 原資料region的start key+"\x00"+二級索引欄位1+"\x00"+二級索引欄位2(複合索引)…+"\x00"+原rowkey。
- 索引資料和真實資料存放在同一臺機器上,減少了網路傳輸的開銷。同理,建立索引後的rowkey的最開始的部分是 原資料region的start key,這樣在通過二級索引定位到資料後,可以在當前的region中直接找到資料,減少網路開銷。減少網路開銷,也意味著寫入的速度會變快。但是多了一步通過rowkey查詢資料的過程,所以讀的過程就不如直接讀取列族裡的資料的速度快。
- 全域性索引
- 適合讀多寫少的情況
- 索引資料會單獨存在一張表裡。
- 全域性索引必須是查詢語句中所有列都包含在全域性索引中,它才會生效。
Select * 不會命中索引
select 具體的欄位 from table where col ...
col 必須是第一個主鍵或者是索引裡包含的欄位才會命中索引
如果索引表包含 a、b 三個欄位,where 裡有 a 和 c 兩個欄位,那麼也不會走索引,因為c不在索引裡,發現索引走不通,只能走全表 - 為了命中索引,要把需要查詢的欄位通過 include 關鍵字來一起寫入索引表裡,也就是覆蓋索引。
- 寫入資料的同時需要往索引表同步寫資料,而索引表是分佈在不同的資料節點上的,跨節點的資料傳輸帶來了較大的效能消耗,所以寫慢;但是查詢的時候,如果命中了索引表,那就直接把資料帶出來了,讀會快。
綜上,本地索引不是表,全域性索引才是表,而async是針對表的一種方式,所以只能作用於全域性索引
2. 如何執行async
- 首先是需要建立一個全域性索引,同時使用 async
create index XXX on database.tablename(col1, col2) include(col3, col4) async
此時去看這個表,會發現 index_state
欄位的值是 building,說明索引表還沒建立好,這是因為 async 關鍵字會初始化一個mr作業,只是把建立索引的資料檔案準備好,還沒有正式開始
- 執行mr作業
hbase org.apache.phoenix.mapreduce.index.IndexTool \
--schema 庫名 --data-table 表名 --index-table 索引表名 \
--output-path hdfs路徑指向一個檔案件即可
庫名、表名、索引表名儘量都不要小寫
這個命令執行後可能會報錯,遇到 org.apache.phoenix.mapreduce.index.IndexTool 依賴的jar沒法載入,那就可以換一個方式執行
java -cp ./本地資料夾路徑:/data1/cloudera/parcels/PHOENIX/lib/phoenix/phoenix-5.0.0-cdh6.2.0-client.jar org.apache.phoenix.mapreduce.index.IndexTool --schema 庫名 --data-table 表名 --index-table 索引表名 --output-path hdfs路徑指向一個檔案件即可
本地資料夾裡需要包含 hbase yarn hdfs 的配置檔案
如果遇到 java.io.IOException: Can't get Master Kerberos principal for use as renewer
說明缺少yarn的配置檔案
如果遇到 org.apache.hadoop.security.AccessControlException: Permission denied: user=phoenix, access=WRITE, inode="/user":hdfs:supergroup:drwxr-xr-x
需要在 hbase-site.xml
檔案裡新增 hbase.fs.tmp.dir
配置項,值是hdfs上一個有讀寫許可權的目錄路徑。
原因:從 org.apache.phoenix.mapreduce.index.IndexTool 開始追程式碼,會找到 org.apache.hadoop.hbase.mapreduce.HFileOutputFormat2,在配置mr作業的時候,configurePartitioner()
方法裡 String hbaseTmpFsDir = conf.get("hbase.fs.tmp.dir", HConstants.DEFAULT_TEMPORARY_HDFS_DIRECTORY);
會去讀取配置檔案裡的這個值,預設是 "/user/" + System.getProperty("user.name") + "/hbase-staging"
3. 附
- 在建立索引的過程中,發現了一個可能是版本bug的地方,已提官網issue,連結如下
問題:如果一個欄位有預設值,比如一個表有 abc 三個欄位,ab是主鍵,c有預設值,c tinyint default 0
,現在要根據b和c來查詢,為了提高效率,那就需要針對b和c建立本地索引的時候,檢視索引表(雖然本地索引不是一個表,只是一個對映),會自動把 a 欄位也帶入進去,這時候會發現 a 欄位的值發生了變化;如果 c 沒有預設值,那麼重複這個操作不會有問題。如果 c 設定成 integer 的話,也沒有問題,感覺是 tinyint 的某些實現有衝突。
附:
PHOENIX_OPTS="-Dsun.security.krb5.principal=phoenix"
/usr/java/jdk1.8.0_121/bin/java $PHOENIX_OPTS -cp "/etc/hbase/conf:/etc/hbase/conf:/data/cloudera/parcels/PHOENIX-5.0.0-cdh6.2.0.p0.1308267/lib/phoenix/bin/../phoenix-5.0.0-cdh6.2.0-client.jar:::/etc/hadoop/conf:/etc/hadoop/conf:/data/cloudera/parcels/CDH-6.2.0-1.cdh6.2.0.p0.967373/lib/hadoop/libexec/../../hadoop/lib/*:/data/cloudera/parcels/CDH-6.2.0-1.cdh6.2.0.p0.967373/lib/hadoop/libexec/../../hadoop/.//*:/data/cloudera/parcels/CDH-6.2.0-1.cdh6.2.0.p0.967373/lib/hadoop/libexec/../../hadoop-hdfs/./:/data/cloudera/parcels/CDH-6.2.0-1.cdh6.2.0.p0.967373/lib/hadoop/libexec/../../hadoop-hdfs/lib/*:/data/cloudera/parcels/CDH-6.2.0-1.cdh6.2.0.p0.967373/lib/hadoop/libexec/../../hadoop-hdfs/.//*:/data/cloudera/parcels/CDH/lib/hadoop-mapreduce/.//*:/data/cloudera/parcels/CDH-6.2.0-1.cdh6.2.0.p0.967373/lib/hadoop/libexec/../../hadoop-yarn/lib/*:/data/cloudera/parcels/CDH-6.2.0-1.cdh6.2.0.p0.967373/lib/hadoop/libexec/../../hadoop-yarn/.//*" -Dlog4j.configuration=file:/data/penglin/log4j.properties sqlline.SqlLine -d org.apache.phoenix.jdbc.PhoenixDriver -u jdbc:phoenix:host241.slave.dev.cluster.enn.cn:2181:/hbase -n none -p none --color=true --fastConnect=false --verbose=true --incremental=false --isolation=TRANSACTION_READ_COMMITTED
- 查詢執行計劃,判斷是否命中索引表
內容 | 含義 |
---|---|
CLIENT | 表明操作在客戶端執行還是服務端執行,客戶端儘量返回少的資料。若為 SERVER 表示在服務端執行。 |
FILTER BY expression | 返回和過濾條件匹配的結果。 |
FULL SCAN OVER tableName | 表明全表掃描某張業務表。 |
RANGE SCAN OVER tableName [ … ] | 表明代表範圍掃描某張表,括號內代表 rowkey 的開始和結束。 |
ROUND ROBIN | 無 ORDER BY 操作時, ROUND ROBIN 代表最大化客戶端的並行化。 |
x-CHUNK | 執行此操作的執行緒數。 |
PARALLEL x-WAY | 表明合併多少並行的掃描。 |
EST_BYTES_READ | 執行查詢時預計掃描的總位元組數。 |
EST_ROWS_READ | 執行查詢時預計掃描多少行。 |
EST_INFO_TS | 收集查詢資訊的 epoch time |
- 如何命中索引
- select * 不會命中索引
- select 具體的欄位 from table where col ... // col 是第一個主鍵或者是索引裡包含的欄位
如果 index 包含 a、b 三個欄位,where 裡有 a 和 c 兩個欄位,那麼也不會走索引,因為c不在索引裡,發現索引走不通,只能走全表
如果 index 包含 a、b 三個欄位,where 裡有 a 欄位,select 裡有 a, c 欄位,會走索引