更新时间:2025-08-22 GMT+08:00

plan management

DWS随着数据库参数设置的更改、版本的升级、数据的变化等,同一个语句的执行计划会存在差异,同时造成执行性能的劣化。plan management可通过为语句绑定指定的计划来防止计划跳变而产生的性能劣化。

plan management主要包括为计划生成唯一标识、为语句和计划生成sql_hash和plan_hash、计划保存和使用、计划失效等部分。

为计划生成唯一标识

如果要为语句指定一个固定的计划,那么每个语句及其计划必须拥有一个唯一的标识,即sql_hash和plan_hash。

可以通过开启GUC参数enable_generate_plan_hash为outline生成唯一标识plan_hash。

适用于生成outline的计划

除FQS和CN强量化计划外,其他计划都会生成outline。

outline保存和使用

下图是outline保存和使用在语句执行过程中处理流程。

图1 outline保存和使用流程

outline保存

outline保存,即将计划自动生成的outline记录到SQL_OUTLINE系统表中,以供后续将其绑定给SQL语句。

  • 如果同一个sql_hash的语句不存在该outline_name的outline,则保存该outline;若已经存在同名的outline,则不会重复保存。
  • outline是在语句正确执行完成后才进行保存的,如果执行出错,则不会保存。

可以通过设置GUC参数planmgmt_options来保存outline,例如:

  • 设置planmgmt_options = 'plan_save_mode_outline, plan_save_level_topsql',满足记录topsql的语句才将其outline进行保存。

  • 设置planmgmt_options = 'plan_save_mode_outline, plan_save_level_all',表示将outline进行保存。

plan management虽可以使语句执行性能稳定,但启用plan management也存在性能消耗,不适用于非资源消耗型的简单语句。因此建议设置planmgmt_options = 'plan_save_mode_outline, plan_save_level_topsql',可避免将简单语句的计划也进行保存。

outline使用

使用pgxc_bind_plan函数为语句绑定一个outline后,可设置planmgmt_options = 'plan_save_mode_outline, plan_save_level_topsql, enable_plan_baseline',使得执行语句时可以使用该绑定的outline。

当前通过plan management支持自动提取以下hint:支持join顺序的leading hint、join方式的hint、scan方式的hint、指定倾斜列的skew hint和指定分布方式的stream hint。

通过plan management绑定outline来固定计划的根本是通过outline中的hint来固定计划,由于outline中支持自动提取的hint类型有限,有以下约束:

  1. 如果sql中手工增删或修改了outline中不支持的hint,可能会出现绑定outline后,计划不能被固定:
    • no merge hint:sql1中指定了no merge hint,sql2和sql1的差别仅在于没有指定no merge hint,sql1和sql2的sql_hash是一样的,所以进行outline绑定就会同时给sql1和sql2都绑定,如果绑定的是sql1生成的outline,则sql2执行时绑定的outline就会不生效,同理反过来也是成立的。这是因为no merge hint限制了子查询的提升,语句块的层次不同,生成的outline中的hint应用的语句块在子查询提升和不提升时是不一样的,所以会导致绑定后无法找到hint中对应的表而无法生效。
    • GUC hint:与no merge hint同理。
  2. 可通过人工修改自动生成的outline,添加outline中不支持自动提取的hint,来固定计划。

