执行计划缓存 
===========================



SQL 语句的优化是一个比较耗时的过程，随着 SQL 语句的复杂度增高，优化的时间也会越来越长。为了避免反复执行优化过程，生成的执行计划会加入到计划缓存中，以便再次执行该 SQL 时使用。每一个租户在每一台服务器上都有一个独立的计划缓存，用以缓存在此服务器上处理过的 SQL 计划。

在应用系统中，同一条 SQL 每次执行可能会使用不同的参数，为了减少计划缓存的大小，系统会首先将用户 SQL 的参数做参数化处理，得到与具体参数无关的 SQL 字符串，并使用该字符串作为计划缓存的键值(具体参数化过程见 [快速参数化](../../../12.sql-optimization-guide-1/2.sql-execution-plan-3/4.fast-parameterization-3.md)介绍)。

计划缓存是一个典型的 Key-Value 结构，Key 就是参数化后的 SQL 字符串，Value 就是该条 SQL 所对应的执行计划。在 OceanBase 数据库的计划缓存中，SQL 的执行计划可以分为本地计划、远程计划和分布式计划三种类型。在计划缓存中，同一条 SQL 根据其需要访问的数据不同，可能同时具有三种执行计划。

对于某一条 SQL 的某一种执行计划，默认情况下 OceanBase 数据库只会保留一个计划，该计划由第一次执行 SQL 时生成；但在某些情况下，同一条 SQL 的参数值可能会影响到计划的选择，计划缓存可能会根据需要，为不同的参数值保留不同的执行计划，从而保证每次执行时可以使用最合适的计划。

计划缓存的淘汰 
----------------

计划缓存的淘汰是指将执行计划从计划缓存中删除，减少计划缓存对内存的使用，OceanBase 数据库支持自动淘汰和手动淘汰两种方式。

#### 自动淘汰 

自动淘汰是指当计划缓存占用的内存达到了需要淘汰计划的内存上限(即淘汰计划的高水位线)时，对计划缓存中执行计划自动进行淘汰。

* 触发执行计划淘汰的条件

  每隔一段时间(具体时间间隔由配置项 `plan_cache_evict_interval` 设置)会自动检查是否需要进行执行计划淘汰，对于不同租户在不同服务器上的计划缓存，会分别进行检查。如果某个计划缓存占用的内存超过该租户设置的淘汰计划的高水位线，则触发计划缓存淘汰。
  




<!-- -->

* 执行计划淘汰策略

  当触发计划缓存淘汰后，优先淘汰最久没被使用的执行计划，淘汰一部分执行计划后，当计划缓存使用的内存为该租户设置的淘汰计划的低水位线时，停止淘汰。
  




<!-- -->

* 与计划缓存淘汰相关配置

  




<!-- -->



自动淘汰示例如下：如果租户内存大小为10G， `ob_plan_cache_percentage`＝10， `ob_plan_cache_evict_high_percentage`＝90， `ob_plan_cache_evict_low_percentage`＝50，则

* 计划缓存内存上限绝对值 ＝ 10G \* 10 / 100 = 1G

  

* 淘汰计划的高水位线 = 1G \* 90 / 100 = 0.9G

  

* 淘汰计划的低水位线 ＝ 1G \* 50 / 100 = 0.5G

  当该租户在某个服务器上计划缓存使用超过 0.9 G 时，会触发淘汰，优先淘汰最久没执行的计划，当淘汰到使用内存只有 0.5 G 时，则停止淘汰。 如果淘汰速度没有新计划生成速度快，计划缓存使用内存达到内存上限绝对值 1 G 时，将不在往计划缓存中添加新计划，直到淘汰后使用的内存小于 1 G 才会添加新计划到计划缓存中。
  




#### 手动淘汰 

手动淘汰是指强制将计划缓存中计划进行删除，现在支持指定不同租户对应的当前 server 或全部服务器中计划缓存全部删除，具体命令如下：

```javascript
alter system flush plan cache [tenant_list] [global]
其中tenant\_list：tenant = 'tenant1, tenant2, tenant3....'
```



其中 tenant_list 和 global 为可选字段： 

* 如果 tenant_list 没有指定，则清空所有租户的计划缓存，否则只清空特定租户的。

  

* 如果 global 没有指定，则清空本机的计划缓存，否则清空该租户所在的所有 server上的计划缓存。

  




计划缓存的刷新 
----------------

计划缓存中执行计划可能因为各种原因失效，这时就需要将计划缓存中失效计划进行刷新，即将该执行计划删除然后重新优化生成后加入计划缓存。在如下场景下执行计划会失效需要对执行计划进行刷新：

* SQL 中涉及的表的 schema 进行变更时（比如添加索引，删除或增加列等），该 SQL 对应的在计划缓存中的执行计划将刷新。

  




<!-- -->

* SQL 中涉及的表的统计信息重新收集时，该 SQL 对应的执行计划会刷新，由于 OceanBase 数据库在合并时统一进行统计信息的收集，因此现在每次进行合并后，计划缓存中所有计划将刷新。

  






计划缓存的使用控制 
------------------

是否使用计划缓存可以使用系统变量及 HINT 进行控制。

* 系统变量控制 `ob_enable_plan_cache` 设置为 ture 时，表示 SQL 请求可以使用计划缓存；设置为 false 时，表示 SQL 请求不使用计划缓存。可进行 session 级和 global 级设置，默认使用该设置为 true。

  

* HINT 控制

  




<!-- -->



计划缓存暂不支持的场景 
--------------------

* 内存大小超过 20 M 执行计划不加入计划缓存。

  

* 如果该计划为分布式计划且涉及多个表，则不加入计划缓存。

  




计划缓存的视图 
----------------

执行计划相关视图包括：

* `(g)v$plan_cache_stat` : 记录每个计划缓存的状态，每个计划缓存在该视图中有一条记录。

  




<!-- -->

* `(g)v$plan_cache_plan_stat` :记录计划缓存中所有 plan 的具体信息及每个计划总的执行统计信息， 每个 plan 在该视图中一条记录。

  




<!-- -->

* `(g)v$plan_cache_plan_explain` : 记录某条 SQL 在计划缓存中的执行计划。

  




详细信息，请参见 [Plan Cache 相关视图](../../../12.sql-optimization-guide-1/4.sql-optimization-1/3.monitor-sql-execution-performance-1/3.plan-cache-view.md)。



