一、概要
在資料庫中,執行計劃就是表示一條SQL将要執行的步驟,這些步驟按照不同的資料庫運算符号(算子)組成,具體的組成和執行方式由資料庫中的查詢優化器來決定。換而言之,執行計劃決定了SQL的執行效率。在資料庫的使用中了解其查詢計劃的構成,是進行查詢性能調優的必要條件。本文将詳細介紹Phoenix的查詢計劃文法、組成結構,以及一些注意事項。
二、查詢計劃
1. 基本說明
在phoenix中,查詢計劃能告訴我們如下的資訊:
- 将要掃描的CHUNK數量
- 用戶端并發線程數量
- 執行模式(并行或串行)
- 查詢過濾字段或者掃描範圍
- 将會查詢的表名
- 估算掃描資料bytes大小(依賴stats資訊)
- 估算掃描資料量大小(依賴stats資訊)
- 估算數量bytes大小和資料量時間
- 操作符被執行在用戶端或者服務端
- 涉及的查詢operations(sort、filter, scan, merge, join, limit等)
2. 文法
explain [select... | upsert ... select | delete...]
explain文法示例如下:
explain SELECT host FROM PTSDB WHERE host IN ('a','b');
explain UPSERT INTO t1 SELECT id FROM t2 ORDER BY K1, V1;
3. 如何選擇最優查詢計劃
檢查查詢計劃是否最優,核心有以下幾點可以作為參考:
- 盡量避免出現FULL SCAN,尤其對于不走索引表的單表查詢,不應該出現FULL SCAN
- 執行模式盡可能使用并行(某些情況一定是串行的執行模式)
- 盡可能将對應表的過濾條件或計算下推到server端
- 盡可能使用覆寫索引,生成不需要回查資料表的查詢計劃
三、查詢計劃詳解
1. 操作符說明
- UNION ALL: 表示union all查詢,操作符後面接查詢計劃中涉及查詢的數量
- AGGREGATE INTO SINGLE ROW: 沒有groupby語句情況下,聚合查詢結果到一行中。例如 count(*)
- AGGREGATE INTO ORDERED DISTINCT ROWS:帶有group by的分組查詢
- FILTER BY expression: 過濾出符合表達式條件的資料
- INNER-JOIN: 多表Join
- MERGE SORT: 進行merge sort排序,大多是用戶端對多線程查詢結果進行排序
- RANGE SCAN: 對主鍵進行範圍掃描,通常有指定start key和stop key
- ROUND ROBIN: 對查詢沒有排序要求,并發的在用戶端發起掃描請求。
- SKIP SCAN: Phoenix實作的一種掃描方式,通常能比Range scan獲得更好的性能。
- FULL SCAN: 全表掃描
- LIMIT: 對查詢結果取TOP N
- CLIENT: 在用戶端執行相關操作
- X-CHUNK: 根據統計資訊可以把一個region分成多個CHUNK, X在查詢計劃中表示将要掃描的CHUNK數量,此處是多線程并發掃描的,并發的數量是由用戶端線程池的大小來決定的
- PARALLEL X-WAY:描述了有X個并發對scan做merge sort之類的用戶端操作
- SERIAL: 單線程串行執行
- SERVER: 在SERVER端(RS)執行相關操作
2. 查詢計劃示例說明
分組聚合查詢。查詢計劃中有5385個并發,并行對表做範圍掃描,在server端以組合rowkey的第二列k2為過濾條件過濾,并以k2列做聚合。
explain select count(k2) from OFFSET_TEST where k2 = '3343' group by k2;
CLIENT 5385-CHUNK 2330168 ROWS 314572800 BYTES PARALLEL 5385-WAY RANGE SCAN OVER OFFSET_TEST [0] - [63]
SERVER FILTER BY FIRST KEY ONLY AND K2 = '3343'
SERVER AGGREGATE INTO DISTINCT ROWS BY [K2]
CLIENT MERGE SORT
無排序查詢生成ROUND ROBIN查詢計劃。查詢計劃中有5385個并發,并行對表做ROUND ROBIN的範圍掃描,在server端以組合rowkey的第二列k2為過濾條件過濾。
explain select * from OFFSET_TEST where k2 = '3343';
CLIENT 5385-CHUNK 2330168 ROWS 314572800 BYTES PARALLEL 5385-WAY ROUND ROBIN RANGE SCAN OVER OFFSET_TEST [0] - [63]
SERVER FILTER BY K2 = '3343'
有排序查詢。查詢計劃中有5385個并發,并行對表做範圍掃描,在server端以組合rowkey的第二列k2為過濾條件過濾并排序,最後在用戶端進行merge sort查詢結果。
explain select * from OFFSET_TEST where k2 = '3343' order by k2;
CLIENT 5385-CHUNK 2330168 ROWS 314572800 BYTES PARALLEL 5385-WAY RANGE SCAN OVER OFFSET_TEST [0] - [63]
SERVER FILTER BY K2 = '3343'
SERVER SORTED BY [K2]
CLIENT MERGE SORT
四、API通路查詢計劃資訊
String explainSql = "EXPLAIN SELECT * FROM T";
Long estimatedBytes = null;
Long estimatedRows = null;
Long estimateInfoTs = null;
try (Statement statement = conn.createStatement(explainSql)) {
int paramIdx = 1;
ResultSet rs = statement.executeQuery(explainSql);
//列印查詢計劃
System.out.println(QueryUtil.getExplainPlan(rs));
//擷取相關估算值
rs.next();
estimatedBytes =
(Long) rs.getObject(PhoenixRuntime.EXPLAIN_PLAN_ESTIMATED_BYTES_READ_COLUMN);
estimatedRows =
(Long) rs.getObject(PhoenixRuntime.EXPLAIN_PLAN_ESTIMATED_ROWS_READ_COLUMN);
estimateInfoTs =
(Long) rs.getObject(PhoenixRuntime.EXPLAIN_PLAN_ESTIMATE_INFO_TS_COLUMN);
}
五、注意事項
- 當有兩個以上索引表時盡量使用hint去指定查詢必須要使用的索引表,這樣可以確定即使以後再加了索引不會影響到現在使用的查詢計劃
- 能通過資料表組合主鍵覆寫的查詢條件,盡量避免建立索引表。索引表表越多,寫放大越嚴重,維護成本也會随之增加
- 在查詢計劃中Scan速度,SKIP SCAN > RANGE SCAN > FULL SCAN
- 不是所有的查詢operations都能下推到server端
- 查詢SERVER FILTER一個普通列,一般會在server端發生全表掃描操作,也需要謹慎檢查
- 組合主鍵或者組合索引的非字首列,作為過濾條件列進行查詢時,一般會生成SCAN OVER的查詢計劃,但實際上這種查詢也很可能需要全表掃描,是以也需要根據實際情況檢查确認