plan management使用示例

  1. 设置开启plan management。

    SET resource_track_cost=0;
    SET resource_track_duration=0;
    SET enable_generate_plan_hash = on;
    SET planmgmt_options='plan_save_mode_outline,plan_save_level_topsql,enable_plan_baseline';

  2. 连接数据库创建表和索引。

    1
    2
    3
    4
    CREATE TABLE t1(a1 int, b1 int, c1 serial) DISTRIBUTE BY hash(a1);
    INSERT INTO t1(a1, b1) VALUES(generate_series(1, 10), generate_series(1, 8));
    CREATE INDEX idx_t1_b1 on t1(b1);
    CREATE TABLE t2(a2, b2, c2) AS SELECT * FROM t1;
    

  3. 查看不使用索引和使用索引两个语句的计划。

    查看不使用索引的语句计划:

    1
    EXPLAIN (verbose on, blockname on, outline on) select count(*) from t1, t2 where b1 = 1 and a1 = a2;
    

    查看使用索引的语句计划:

    1
    EXPLAIN (verbose on, blockname on, outline on) select /*+ indexscan(t1 idx_t1_b1) */ count(*) from t1, t2 where b1 = 1 and a1 = a2;
    

  4. 查看sql_outline表中保存的计划。

    1
    SELECT sql_hash, plan_hash, outline_name, outline FROM dbms_om.sql_outline WHERE outline like '%public.t1%public.t2%';
    

    使用索引的语句对应的sql_hash、plan_hash和outline_name分别为'sql_6e8591b8291c3b458b26ce7419fd3704'和'plan_00df934ed2620cb09c5f51a33f1945fd'和'outline_ae16ae6300416c3efc96378c7b3a7e7b'。

  5. 为语句绑定使用索引的计划。

    1
    2
    3
    4
    5
    SELECT pgxc_bind_plan('sql_6e8591b8291c3b458b26ce7419fd3704', 'outline_ae16ae6300416c3efc96378c7b3a7e7b');
     pgxc_bind_plan
    ----------------
     t
    (1 row)
    

  6. 查看语句是否使用了绑定的索引计划。

    1
    EXPLAIN (verbose on, blockname on, outline on) select count(*) from t1, t2 where b1 = 1 and a1 = a2;
    

    从上面的计划可见,绑定的索引计划被使用。

  7. 为语句解绑计划。

    SELECT pgxc_unbind_plan('sql_6e8591b8291c3b458b26ce7419fd3704');
     pgxc_unbind_plan
    ------------------
     t
    (1 row)

  8. 查看解绑计划后的执行计划。

    1
    EXPLAIN (verbose on, blockname on, outline on) select count(*) from t1, t2 where b1 = 1 and a1 = a2;
    

    从上面的计划可见,索引计划不再被使用。

  9. 重新绑定计划,删除索引。

    1
    2
    3
    4
    5
    6
    7
    SELECT pgxc_bind_plan('sql_6e8591b8291c3b458b26ce7419fd3704', 'outline_ae16ae6300416c3efc96378c7b3a7e7b');
     pgxc_bind_plan
    ----------------
     t
    (1 row)
    
    DROP INDEX idx_t1_b1;
    

  10. 查看计划失效后的语句执行计划。

    EXPLAIN (verbose on, blockname on, outline on) select count(*) from t1, t2 where b1 = 1 and a1 = a2;

    从上面的计划可见,虽然绑定的outline被使用,但绑定的outline中的索引hint由于索引删除,导致索引hint未生效。

  11. 含no merge hint的语句的计划。

    EXPLAIN (verbose on, blockname on, outline on) select * from t1 where a1 in (select /*+ no merge */ a2 from t2 where b2 < 10);

  12. 无no merge hint的语句的计划。

    EXPLAIN (verbose on, blockname on, outline on) select * from t1 where a1 in (select a2 from t2 where b2 < 10);

  13. 查询含no merge hint和无no merge hint生成的outline。

    SELECT sql_hash, plan_hash, outline_name, outline FROM dbms_om.sql_outline WHERE sql_hash = 'sql_14150502dc61e290b4c81c5c43d7446c';

  14. 绑定含no merge hint的outline。

    SELECT pgxc_bind_plan('sql_14150502dc61e290b4c81c5c43d7446c', 'outline_a2cdc54106b991174cc0a35b8e0f0b29');
     pgxc_bind_plan
    ----------------
     t
    (1 row)

  15. 对无no merge hint的语句,应用绑定的outline后,仍产生了子查询提升。

    EXPLAIN (verbose on, blockname on, outline on) select * from t1 where a1 in (select a2 from t2 where b2 < 10);

  16. 人工修改绑定的outline添加no merge hint,并重新创建到dbms_om.sql_outline中。

    CREATE OUTLINE outline_nomerge for sql_14150502dc61e290b4c81c5c43d7446c using
    '/*+
        begin_outline_data
         no merge@"sel$2"
         TableScan(@"sel$1" public.t1@"sel$1")
         TableScan(@"sel$2" public.t2@"sel$2")
        end_outline_data
     */'

  17. 将新创建的含no merge hint的outline进行绑定。

    SELECT pgxc_bind_plan('sql_14150502dc61e290b4c81c5c43d7446c', 'outline_nomerge');
     pgxc_bind_plan
    ----------------
     t
    (1 row)

  18. 对无no merge hint的语句,应用绑定的outline后,不再产生子查询提升。

    EXPLAIN (verbose on, blockname on, outline on) select * from t1 where a1 in (select a2 from t2 where b2 < 10);