全部產品
Search
文件中心

PolarDB:ePQ Hint Injection

更新時間:Jul 06, 2024

本文介紹了ePQ Hint Injection功能的簡介、使用方法以及樣本等內容。

前提條件

ePQ Hint Injection依賴pg_hint_plan 外掛程式,您需要修改shared_preload_libraries參數,將pg_hint_plan添加到shared_pre_load_libraries 參數值中。修改參數具體操作,請參見設定叢集參數

簡介

ePQ Hint Injection功能適用於跨機並行查詢(ePQ),資料庫營運時,在無法直接給SQL增加PX Hint的情況下,可以通過ePQ Hint Injection功能對指定SQL注入PX Hint 。在不改造SQL的情況下,可以讓指定的SQL運行或者不運行ePQ。

使用指南

建立外掛程式並設定開關

說明

推薦根據DB/角色來設定開關。例如,當前的DB為postgres。

create extension polar_sql_mapping;
create extension pg_hint_plan;
alter database postgres set pg_hint_plan.enable_hint TO on;

SQL改寫功能

通過以下SQL樣本說明改寫功能。

explain select count(*) from t1;
select count(*) from t1;

準備資料

create table t1(c1 int,c2 int);
insert into t1 select generate_series(1,1000), generate_series(1,1000);
-- 將 t1 表的 px_workers 開啟
alter table t1 set(px_workers=1000);

將SQL寫入mapping表

-- 建立外掛程式
create extension polar_sql_mapping;
create extension pg_hint_plan;
alter database postgres set pg_hint_plan.enable_hint TO on;
-- 將 SQL 寫入 px mapping 表
select polar_sql_mapping.insert_px_mapping('explain select count(*) from t1;');
select polar_sql_mapping.insert_px_mapping('select count(*) from t1;');
-- 將 SQL 寫入 nonpx mapping 表
select polar_sql_mapping.insert_nonpx_mapping('explain select count(*) from t1;');
select polar_sql_mapping.insert_nonpx_mapping('select count(*) from t1;');
說明

其中:

  • polar_sql_mapping.insert_px_mapping:表示寫入SQL至polar_sql_mapping.polar_px_mapping_table,該表在polar_enable_px關閉時使用,目的是將在polar_px_mapping_table表中匹配到的SQL加上/*+PX()*/。例如,select count(*) from t1;改寫為 /*+PX()*/ select count(*) from t1;。簡單來說,是為了在ePQ功能關閉的情況下,讓某些長查詢能夠進行ePQ查詢。

  • polar_sql_mapping.insert_nonpx_mapping:表示寫入SQL至polar_sql_mapping.polar_nonpx_mapping_table,在polar_enable_px開啟時使用,目的是將在polar_nonpx_mapping_table表中匹配到的SQL加上/*+NoPX()*/。例如,select count(*) from t1改寫為/*+NoPX()*/ select count(*) from t1;。簡單來說,是為了在ePQ功能開啟的情況下,將某些短查詢剔出ePQ查詢。

開啟改寫功能

推薦根據資料庫來設定參數,假設您當前在使用的資料庫名為postgres

-- 開啟改寫功能
-- 注意,alter postgres 只對新會話生效,需要重啟串連, postgres 可替換為你當前的資料庫名
alter database postgres set polar_sql_mapping.use_px_sql_mapping=true;

-- 開啟 Notice,方便看提示資訊
SET pg_hint_plan.debug_print TO on;
SET pg_hint_plan.message_level TO notice;
SET polar_sql_mapping.log_usage = notice;
SET client_min_messages TO notice;

-- 進行改寫
explain select count(*) from t1;

得到如下的輸出,說明改寫成功:

postgres=# explain select count(*) from t1;
NOTICE:  sql mapping exist. The id = 1
NOTICE:  px sql mapping: change sql to '/*+PX()*/explain select count(*) from t1;'.
NOTICE:  pg_hint_plan:
used hint:
PX()
not used hint:
duplication hint:
error hint:

                                     QUERY PLAN
-------------------------------------------------------------------------------------
 Finalize Aggregate  (cost=0.00..431.00 rows=1 width=8)
   ->  PX Coordinator 6:1  (slice1; segments: 6)  (cost=0.00..431.00 rows=1 width=8)
         ->  Partial Aggregate  (cost=0.00..431.00 rows=1 width=8)
               ->  Partial Seq Scan on t1  (cost=0.00..431.00 rows=167 width=1)
 Optimizer: PolarDB PX Optimizer
(5 rows)

