更新时间:2024-06-03 GMT+08:00

Outline Hint

功能描述

Outline是描述执行计划的重要手段,也是计划固化的持久化表示。Outline存储在数据库中,需要在不同版本之间兼容,可以指导优化器生成指定计划。内核在生成执行计划的同时,可以生成Outline。同时优化器能够使用Outline对计划进行控制,Outline是计划管理的核心前提能力。

Outline Hint是优化器为了完全复现某一计划而生成的一组hint信息,以BEGIN_OUTLINE_DATA开始,并以END_OUTLINE_DATA结束。Outline Hint可以通过explain(outline on)获得。使用获得的Outline Hint能够对计划进行控制。

特性约束

  1. 使用之前需要设置set explain_perf_mode = pretty选项。
  2. Outline用于计划的复现还原,目前Outline可以控制同一条SQL的如下方面:
    • 查询重写。
    • 每层subquery的物理算子:

      a)扫描方式

      b)连接方法

      c)连接顺序

      d)bitmap扫描的索引表

      e)参数化路径

      f)连接的内表物化

    • 每层subquery的agg方法。
    • any子链接提升的额外处理:hashed或者material。
    • smp计划数据的传输方式。
  3. 目前内核的bitmapscan、indexscan Hint指定优化器在扫描相关表时,使用指定的索引产生索引扫描路径,具体的索引条件由优化器根据代价生成。
  4. 对于复杂多表连接的SQL,outline固定计划还原时,性能优于遗传算法。
  5. 当有Outline Hint时,对于在BEGIN OUTLINE和END OUTLINE包含以外的hint,若为控制计划生成的hint则全部失效处理(2,3,4点所提及的hint),若非控制计划生成的hint则保留,如wlmrule慢SQL管控规则的hint。

语法格式

Outline Hint也是hint的一种,遵循hint的语法。

BEGIN_OUTLINE_DATA
VERSION(@version_num)
END_OUTLINE_DATA

参数说明

  • @version_num:Outline的版本。不指定默认为1.0.0,当前仅支持1.0.0,为后续版本Outline行为控制做预留。
  • BEGIN_OUTLINE_DATA/END_OUTLINE_DATA:生成的Outline Hint,在使用时需要放在两者之间。
  1. BEGIN_OUTLINE_DATA和END_OUTLINE_DATA必须成对使用。
  2. 当同时使用BEGIN_OUTLINE_DATA和END_OUTLINE_DATA时,只有两者之间的hint会生效。

