
# 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、index 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，在使用时需要放在两者之间。
![](https://support.huaweicloud.com/centralized-devg-v8-gaussdb/public_sys-resources/notice_3.0-zh-cn.png)
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)
   ```
   ![](https://support.huaweicloud.com/centralized-devg-v8-gaussdb/public_sys-resources/note_3.0-zh-cn.png)
   
   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)
      ```
      
   
   
   ![](https://support.huaweicloud.com/centralized-devg-v8-gaussdb/public_sys-resources/note_3.0-zh-cn.png)
   可以看到，普通的hint只指定了部分行为，并不能完全固定计划。
   示例a中，普通hint产生了Materialize计划，该计划并不在hint中。
   示例b中，Outline Hint没有产生Materialize计划，完整地固化了计划。
   
 