postgres=# select count(*) from t1;
NOTICE:  sql mapping exist. The id = 2
NOTICE:  px sql mapping: change sql to '/*+PX()*/select count(*) from t1;'.
NOTICE:  pg_hint_plan:
used hint:
PX()
not used hint:
duplication hint:
error hint:

 count
-------
  1000
(1 row)

polar_enable_px開啟的情況下,則會被改寫為不帶PX的查詢:

set polar_enable_px=1;
explain select count(*) from t1;

顯示結果如下:

postgres=# set polar_enable_px=1;
SET
postgres=# select count(*) from t1;
NOTICE:  sql mapping exist. The id = 2
NOTICE:  px sql mapping: change sql to '/*+NoPX()*/select count(*) from t1;'.
NOTICE:  pg_hint_plan:
used hint:
NoPX(0)
not used hint:
duplication hint:
error hint:

 count
-------
  1000
(1 row)

postgres=# explain select count(*) from t1;
NOTICE:  sql mapping exist. The id = 1
NOTICE:  px sql mapping: change sql to '/*+NoPX()*/explain select count(*) from t1;'.
NOTICE:  pg_hint_plan:
used hint:
NoPX(0)
not used hint:
duplication hint:
error hint:

                         QUERY PLAN
------------------------------------------------------------
 Aggregate  (cost=17.50..17.51 rows=1 width=8)
   ->  Seq Scan on t1  (cost=0.00..15.00 rows=1000 width=0)
(2 rows)

開啟parse_tree匹配

ePQ Hint Injection預設採用字串匹配,需要空格、換行等精確匹配。ePQ Hint Injection目前提供parse_tree匹配模式,可以忽略空格、換行的影響,但是會增加匹配時間,約為字串匹配的兩倍開啟檔案如下,參數為 polar_sql_mapping.use_px_parse_match

-- 多了空格,無法匹配
explain select count(*) from t1;
-- 開啟 parse_tree 匹配
alter database postgres set polar_sql_mapping.use_px_parse_match =true;
-- 重啟會話
-- 可以匹配
explain select count(*) from t1;

顯示結果如下:

postgres=# explain select count(*) from t1;
                         QUERY PLAN
------------------------------------------------------------
 Aggregate  (cost=17.50..17.51 rows=1 width=8)
   ->  Seq Scan on t1  (cost=0.00..15.00 rows=1000 width=0)
(2 rows)

postgres=# alter database postgres set polar_sql_mapping.use_px_parse_match=true;
ALTER DATABASE

postgres=# explain select count(*) from t1;
NOTICE:  Parse tree match hit. Sql mapping exist. The id = 1
NOTICE:  px sql mapping: change sql to '/*+PX()*/explain select count(*) from t1;'.
NOTICE:  pg_hint_plan:
used hint:
PX()
not used hint:
duplication hint:
error hint:

                                     QUERY PLAN
-------------------------------------------------------------------------------------
 Finalize Aggregate  (cost=0.00..431.00 rows=1 width=8)
   ->  PX Coordinator 6:1  (slice1; segments: 6)  (cost=0.00..431.00 rows=1 width=8)
         ->  Partial Aggregate  (cost=0.00..431.00 rows=1 width=8)
               ->  Partial Seq Scan on t1  (cost=0.00..431.00 rows=167 width=1)
 Optimizer: PolarDB PX Optimizer
(5 rows)
說明

進行匹配的SQL和插入select polar_sql_mapping.insert_px_mapping()的SQL必須滿足以下三個條件:

  • 含有select的DQL,也可以是explain

  • 不含有/*+PX()*//*+NoPX()*/的hint。

  • 文法正確。

不滿足以上三個條件的SQL會觸發報錯。

無法匹配的情況

如果出現無法匹配的情況,可以開啟以下參數,通過Notice日誌,查看是否正確匹配。

-- 開啟 Notice,方便看提示資訊
SET pg_hint_plan.debug_print TO on;
SET pg_hint_plan.message_level TO notice;
SET polar_sql_mapping.log_usage = notice;
SET client_min_messages TO notice;

extend協議和PreparedStmt的SQL

對於大部分的應用程式都是使用PreparedStmt,在寫新的SQL時,需要注意預留位置應該是$n而不是'?',樣本如下:image..png

SQL中含有引號的情況

如果SQL中含有引號的情況,需要增加一個引號進行匹配。

樣本如下:

select instr(varchar_test,'arh') from test_datatype_list where varchar_test is not null order by 1 ;

則需要修改為以下SQL:

