执行计划管理 
===========================

SQL Plan Management（SPM）是一种稳定执行计划、控制计划演进的机制，确保新生成的计划在经过验证后才能使用，保证计划性能朝好的方向不断更新。

SPM 基于 SQL Plan Baseline 实现，SQL Plan Baseline 是执行计划的一个基线，持久化存储已经验证过的执行计划的信息（outline_data 等信息），每个执行计划可对应一个 Plan Baseline，通过该 Plan Baseline 可复现一个执行计划。

SPM 包含如下过程：

1. 计划捕获。

   对于新生成的计划，如果 SQL Plan Baseline 为空，则直接加入 SQL Plan Baseline，否则通过演进验证新生成计划比 SQL Plan Baseline 中计划性能更优后加入 SQL Plan Baseline，并删除旧的 Plan Baseline。
   




<!-- -->

2. 计划演进。

   相同 SQL 新捕获的计划如果和 SQL Plan Baseline 中计划不一样，则通过流量灰度验证新计划性能是否比以前验证过的计划更优。如果更优，则将新计划加入 SQL Plan Baseline，并执行新计划，否则仍使用旧计划。
   




<!-- -->

3. 计划选择。

   在优化器新生成计划时，会查看 SQL Plan Baseline 是否有已验证的计划，如果有，则优先使用已验证计划，新计划需要通过演进验证后再使用。
   






SPM 的系统变量 
------------------

SPM 使用如下系统变量和系统包对执行计划进行管理：


|               **系统变量**               | **取值** |                                                                **解释**                                                                 |
|--------------------------------------|--------|---------------------------------------------------------------------------------------------------------------------------------------|
| optimizer_capture_sql_plan_baselines | true   | 对于新生成的计划，如果该 SQL 没有对应的 Plan Baseline，则将该计划加入到 SQL Plan Baseline；如果已有 Plan Baseline 且与新计划不同，则会触发计划演进进行验证，确定是否需要将新计划替换老的 Plan Baseline。 |
| optimizer_capture_sql_plan_baselines | false  | 再自动捕获新计划到 Plan Baseline 中。                                                                                                            |
| optimizer_use_sql_plan_baselines     | true   | 在新生成计划时，优化器会优先使用 Plan Baseline 计划，对于新的不同计划则验证后通过后才使用。                                                                                 |
| optimizer_use_sql_plan_baselines     | false  | 在新生成计划时，不在考虑 Plan Baseline 中计划，直接使用优化器新生成计划并执行。                                                                                       |



设置说明如下：


| **optimizer_capture_sql_plan_baselines 的取值** | **optimizer_use_sql_plan_baselines 的取值** |                                       **说明**                                        |
|----------------------------------------------|------------------------------------------|-------------------------------------------------------------------------------------|
| True                                         | True                                     | 计划捕获和演进均打开，优化器会使用 Plan Baseline 计划。                                                 |
| True                                         | False                                    | Plan Baseline 中无计划时会捕获计划到 Plan Baseline，不演进，优化器不考虑 Plan Baseline 计划，使用新生成计划。        |
| False                                        | True                                     | 不捕获计划到 Plan Baseline， 优化器会使用 Plan Baseline 计划，如果 SQL Plan Baseline 没有对应计划，则使用新生成计划。 |
| False                                        | False                                    | 不捕获计划，不演进，优化器不使用 Plan Baseline 计划，使用新生成的计划。                                         |



DBMS_SPM 
-----------------

DBMS_SPM 是用于操作 SPM 的命令包，可支持加载、更改以及删除 Plan Baseline 信息。

#### **LOAD_PLANS_FROM_CURSOR_CACHE** 

LOAD_PLANS_FROM_CURSOR_CACHE 用于将 plan cache 中执行计划对应的 Plan Baseline 信息加载到 _ _all_tenant_plan_baseline 表中。语法如下：
**说明**



_ _all_tenant_plan_baseline 为 OceanBase 数据库内部表。

```javascript
DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE (
   sql_id            IN  VARCHAR2,
   plan_hash_value   IN  NUMBER   := NULL,
   fixed             IN  VARCHAR2 := 'NO',
   enabled           IN  VARCHAR2 := 'YES')
 RETURN PLS_INTEGER;
```



参数解释如下：


|       参数        |                             解释                              |
|-----------------|-------------------------------------------------------------|
| sql_id          | SQL 的唯一标识。                                                  |
| plan_hash_value | plan 的唯一标识。如果为空，则处理 sql_id 下的所有计划。                          |
| fixed           | 加入到 SQL Plan Baseline 后是否将该计划固化。固化后以后优化器会直接选择该计划，不再捕获和演进计划。 |
| enabled         | 优化器是否可以使用该 Plan Baseline。                                   |



如下例所示：

```javascript
DECLARE
  v_load_plans number;
BEGIN
  v_load_plans := DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE(
     sql_id => '529F6E6454EF579C7CC265D1F6131D70',
     plan_hash_value => 13388268709115914355);
END;
/
```



#### **ALTER_SQL_PLAN_BASELINE** 

ALTER_SQL_PLAN_BASELINE 用于修改 Plan Baseline 中某些属性。语法如下：

```javascript
DBMS_SPM.ALTER_SQL_PLAN_BASELINE ( 
   sql_handle        IN VARCHAR2 := NULL,
   plan_name         IN VARCHAR2 := NULL,
   attribute_name    IN VARCHAR2,
   attribute_value   IN VARCHAR2)
 RETURN PLS_INTEGER;
```



参数解释如下：


|       参数        |                       解释                       |
|-----------------|------------------------------------------------|
| sql_handle      | SQL 的唯一标识。先用 sql_id 代替。                        |
| plan_name       | plan 的唯一标识。先使用 plan_hash_value 代替。             |
| attribute_name  | 需要更改的字段名。OceanBase 数据库支持修改 enabled 和 fixed 字段。 |
| attribute_value | 更改后的值。                                         |



如下示例所示，将某个 Plan Baseline 固化后该 SQL 仅使用该计划：

```javascript
DECLARE
  v_alter_plans number;
BEGIN
  v_alter_plans := DBMS_SPM.ALTER_SQL_PLAN_BASELINE(
     sql_handle => '529F6E6454EF579C7CC265D1F6131D70',
     plan_name => '3388268709115914355',
     attribute_name => 'fixed',
     attribute_value => 'YES' );
END;
/
```





#### **DROP_SQL_PLAN_BASELINE** 

DROP_SQL_PLAN_BASELINE 用于删掉某个 Plan Baseline。语法如下：

```javascript
DBMS_SPM.DROP_SQL_PLAN_BASELINE (
   sql_handle     IN VARCHAR2 := NULL,
   plan_name      IN VARCHAR2 := NULL)
RETURN PLS_INTEGER;
```



示例如下：

```javascript
DECLARE
  v_drop_plans number;
BEGIN
  v_drop_plans := DBMS_SPM.DROP_SQL_PLAN_BASELINE(
     sql_handle => '529F6E6454EF579C7CC265D1F6131D70',
     plan_name => '3388268709115914355' );
END;
/
```