使用说明

  1. 生成Outline Hint

    使用前设置:

    SET explain_perf_mode = pretty;

    explain (Outline on):

    --创建表
    gaussdb=# CREATE TABLE ot_t1(a int, b int);
    gaussdb=# CREATE TABLE ot_t2(a int, b int);
    
    --执行
    gaussdb=# EXPLAIN (OUTLINE ON, COSTS OFF) SELECT * FROM ot_t1 JOIN ot_t2 ON ot_t1.a = ot_t2.a;
     id |          operation          
    ----+-----------------------------
      1 | ->  Hash Join (2,3)
      2 |    ->  Seq Scan on ot_t1
      3 |    ->  Hash
      4 |       ->  Seq Scan on ot_t2
    (4 rows)
    
     Predicate Information (identified by plan id) 
    -----------------------------------------------
       1 --Hash Join (2,3)
             Hash Cond: (ot_t1.a = ot_t2.a)
    (2 rows)
    
                        ====== Outline Data =====                    
    -----------------------------------------------------------------
       begin_outline_data
       HashJoin(@"sel$1" public.ot_t1@"sel$1" public.ot_t2@"sel$1")
       Leading(@"sel$1" (public.ot_t1@"sel$1" public.ot_t2@"sel$1"))
       TableScan(@"sel$1" public.ot_t1@"sel$1")
       TableScan(@"sel$1" public.ot_t2@"sel$1")
       version("1.0.0")
       end_outline_data
    (7 rows)

    1. 上述Outline data由一系列hint组成。关于hint和hint中查询块的指定功能,遵循原有hint能力。
    2. BEGIN_OUTLINE_DATA和END_OUTLINE_DATA分别表示Outline的开始和结束。
    3. HashJoin(@"sel$1" t1@"sel$1" t2@"sel$1")表示在第一个查询块(只有一个查询块),对t1(t1原处于sel$1查询块),t2(t2原处于sel$1查询块)进行HashJoin运算,Leading(@"sel$1" (t1@"sel$1" t2@"sel$1"))表示连接顺序,TableScan(@"sel$1" t1@"sel$1"),TableScan(@"sel$1" t2@"sel$1")表示t1和t2表都进行了顺序扫描。
    4. Outline能够和计划对应。
    5. 目前版本号固定为("1.0.0")。
  2. Outline Hint的使用

    在Outline使用时,会使用Outline转换而成的SQL。

    gaussdb=# EXPLAIN (OUTLINE ON, COSTS OFF) SELECT /*+ 
        BEGIN_OUTLINE_DATA
        HashJoin(@"sel$1" public.ot_t1@"sel$1" public.ot_t2@"sel$1")
        Leading(@"sel$1" (public.ot_t1@"sel$1" public.ot_t2@"sel$1"))
        TableScan(@"sel$1" public.ot_t1@"sel$1")
        TableScan(@"sel$1" public.ot_t2@"sel$1")
        VERSION("1.0.0")
        END_OUTLINE_DATA */ * FROM ot_t1 JOIN ot_t2 ON ot_t1.a = ot_t2.a;
     id |          operation          
    ----+-----------------------------
      1 | ->  Hash Join (2,3)
      2 |    ->  Seq Scan on ot_t1
      3 |    ->  Hash
      4 |       ->  Seq Scan on ot_t2
    (4 rows)
    
     Predicate Information (identified by plan id) 
    -----------------------------------------------
       1 --Hash Join (2,3)
             Hash Cond: (ot_t1.a = ot_t2.a)
    (2 rows)
    
                        ====== Outline Data =====                    
    -----------------------------------------------------------------
       begin_outline_data
       HashJoin(@"sel$1" public.ot_t1@"sel$1" public.ot_t2@"sel$1")
       Leading(@"sel$1" (public.ot_t1@"sel$1" public.ot_t2@"sel$1"))
       TableScan(@"sel$1" public.ot_t1@"sel$1")
       TableScan(@"sel$1" public.ot_t2@"sel$1")
       version("1.0.0")
       end_outline_data
    (7 rows)

    对比1,2可以看到两个计划完全相同,说明可以用Outline Hint控制计划的生成。

  3. 对比非Outline Hint计划
    1. 普通的hint:
      gaussdb=#  EXPLAIN (OUTLINE ON, COSTS OFF) SELECT /*+ 
          NestLoop(@"sel$1" ot_t1@"sel$1" ot_t2@"sel$1")
          Leading(@"sel$1" (ot_t1@"sel$1" ot_t2@"sel$1"))
          TableScan(@"sel$1" ot_t1@"sel$1")
          TableScan(@"sel$1" ot_t2@"sel$1") */ * FROM ot_t1 JOIN ot_t2 ON ot_t1.a = ot_t2.a;
       id |          operation          
      ----+-----------------------------
        1 | ->  Nested Loop (2,3)
        2 |    ->  Seq Scan on ot_t1
        3 |    ->  Materialize
        4 |       ->  Seq Scan on ot_t2
      (4 rows)
      
       Predicate Information (identified by plan id) 
      -----------------------------------------------
         1 --Nested Loop (2,3)
               Join Filter: (ot_t1.a = ot_t2.a)
      (2 rows)
      
                          ====== Outline Data =====                    
      -----------------------------------------------------------------
         begin_outline_data
         NestLoop(@"sel$1" public.ot_t1@"sel$1" public.ot_t2@"sel$1")
         Leading(@"sel$1" (public.ot_t1@"sel$1" public.ot_t2@"sel$1"))
         TableScan(@"sel$1" public.ot_t1@"sel$1")
         Materialize_Inner(@"sel$1" public.ot_t2@"sel$1")
         TableScan(@"sel$1" public.ot_t2@"sel$1")
         version("1.0.0")
         end_outline_data
      (8 rows)
       
    2. Outline Hint:
      gaussdb=#  EXPLAIN (OUTLINE ON, COSTS OFF) SELECT /*+ 
          BEGIN_OUTLINE_DATA
          NestLoop(@"sel$1" ot_t1@"sel$1" ot_t2@"sel$1")
          Leading(@"sel$1" (ot_t1@"sel$1" ot_t2@"sel$1"))
          TableScan(@"sel$1" ot_t1@"sel$1")
          TableScan(@"sel$1" ot_t2@"sel$1")
          VERSION("1.0.0")
          END_OUTLINE_DATA */ * from ot_t1 join ot_t2 on ot_t1.a = ot_t2.a;
       id |        operation         
      ----+--------------------------
        1 | ->  Nested Loop (2,3)
        2 |    ->  Seq Scan on ot_t1
        3 |    ->  Seq Scan on ot_t2
      (3 rows)
      
       Predicate Information (identified by plan id) 
      -----------------------------------------------
         1 --Nested Loop (2,3)
               Join Filter: (ot_t1.a = ot_t2.a)
      (2 rows)
      
                          ====== Outline Data =====                    
      -----------------------------------------------------------------
         begin_outline_data
         NestLoop(@"sel$1" public.ot_t1@"sel$1" public.ot_t2@"sel$1")
         Leading(@"sel$1" (public.ot_t1@"sel$1" public.ot_t2@"sel$1"))
         TableScan(@"sel$1" public.ot_t1@"sel$1")
         TableScan(@"sel$1" public.ot_t2@"sel$1")
         version("1.0.0")
         end_outline_data
      (7 rows)

    可以看到,普通的hint只指定了部分行为,并不能完全固定计划。

    示例a中,普通hint产生了Materialize计划,该计划并不在hint中。

    示例b中,Outline Hint没有产生Materialize计划,完整地固化了计划。