1. 程式人生 > 實用技巧 >Phoenix 非同步建立索引

Phoenix 非同步建立索引

date: 2020-11-07 15:34:00
updated: 2020-11-07 19:16:00

Phoenix 非同步建立索引

當表資料量過大的時候,建立索引會報錯,可以修改伺服器端的 hbase.rpc.timeout,預設是1分鐘,可以自定義時間。也可以非同步建立索引,通過在語句後面新增async 關鍵字。

需要注意的是:

  1. 非同步建立索引只支援全域性索引
  2. 執行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

  1. 首先是需要建立一個全域性索引,同時使用 async

create index XXX on database.tablename(col1, col2) include(col3, col4) async

此時去看這個表,會發現 index_state 欄位的值是 building,說明索引表還沒建立好,這是因為 async 關鍵字會初始化一個mr作業,只是把建立索引的資料檔案準備好,還沒有正式開始

  1. 執行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. 附

  1. 在建立索引的過程中,發現了一個可能是版本bug的地方,已提官網issue,連結如下

官網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 
  1. 查詢執行計劃,判斷是否命中索引表
內容 含義
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
  1. 如何命中索引
  • select * 不會命中索引
  • select 具體的欄位 from table where col ... // col 是第一個主鍵或者是索引裡包含的欄位

如果 index 包含 a、b 三個欄位,where 裡有 a 和 c 兩個欄位,那麼也不會走索引,因為c不在索引裡,發現索引走不通,只能走全表

如果 index 包含 a、b 三個欄位,where 裡有 a 欄位,select 裡有 a, c 欄位,會走索引