AnalyticDB PostgreSQL版通過sr_plan外掛程式提供了查詢計劃管理(SQL Plan Management)功能,本文為您介紹如何使用查詢計劃管理功能。
注意事項
僅核心版本為V6.3.9.0及以上的AnalyticDB PostgreSQL版儲存彈性模式執行個體支援查詢計劃管理功能。
功能介紹
AnalyticDB PostgreSQL版最佳化器基於統計資料而非固定的規則,系統會評估SQL語句各種可行的執行運算元的代價並選擇代價最低的組合執行。雖然最佳化器會儘可能選擇最優的執行計畫,但是在複雜的業務情境中,急劇變化的資料量與資料分布特徵也會導致執行計畫的變動過大,同時,最佳化器版本的升級與特性的增強也會導致計劃變化,偶爾會產生不符合預期的執行計畫。在分析情境中,偶爾出現周期性相對複雜的報表查詢,最佳化器需要在較大的查詢計劃空間中檢索出相對最佳的計劃,檢索耗時較長。
查詢計劃管理功能可以儲存指定查詢語句和查詢計劃,當出現同類查詢語句時,可以直接調用儲存的查詢計劃。查詢計劃管理功能具有如下優點:
減少重複產生查詢計劃的時間。
穩定查詢計劃,避免在資料量大且資料變更頻繁的情境下查詢計劃變動過大。
支援參數化查詢語句中的常數,一個查詢計劃可以適用於所有常數不同的同類查詢語句。
管理外掛程式
安裝外掛程式
使用查詢計劃管理功能,您需要首先安裝sr_plan外掛程式,語句如下:
CREATE EXTENSION sr_plan;僅支援在安裝外掛程式的庫中使用該功能。
臨時關閉
如果需要在當前會話中臨時關閉查詢計劃管理功能,可以通過如下語句關閉:
SET sr_plan.enabled to off;卸載外掛程式
如果不需要使用查詢計劃管理功能,可以通過如下語句卸載外掛程式:
DROP EXTENSION sr_plan;sr_plans表
安裝sr_plan外掛程式後,系統會產生一張sr_plans表用於儲存計劃,每個計劃對應一條同類查詢語句和查詢計劃。表結構如下:
列名 | 類型 | 說明 |
query_hash | bigint | 查詢語句參數化後的64位Hash值,用於標識同類查詢語句。 |
query_id | int8 | 預留值。 |
plan_hash | bigint | 查詢計劃序列化後的64位Hash值,用於標識查詢計劃。 |
enable | bool | 查詢計劃是否生效,取值如下:
|
query | varchar | 註冊時的查詢語句。 |
plan | bytea | 查詢計劃序列化後的二進位序列,可以使用show_plan或show_plan_node函數展示詳細內容。函數的使用方法,請參見易用性函數使用說明。 |
const_list | bytea | 參數化列表序列化後的二進位序列,可以使用show_const_list函數展示詳細內容。函數的使用方法,請參見易用性函數使用說明。 |
reloids | oid[] | 查詢計劃相關的表的OID列表。 說明 當查詢計劃相關的表被刪除時,該計劃也會被刪除。 |
index_reloids | oid[] | 查詢計劃相關的索引的OID列表。 說明 當查詢計劃相關的索引被刪除時,該計劃也會被刪除。 |
GUC參數
AnalyticDB PostgreSQL版提供了如下GUC參數控制查詢計劃管理功能:
參數名稱 | 預設值 | 說明 |
sr_plan.enabled | on | 是否開啟查詢計劃管理功能,取值如下:
|
sr_plan.log_usage | none | sr_plan詳細使用資訊的日誌等級,取值如下: warning、notice、info、log、debug[1-5]、none。 none表示不記錄。 |
sr_plan.write_mode | off | 是否進入儲存計劃的模式,取值如下:
說明 該參數不建議手動修改,如需儲存計劃請使用註冊計劃的函數,具體操作,請參見易用性函數使用說明。 |
易用性函數使用說明
註冊計劃
bool sr_plans_register(<query>, <const_list>, <hint_str>)參數說明如下:
參數 | 類型 | 說明 |
query | text | 查詢語句,支援如下兩種模式:
具體匹配請以query_hash參數為準,建議開啟 |
const_list | text | 填充execute時的參數列表。 僅query為prepare模式時,需要傳入const_list參數。預設為NULL。 例如: |
hint_str | text | 需要額外添加的Hint字串。Hint使用資訊,請參見使用Hint。 預設為 |
如果發現登入的查詢語句未使用登入的執行計畫,可能是因為資料類型的強一致匹配問題。您可以使用EXPLAIN擷取常數在語句中的實際資料類型,通過顯式類型轉換進行註冊。樣本如下:
_p模式:
rname = 'ASIA'顯式轉換為rname = _p('ASIA'::bpchar)。prepare模式:
a = 1顯式轉換為a = $1::int。
樣本
使用_p模式註冊計劃,樣本如下:
SELECT sr_plans_register('SELECT * FROM test_table, test_table2 WHERE test_table.test_attr1 = test_table2.test_attr1 and test_table.test_attr2 = _p(11);', NULL, '/*+ Set(optimizer_enable_hashjoin off) */');使用prepare模式註冊計劃,樣本如下:
SELECT sr_plans_register('SELECT * FROM test_table, test_table2 WHERE test_table.test_attr1 = test_table2.test_attr1 and test_table.test_attr2 = $1;', '11', '/*+ MergeJoin(test_table test_table2) Leading((test_table test_table2)) */');
如果sr_plans_register函數參數報錯導致內部prepare對象沒有釋放,可以使用clean_sr_plans_register_prepare()函數進行清理。
查看計劃
查看文本計劃
show_plan(<query_hash>,<plan_hash>,<format>)參數
類型
說明
query_hash
bigint
查詢語句參數化後的64位Hash值,對應sr_plans表中的query_hash值。
plan_hash
bigint
查詢計劃參數化後的64位Hash值,對應sr_plans表中的plan_hash值。
預設為NULL,為NULL時表示第一個enable狀態的記錄。
format
text
展示格式,預設為
'text'。取值如下:'text'
'xml'
'json'
'yaml'
樣本
查看指定文本計劃,返回結果的展示格式為
'text',樣本如下:SELECT show_plan(-7846983602634689470, 1283098916874729409, 'text');查看完整計劃結構
show_plan_node(<query_hash>,<plan_hash>,<pretty>)參數
類型
說明
query_hash
bigint
查詢語句參數化後的64位Hash值,對應sr_plans表中的query_hash值。
plan_hash
bigint
查詢計劃參數化後的64位Hash值,對應sr_plans表中的plan_hash值。
預設為NULL,為NULL時表示第一個enable狀態的記錄。
pretty
bool
是否使用換行和縮排的格式展示結果。取值如下:
true(預設):使用換行和縮排的展示格式。
false:不使用換行和縮排的展示格式。
樣本
查看完整的計劃結構,返回結果自動換行和縮排,樣本如下:
SELECT show_plan_node(-7846983602634689470, 1283098916874729409, on);查看參數化列表
show_const_list(<query_hash>,<plan_hash>,<is_list>,<pretty>)參數
類型
說明
query_hash
bigint
查詢語句參數化後的64位Hash值,對應sr_plans表中的query_hash值。
plan_hash
bigint
查詢計劃參數化後的64位Hash值,對應sr_plans表中的plan_hash值。
預設為NULL,為NULL時表示第一個enable狀態的記錄。
is_list
bool
是否展示完整的運算元結構,取值如下:
true(預設):展示完整的運算元列表結構。
false:展示簡潔的字串列表。
pretty
bool
是否使用換行和縮排的格式展示結果。取值如下:
true(預設):使用換行和縮排的展示格式。
false:不使用換行和縮排的展示格式。
樣本
查看參數化列表,返回結果展示完整的運算元列表結構,不使用換行和縮排的展示效果,樣本如下:
SELECT show_const_list(-7846983602634689470, 1283098916874729409, true, false);
啟用計劃
enable_sr_plans(<query_hash>,<plan_hash>)參數 | 類型 | 說明 |
query_hash | bigint | 查詢語句參數化後的64位Hash值,對應sr_plans表中的query_hash值。 |
plan_hash | bigint | 查詢計劃參數化後的64位Hash值,對應sr_plans表中的plan_hash值。 預設為NULL,為NULL時表示所有對應query_hash的plan_hash的記錄。 |
樣本
啟用指定的計劃,樣本如下:
SELECT enable_sr_plans(-7846983602634689470, 1283098916874729409);停止計劃
disable_sr_plans(<query_hash>,<plan_hash>)參數 | 類型 | 說明 |
query_hash | bigint | 查詢語句參數化後的64位Hash值,對應sr_plans表中的query_hash值。 |
plan_hash | bigint | 查詢計劃參數化後的64位Hash值,對應sr_plans表中的plan_hash值。 預設為NULL,為NULL時表示所有對應query_hash的plan_hash的記錄。 |
樣本
停止指定的計劃,樣本如下:
SELECT disable_sr_plans(-7846983602634689470, 1283098916874729409);刪除計劃
delete_sr_plans(<query_hash>,<plan_hash>)參數 | 類型 | 說明 |
query_hash | bigint | 查詢語句參數化後的64位Hash值,對應sr_plans表中的query_hash值。 |
plan_hash | bigint | 查詢計劃參數化後的64位Hash值,對應sr_plans表中的plan_hash值。 預設為NULL,為NULL時表示所有對應query_hash的plan_hash的記錄。 |
樣本
刪除指定的計劃,樣本如下:
SELECT delete_sr_plans(-7846983602634689470, 1283098916874729409);修改計劃
update_sr_plans(<query_hash>,<plan_node_string>,<plan_hash>)該函數處理過程中僅校正字串是否滿足讀取並轉化為對應的可執行計畫的要求,不校正對應計劃是否完全滿足該查詢的要求。建議您在對Plan的資料結構有較高的理解的情況下使用。
參數 | 類型 | 說明 |
query_hash | bigint | 查詢語句參數化後的64位Hash值,對應sr_plans表中的query_hash值。 |
plan_node_string | text | 修改後的plan node的字串。可以使用show_plan_node函數可擷取目前狀態。 |
plan_hash | bigint | 查詢計劃參數化後的64位Hash值,對應sr_plans表中的plan_hash值。 預設為NULL,為NULL時表示所有對應query_hash的plan_hash的記錄。 |
使用樣本
準備測試用表test_table和test_table2,並插入測試資料。
DROP TABLE IF EXISTS test_table; DROP TABLE IF EXISTS test_table2; CREATE TABLE test_table(test_attr1 int, test_attr2 int); CREATE TABLE test_table2(test_attr1 int, test_attr2 int); INSERT INTO test_table SELECT i, i + 1 FROM generate_series(1, 20000) i; INSERT INTO test_table2 SELECT i, i + 1 FROM generate_series(1, 20000) i; CREATE INDEX test_table_index1 ON test_table (test_attr1); CREATE INDEX test_table_index2 ON test_table (test_attr2); ANALYZE test_table; ANALYZE test_table2;開啟sr_plan日誌顯示,用於查看註冊和使用時的提示。
建議您在註冊時啟用,便於觀測是否可以使用儲存的計劃。
SET sr_plan.log_usage = NOTICE;查看原生查詢計劃。
執行查詢計劃:
EXPLAIN SELECT * FROM test_table, test_table2 WHERE test_table.test_attr1 = test_table2.test_attr1 and test_table.test_attr2 = 11;返回資訊如下:
QUERY PLAN ------------------------------------------------------------------------------------------------------ Gather Motion 3:1 (slice1; segments: 3) (cost=0.00..438.43 rows=1 width=16) -> Hash Join (cost=0.00..438.43 rows=1 width=16) Hash Cond: (test_table2.test_attr1 = test_table.test_attr1) -> Seq Scan on test_table2 (cost=0.00..431.14 rows=6667 width=8) -> Hash (cost=6.00..6.00 rows=1 width=8) -> Index Scan using test_table_index2 on test_table (cost=0.00..6.00 rows=1 width=8) Index Cond: (test_attr2 = 11) Optimizer: Pivotal Optimizer (GPORCA) version 3.86.0 (8 rows)註冊查詢計劃。
使用_p模式,帶Hint:
SELECT sr_plans_register('SELECT * FROM test_table, test_table2 WHERE test_table.test_attr1 = test_table2.test_attr1 and test_table.test_attr2 = _p(11);', NULL, '/*+ Set(optimizer_enable_hashjoin off) */');返回資訊如下:
NOTICE: sr_plan: saved plan for /*+ Set(optimizer_enable_hashjoin off) */SELECT * FROM test_table, test_table2 WHERE test_table.test_attr1 = test_table2.test_attr1 and test_table.test_attr2 = _p(11); CONTEXT: SQL statement "/*+ Set(optimizer_enable_hashjoin off) */SELECT * FROM test_table, test_table2 WHERE test_table.test_attr1 = test_table2.test_attr1 and test_table.test_attr2 = _p(11);" PL/pgSQL function sr_plans_register(text,text,text) line 17 at EXECUTE statement corresponding column is : sr_plans_register sr_plans_register ------------------- t (1 row)使用prepare模式,無法使用ORCA最佳化器,帶Hint:
SELECT sr_plans_register('SELECT * FROM test_table, test_table2 WHERE test_table.test_attr1 = test_table2.test_attr1 and test_table.test_attr2 = $1;', '11', '/*+ MergeJoin(test_table test_table2) Leading((test_table test_table2)) */');返回資訊如下:
NOTICE: sr_plan: saved plan for select * from test_table, test_table2 where test_table.test_attr1 = test_table2.test_attr1 and test_table.test_attr2 = $1; CONTEXT: SQL statement "/*+ MergeJoin(test_table test_table2) Leading((test_table test_table2)) */execute _sr_plans_register(11)" PL/pgSQL function sr_plans_register(text,text,text) line 27 at EXECUTE statement corresponding column is : sr_plans_register sr_plans_register ------------------- t (1 row)
查詢所有記錄的計劃。
SELECT * FROM sr_plans;為了更好地展示效果,您可以在psql中執行
\x命令來橫向展示結果,返回資訊如下:-[ RECORD 1 ]-+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- query_hash | -7846983602634689470 query_id | 0 plan_hash | 1283098916874729409 enable | f query | /*+ Set(optimizer_enable_hashjoin off) */SELECT * FROM test_table, test_table2 WHERE test_table.test_attr1 = test_table2.test_attr1 and test_table.test_attr2 = _p(11); plan | \x28b52ffd603f07150d00520b2a31504f0200745b6bf119296929280e2ae3d08e676124f9ca5fc36c629033c7b9a7a9bf766e213a3e4266ce009604c2ffbef32823871cd54a100b835171815211be6dfaa6d4ad563800a7f48bdc2a744c5dc8a4feeba7284366093db00c71a02190f870675a5b137a269a8d96868e83c5f4b733c6b21ecfc0752250e0e15cab5a4b2b33395506c20b063530f08f32c618ff808f4a133af1bbcca857f2dee7c220a5c311d1ecef4d928080a8b0d64000082000f0829199e1361200c234ca2508e40831901063483011c944125018292ceda0be8ff48b9cc45e2e1df50755b915267643696e998066ca9cdbdaae59e2e74e6a40f2efd406378e2636c854b8508978e1ecf58b356ea8bdab1b208ed06a4c2dea383dc365f24e0ed8e4a78ac542b50cf462aecf05650c56bab6f4266ab87c29354a59b2509a3fed448f897e4ccdf0623e9f293f6dbff7773e405f0884b67d8bc8c18f245c4b8a82297074668c19e8b0158d9ecdd3986fa2adddc4ed0cbaad8a1f54de36785a99cc39475c06e36f415872b3801e3a45a10aa91eded2dcddd3cf88abbd9824b0259f271a8692919907 const_list | \x28b52ffd2049ad0100540290020100000035010f80000017003001ffffffff040000000100a30000000ba0000000adde05004f9107417606fc3904325002 reloids | {40963,40960,40960,40963} index_reloids | {40969} -[ RECORD 2 ]-+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- query_hash | -7846983602634689470 query_id | 0 plan_hash | 8380868479165711144 enable | f query | select * from test_table, test_table2 where test_table.test_attr1 = test_table2.test_attr1 and test_table.test_attr2 = $1; plan | \x28b52ffd604a09c51000024f383730918200d5a047fa8799c1e341cc05c54480f746007c5f533b0d22256706d75b93bfed805f83bc0a6319ff43a73f4fbf55a3ed96bef73d35272fde92a40102d5171724a5427da08a5ebe361594ce16b5abf26cb13b631f23b0f98a19b4546c293fc0ac6fddf58b68f773dce8e8f337fc13aa2072a113828e4bc8a9041323b3da5577c38012ce0451cc700c172513f439a0146cd8cc7a5b69fd10a1473da9ad4c4a59c752bdd7344d76524ad2ee8add0a043f0fc448813f171ac771fc0f76663a52895fdbf033452e93800649c68479dc2c3c98bef9caa189784930d1c3755902809fa8d1d522400109822001f00281111a2b1200932cc9640c42c000048c314a0015000413418080090c680cc6838e2fd492d1b489b1738684cc0313c3b91659002bb5237c70b22b863bcc54a973f15fe134370df65c634396bf878f89b3658422ac27a9bb02c7d8b83cd81d1f074acaf5a107f14aa5a1d6c9d5c03b4aa7b08ad04359d3fe16bd509d41c12e0c028c55c9a0390794423566ac01d18c7b021016bf8ba100e83144bf84b0e4ad418a313c57df176b143aab601c43cd46c9516f08c7ea00e3d040d35e7d8b50d52462dfd3b03662d2674a78c9e9cc825594d3fbed29f0acdd1ce9caabbeae8a34c54a23879edea532081ef80aac28e26c1e26e6d99915549bcbb8a7d82cf8c2fef988a1c623472cff31c25dc69a69a130bdac655748e0a6ac3abe97d95b431fe104caffe6c083f007 const_list | \x28b52ffd2020010100900201000000310100000100000017000000ffffffff0000000095000000adde reloids | {40960,40963} index_reloids | {40970}查看計劃。
通過query_hash和plan_hash欄位查看第一個計劃:
SELECT show_plan(-7846983602634689470, 1283098916874729409);返回資訊如下:
show_plan ------------------------------------------------------------------------------------------------------------------ ("Gather Motion 3:1 (slice1; segments: 3)") (" Output: test_table.test_attr1, test_table.test_attr2, test_table2.test_attr1, test_table2.test_attr2") (" -> Nested Loop") (" Output: test_table.test_attr1, test_table.test_attr2, test_table2.test_attr1, test_table2.test_attr2") (" Join Filter: true") (" -> Seq Scan on public.test_table2") (" Output: test_table2.test_attr1, test_table2.test_attr2") (" -> Index Scan using test_table_index1 on public.test_table") (" Output: test_table.test_attr1, test_table.test_attr2") (" Index Cond: (test_table.test_attr1 = test_table2.test_attr1)") (" Filter: (test_table.test_attr2 = _p(11))") ("Optimizer: Pivotal Optimizer (GPORCA) version 3.86.0") ("Settings: rds_plan_cache_mode=auto") (13 rows)通過query_hash和plan_hash欄位查看第一個計劃的參數列表:
SELECT show_const_list(-7846983602634689470, 1283098916874729409, false);返回資訊如下:
show_const_list ---------------------------- _p(4 [ 11 0 0 0 0 0 0 0 ]) (1 row)通過query_hash和plan_hash欄位查看第二個計劃:
SELECT show_plan(-7846983602634689470, 8380868479165711144);返回資訊如下:
show_plan ------------------------------------------------------------------------------------------------------------------ ("Gather Motion 3:1 (slice1; segments: 3)") (" Output: test_table.test_attr1, test_table.test_attr2, test_table2.test_attr1, test_table2.test_attr2") (" -> Merge Join") (" Output: test_table.test_attr1, test_table.test_attr2, test_table2.test_attr1, test_table2.test_attr2") (" Merge Cond: (test_table.test_attr1 = test_table2.test_attr1)") (" -> Sort") (" Output: test_table.test_attr1, test_table.test_attr2") (" Sort Key: test_table.test_attr1") (" -> Index Scan using test_table_index2 on public.test_table") (" Output: test_table.test_attr1, test_table.test_attr2") (" Index Cond: (test_table.test_attr2 = $1)") (" -> Sort") (" Output: test_table2.test_attr1, test_table2.test_attr2") (" Sort Key: test_table2.test_attr1") (" -> Seq Scan on public.test_table2") (" Output: test_table2.test_attr1, test_table2.test_attr2") ("Optimizer: Postgres query optimizer") ("Settings: rds_plan_cache_mode=auto") (18 rows)通過query_hash和plan_hash欄位查看第二個計劃的參數列表:
SELECT show_const_list(-7846983602634689470, 8380868479165711144, false);返回資訊如下:
show_const_list ----------------- $1 (1 row)
使用查詢計劃管理功能。
驗證第一個計劃
啟用第一個計劃:
SELECT enable_sr_plans(-7846983602634689470, 1283098916874729409);在啟用第一個計劃的情況下執行查詢計劃,參數化常數可變:
EXPLAIN SELECT * FROM test_table, test_table2 WHERE test_table.test_attr1 = test_table2.test_attr1 and test_table.test_attr2 = 12;返回資訊如下:
NOTICE: sr_plan: cached plan was used for query: explain SELECT * FROM test_table, test_table2 WHERE test_table.test_attr1 = test_table2.test_attr1 and test_table.test_attr2 = 12; QUERY PLAN ----------------------------------------------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) (cost=0.00..120434.24 rows=8000 width=16) -> Nested Loop (cost=0.00..120433.77 rows=2667 width=16) Join Filter: true -> Seq Scan on test_table2 (cost=0.00..431.14 rows=6667 width=8) -> Index Scan using test_table_index1 on test_table (cost=0.00..120002.35 rows=1 width=8) Index Cond: (test_attr1 = test_table2.test_attr1) Filter: (test_attr2 = 12) Optimizer: Pivotal Optimizer (GPORCA) version 3.86.0 (8 rows)停止第一個計劃:
SELECT disable_sr_plans(-7846983602634689470, 1283098916874729409);
驗證第二個計劃
啟用第二個計劃:
SELECT enable_sr_plans(-7846983602634689470, 8380868479165711144);在啟用第二個計劃的情況下執行查詢計劃,參數化常數可變:
EXPLAIN SELECT * FROM test_table, test_table2 WHERE test_table.test_attr1 = test_table2.test_attr1 and test_table.test_attr2 = 13;返回資訊如下:
NOTICE: sr_plan: cached plan was used for query: explain SELECT * FROM test_table, test_table2 WHERE test_table.test_attr1 = test_table2.test_attr1 and test_table.test_attr2 = 13; QUERY PLAN ------------------------------------------------------------------------------------------------------ Gather Motion 3:1 (slice1; segments: 3) (cost=1660.96..1760.98 rows=15 width=16) -> Merge Join (cost=1660.96..1760.98 rows=5 width=16) Merge Cond: (test_table.test_attr1 = test_table2.test_attr1) -> Sort (cost=8.19..8.20 rows=1 width=8) Sort Key: test_table.test_attr1 -> Index Scan using test_table_index2 on test_table (cost=0.16..8.18 rows=1 width=8) Index Cond: (test_attr2 = 13) -> Sort (cost=1652.77..1702.77 rows=6667 width=8) Sort Key: test_table2.test_attr1 -> Seq Scan on test_table2 (cost=0.00..224.00 rows=6667 width=8) Optimizer: Postgres query optimizer (11 rows)停止第二個計劃:
SELECT disable_sr_plans(-7846983602634689470, 8380868479165711144);
刪除計劃。
刪除第一個計劃:
SELECT delete_sr_plans(-7846983602634689470, 1283098916874729409);刪除第二個計劃:
SELECT delete_sr_plans(-7846983602634689470, 8380868479165711144);