date: 2020-11-07 15:34:00
updated: 2020-11-13 17:17: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. 附
- 查詢執行計划,判斷是否命中索引表
內容 | 含義 |
---|---|
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 |
- 在創建索引的過程中,發現了一個可能是版本bug的地方,已提官網issue,鏈接如下
問題:如果在創建本地索引時,有一個字段設置了default value,在生成的索引表里就只會顯示默認值,不管是什么類型;如果這個類型是tinyint的話,還可能會造成之后主鍵的數據,原表的數據是對的,但是索引表是錯的,如果命中了索引表,那么就返回的是錯誤的數據。