select polar_sql_mapping.insert_px_mapping('select instr(varchar_test,''arh'') from test_datatype_list where varchar_test is not null order by 1 ');

查看參與mapping的SQL

-- 查看執行 ePQ 查詢的映射表內容
postgres=# select * from polar_sql_mapping.polar_px_mapping_table;
 id |                   source_sql                   |                       target_sql

----+------------------------------------------------+---------------------------------
------------------------
  1 | select * from polar_sql_mapping.px_query_info; | /*+PX()*/select * from polar_sql
_mapping.px_query_info;
  2 | select count(*) from t1;                       | /*+PX()*/select count(*) from t1
;
(2 rows)

-- 查看不執行 ePQ 查詢的映射表內容
postgres=# select * from polar_sql_mapping.polar_nonpx_mapping_table;
 id | source_sql | target_sql
----+------------+------------
(0 rows)

慢SQL採集功能

慢SQL採集是一個協助工具功能,會將執行時間>=閾值或者<=閾值的最新N條SQL(N表示px_max_num) 採集到hash table當中,通過 select polar_sql_mapping.insert_px_mapping_id(id);即可將採集的SQL添加到mapping表當中,無需輸入整條SQL。參數如下:

參數

說明

polar_sql_mapping.px_record_query

表示是否開啟採集功能,取值如下:

  • true:開啟採集功能。

  • false(預設):不開啟採集功能。

polar_sql_mapping.px_record_upper_or_lower

表示需要採集>=閾值的SQL,還是採集<=閾值的SQL,取值如下:

  • true(預設):採集>=閾值的SQL。

  • false:採集<=閾值的SQL。

polar_sql_mapping.px_record_duration_time

表示記錄時間的閾值,預設為10000ms,單位為ms。

polar_sql_mapping.px_max_num

表示能採集的最大SQL,預設為20。

說明

修改該參數需要重啟資料庫。

polar_sql_mapping.px_record_explain

表示是否採集explain語句。

  • true:採集explain語句。

  • false(預設):不採集explain語句。

polar_sql_mapping.px_evict_entry_num

表示一次剔除多少個entry,預設為5。

查詢採集表

外掛程式內建函數可以查詢完整的採集表,px_query_info是一個視圖,視圖通過函數px_psm_query_info_internal() 來進行遍曆雜湊表。

select * from polar_sql_mapping.px_query_info;

清空採集表

外掛程式目前只提供清空採集表的功能,通過如下函數來進行觸發。

select polar_sql_mapping.px_query_info_clear();

樣本

-- 根據 DB 設定參數
alter database postgres set polar_sql_mapping.px_record_query=1;
alter database postgres set polar_sql_mapping.px_record_upper_or_lower=1;
-- 大於 3s 的 SQL 會被採集
alter database postgres set polar_sql_mapping.px_record_duration_time=3000;

-- 執行 >3 的 SQL
select sum(c1) from t1 where c1=(select count(pg_sleep(3)));

查詢 polar_sql_mapping.px_query_info,得到如下結果,說明已採集到。

select * from polar_sql_mapping.px_query_info;
 id |                            query                             | execution_time | calls
----+--------------------------------------------------------------+----------------+-------
  1 | select sum(c1) from t1 where c1=(select count(pg_sleep(3))); |           3004 |     1
(1 row)

將SQL添加到mapping表

-- 添加到 polar_px_mapping_table 中
select polar_sql_mapping.insert_px_mapping_id(1);
-- 添加到 polar_nonpx_mapping_table 中
select polar_sql_mapping.insert_nonpx_mapping_id(1);

將所有SQL添加到mapping表

-- 添加所有 query 到 polar_px_mapping_table 中
select polar_sql_mapping.insert_px_mapping_id(id) from polar_sql_mapping.px_query_info;
-- 添加所有 query 到 polar_nonpx_mapping_table 中
select polar_sql_mapping.insert_nonpx_mapping_id(id) from polar_sql_mapping.px_query_info;

進行改寫

結果如下所示:

postgres=# select sum(c1) from t1 where c1=(select count(pg_sleep(3)));
NOTICE:  Parse tree match hit. Sql mapping exist. The id = 6
NOTICE:  px sql mapping: change sql to '/*+PX()*/select sum(c1) from t1 where c1=(select count(pg_sleep(3)));'.
NOTICE:  pg_hint_plan:
used hint:
PX()
not used hint:
duplication hint:
error hint:

 sum
-----
   1
(1 row)

關閉採集功能

為了提升效能,不使用時,需要將採集功能關閉。

alter database postgres set polar_sql_mapping.px_record_query=0;