="t18">
什么是执行计划
所谓执行计划
![](/icons/29739dou.gif)
顾名思义
![](/icons/29739dou.gif)
就是对
![](/icons/29739yi.gif)
个查询任务
![](/icons/29739dou.gif)
做出
![](/icons/29739yi.gif)
份怎样去完成任务
![](/icons/29739de.gif)
具体方案
![](/icons/29739dou2.gif)
举个生活中
![](/icons/29739de.gif)
例子
![](/icons/29739dou.gif)
我从珠海要去英国
![](/icons/29739dou.gif)
我可以选择先去香港然后转机
![](/icons/29739dou.gif)
也可以先去北京转机
![](/icons/29739dou.gif)
或者去广州也可以
![](/icons/29739dou2.gif)
但是到底怎样去英国划算
![](/icons/29739dou.gif)
也就是我
![](/icons/29739de.gif)
费用最少
![](/icons/29739dou.gif)
这是
![](/icons/29739yi.gif)
件值得考究
![](/icons/29739de.gif)
事情
![](/icons/29739dou2.gif)
同样对于查询而言
![](/icons/29739dou.gif)
我们提交
![](/icons/29739de.gif)
SQL仅仅是描述出了我们
![](/icons/29739de.gif)
目
![](/icons/29739de.gif)
地是英国
![](/icons/29739dou.gif)
但至于如何去
![](/icons/29739dou.gif)
通常我们
![](/icons/29739de.gif)
SQL中是没有给出提示信息
![](/icons/29739de.gif)
![](/icons/29739dou.gif)
是由数据库来决定
![](/icons/29739de.gif)
![](/icons/29739dou2.gif)
我们先简单
![](/icons/29739de.gif)
看
![](/icons/29739yi.gif)
个执行计划
![](/icons/29739de.gif)
对比:
SQL>
![](/icons/29739set.gif)
autotrace traceonly
执行计划
![](/icons/29739yi.gif)
:
SQL> select count(*) from t;
COUNT(*)
----------
24815
Execution Plan
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 SORT (AGGREGATE)
2 1 TABLE Access (FULL) OF 'T'
执行计划 2:
SQL> select count(*) from t;
COUNT(*)
24815
Execution Plan
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=26 Card=1)
1 0 SORT (AGGREGATE)
2 1 INDEX (FULL SCAN) OF 'T_INDEX' (NON-UNIQUE) (Cost=26 Card=28180)
这两个执行计划中
![](/icons/29739dou.gif)
第
![](/icons/29739yi.gif)
个表示求和是通过进行全表扫描来做
![](/icons/29739de.gif)
![](/icons/29739dou.gif)
把整个表中数据读入内存来逐条累加;第 2个表示根据表中索引
![](/icons/29739dou.gif)
把整个索引读进内存来逐条累加
![](/icons/29739dou.gif)
而不用去读表中
![](/icons/29739de.gif)
数据
![](/icons/29739dou2.gif)
但是这两种方式到底哪种快呢?通常来说可能 2比
![](/icons/29739yi.gif)
快
![](/icons/29739dou.gif)
但也不是绝对
![](/icons/29739de.gif)
![](/icons/29739dou2.gif)
这是
![](/icons/29739yi.gif)
个很简单
![](/icons/29739de.gif)
例子演示执行计划
![](/icons/29739de.gif)
差异
![](/icons/29739dou2.gif)
对于复杂
![](/icons/29739de.gif)
SQL(表连接、嵌套子查询等)
![](/icons/29739dou.gif)
执行计划可能几十种甚至上百种
![](/icons/29739dou.gif)
但是到底那种最好呢?我们事前并不知道
![](/icons/29739dou.gif)
数据库本身也不知道
![](/icons/29739dou.gif)
但是数据库会根据
![](/icons/29739yi.gif)
定
![](/icons/29739de.gif)
规则或者统计信息(statistics)去选择
![](/icons/29739yi.gif)
个执行计划
![](/icons/29739dou.gif)
通常来说选择
![](/icons/29739de.gif)
是比较优
![](/icons/29739de.gif)
![](/icons/29739dou.gif)
但也有选择失误
![](/icons/29739de.gif)
时候
![](/icons/29739dou.gif)
这就是这次讨论
![](/icons/29739de.gif)
价值所在
![](/icons/29739dou2.gif)
Oracle优化器模式
Oracle优化器有两大类
![](/icons/29739dou.gif)
基于规则
![](/icons/29739de.gif)
和基于代价
![](/icons/29739de.gif)
![](/icons/29739dou.gif)
在SQLPLUS中我们可以查看init文件中定义
![](/icons/29739de.gif)
缺省
![](/icons/29739de.gif)
优化器模式
SQL> show parameters optimizer_mode
NAME TYPE VALUE
optimizer_mode
![](/icons/29739string.gif)
CHOOSE
SQL>
这是Oracle8.1.7 企业版
![](/icons/29739dou.gif)
我们可以看出
![](/icons/29739dou.gif)
默认安装后数据库优化器模式为CHOOSE,我们还可以设置为 RULE、FIRST_ROWS,ALL_ROWS
![](/icons/29739dou2.gif)
可以在init文件中对整个instance
![](/icons/29739de.gif)
所有会话设置
![](/icons/29739dou.gif)
也可以单独对某个会话设置:
SQL> ALTER SESSION SET optimizer_mode = RULE;
会话已更改
SQL> ALTER SESSION SET optimizer_mode = FIRST_ROWS;
会话已更改
SQL> ALTER SESSION SET optimizer_mode = ALL_ROWS;
会话已更改
基于规则
![](/icons/29739de.gif)
查询
![](/icons/29739dou.gif)
数据库根据表和索引等定义信息
![](/icons/29739dou.gif)
按照
![](/icons/29739yi.gif)
定
![](/icons/29739de.gif)
规则来产生执行计划;基于代价
![](/icons/29739de.gif)
查询
![](/icons/29739dou.gif)
数据库根据搜集
![](/icons/29739de.gif)
表和索引
![](/icons/29739de.gif)
数据
![](/icons/29739de.gif)
统计信息(通过analyze 命令或者使用dbms_stats包来搜集)综合来决定选取
![](/icons/29739yi.gif)
个数据库认为最优
![](/icons/29739de.gif)
执行计划(实际上不
![](/icons/29739yi.gif)
定最优)
![](/icons/29739dou2.gif)
RULE是基于规则
![](/icons/29739de.gif)
![](/icons/29739dou.gif)
CHOOSE表示假如查询
![](/icons/29739de.gif)
表存在搜集
![](/icons/29739de.gif)
统计信息则基于代价来执行(在CHOOSE模式下Oracle采用
![](/icons/29739de.gif)
是 FIRST_ROWS)
![](/icons/29739dou.gif)
否则基于规则来执行
![](/icons/29739dou2.gif)
在基于代价
![](/icons/29739de.gif)
两种方式中
![](/icons/29739dou.gif)
FIRST_ROWS指执行计划采用最少资源尽快
![](/icons/29739de.gif)
返回部分结果给客户端
![](/icons/29739dou.gif)
对于排序分页页显示这种查询尤其适用
![](/icons/29739dou.gif)
ALL_ROWS指以总体消耗资源最少
![](/icons/29739de.gif)
方式返回结果给客户端
![](/icons/29739dou2.gif)
基于规则
![](/icons/29739de.gif)
模式下
![](/icons/29739dou.gif)
数据库
![](/icons/29739de.gif)
执行计划通常比较稳定
![](/icons/29739dou2.gif)
但在基于代价
![](/icons/29739de.gif)
模式下
![](/icons/29739dou.gif)
我们才有更大
![](/icons/29739de.gif)
机会选择最优
![](/icons/29739de.gif)
执行计划
![](/icons/29739dou2.gif)
也由于Oracle
![](/icons/29739de.gif)
很多查询方面
![](/icons/29739de.gif)
特性必须在基于代价
![](/icons/29739de.gif)
模式下才能体现出来
![](/icons/29739dou.gif)
所以我们通常不选择RULE(并且Oracle宣称从 Oracle 10i版本数据库开始将不再支持 RULE)
![](/icons/29739dou2.gif)
既然是基于代价
![](/icons/29739de.gif)
模式
![](/icons/29739dou.gif)
也就是说执行计划
![](/icons/29739de.gif)
选择是根据表、索引等定义和数据
![](/icons/29739de.gif)
统计信息来决定
![](/icons/29739de.gif)
![](/icons/29739dou.gif)
这个统计信息是根据 analyze 命令或者dbms_stats包来定期搜集
![](/icons/29739de.gif)
![](/icons/29739dou2.gif)
首先存在着
![](/icons/29739yi.gif)
种可能
![](/icons/29739dou.gif)
就是由于搜集信息是
![](/icons/29739yi.gif)
个很消耗资源和时间
![](/icons/29739de.gif)
动作
![](/icons/29739dou.gif)
尤其当表数据量很大
![](/icons/29739de.gif)
时候
![](/icons/29739dou.gif)
![](/icons/29739yinwei.gif)
搜集信息是对整个表数据进行重新
![](/icons/29739de.gif)
完全统计
![](/icons/29739dou.gif)
所以这是我们必须慎重考虑
![](/icons/29739de.gif)
问题
![](/icons/29739dou2.gif)
我们只能在服务器空闲
![](/icons/29739de.gif)
时候定期
![](/icons/29739de.gif)
进行信息搜集
![](/icons/29739dou2.gif)
这介绍说明我们在
![](/icons/29739yi.gif)
段时期内
![](/icons/29739dou.gif)
统计信息可能和数据库本身
![](/icons/29739de.gif)
数据并不吻合;另外就是Oracle
![](/icons/29739de.gif)
统计数据本身也存在着不精确部分(具体参考Oracle DOCUMENT)
![](/icons/29739dou.gif)
更重要
![](/icons/29739de.gif)
![](/icons/29739yi.gif)
个问题就是及时统计数据相对已经比较准确
![](/icons/29739dou.gif)
但是Oracle
![](/icons/29739de.gif)
优化器
![](/icons/29739de.gif)
选择也并不是始终是最优
![](/icons/29739de.gif)
方案
![](/icons/29739dou2.gif)
这也倚赖于Oracle对区别执行计划
![](/icons/29739de.gif)
代价
![](/icons/29739de.gif)
计算规则(我们通常是无法知道具体
![](/icons/29739de.gif)
计算规则
![](/icons/29739de.gif)
)
![](/icons/29739dou2.gif)
这好比我们决定从香港还是从北京去英国
![](/icons/29739dou.gif)
车票、机票等实际价格到底是如何核算出来
![](/icons/29739de.gif)
我们并不知道
![](/icons/29739dou.gif)
或者说我们现在了解
![](/icons/29739de.gif)
价格信息
![](/icons/29739dou.gif)
在我们乘车前往
![](/icons/29739de.gif)
时候
![](/icons/29739dou.gif)
真实价格跟我们
![](/icons/29739de.gif)
预算已经发生了变化
![](/icons/29739dou2.gif)
所有
![](/icons/29739de.gif)
原因
![](/icons/29739dou.gif)
都将影响我们
![](/icons/29739de.gif)
整个开销
执行计划稳定性能带给我们什么
Oracle存在着执行计划选择失误
![](/icons/29739de.gif)
可能
![](/icons/29739dou2.gif)
这也是我们经常遇见
![](/icons/29739de.gif)
![](/icons/29739yi.gif)
些现象
![](/icons/29739dou.gif)
比如总有人说我
![](/icons/29739de.gif)
![](/icons/29739chengxu.gif)
在测试数据库中跑
![](/icons/29739de.gif)
很好
![](/icons/29739dou.gif)
但在产品数据库上就是跑
![](/icons/29739de.gif)
很差
![](/icons/29739dou.gif)
甚至后者硬件条件比前者还好
![](/icons/29739dou.gif)
这到底是为什么?硬件资源、统计信息、参数设置都可能对执行计划产生影响
![](/icons/29739dou2.gif)
由于原因太多
![](/icons/29739dou.gif)
我们总是对未来怀着
![](/icons/29739yi.gif)
种莫名
![](/icons/29739de.gif)
惧怕
![](/icons/29739dou.gif)
我
![](/icons/29739de.gif)
产品数据库上线后到底跑
![](/icons/29739de.gif)
好不好?于是Oracle提供了
![](/icons/29739yi.gif)
种稳定执行计划
![](/icons/29739de.gif)
能力
![](/icons/29739dou.gif)
也就是把在测试环境中
![](/icons/29739de.gif)
运行良好
![](/icons/29739de.gif)
执行计划所产生
![](/icons/29739de.gif)
OUTLINES移植到产品数据库
![](/icons/29739dou.gif)
使得执行计划不会随着其他原因
![](/icons/29739de.gif)
变化而变化
![](/icons/29739dou2.gif)
那么OUTLINES是什么呢?先要介绍
![](/icons/29739yi.gif)
个内容
![](/icons/29739dou.gif)
Oracle提供了在SQL中使用HINTS来引导优化器产生我们想要
![](/icons/29739de.gif)
执行计划
![](/icons/29739de.gif)
能力
![](/icons/29739dou2.gif)
这在多表连接、复杂查询中非凡有效
![](/icons/29739dou2.gif)
HINTS
![](/icons/29739de.gif)
类型很多
![](/icons/29739dou.gif)
可以设置优化器目标(RULE、CHOOSE、FIRST_ROWS、ALL_ROWS)
![](/icons/29739dou.gif)
可以指定表连接
![](/icons/29739de.gif)
顺序
![](/icons/29739dou.gif)
可以指定使用哪个表
![](/icons/29739de.gif)
哪个索引等等
![](/icons/29739dou.gif)
可以对SQL进行很多精细
![](/icons/29739de.gif)
控制
![](/icons/29739dou2.gif)
通过这种方式产生我们想要
![](/icons/29739de.gif)
执行计划
![](/icons/29739de.gif)
这些HINTS,Oracle可以存储这些HINTS
![](/icons/29739dou.gif)
我们称的为OUTLINES
![](/icons/29739dou2.gif)
通过STORE OUTLINES可以使得我们拥有以后产生相同执行计划
![](/icons/29739de.gif)
能力
![](/icons/29739dou.gif)
也就是使我们拥有了稳定执行计划
![](/icons/29739de.gif)
能力
![](/icons/29739dou2.gif)
这里想给出
![](/icons/29739yi.gif)
个附加
![](/icons/29739de.gif)
介绍说明就是
![](/icons/29739dou.gif)
实际上
![](/icons/29739dou.gif)
我们通过工具改写SQL
![](/icons/29739dou.gif)
比如使用SQL EXPERT改写后
![](/icons/29739de.gif)
SQL
![](/icons/29739dou.gif)
这些不仅仅是加了HINTS而且文本都已经发生了变化
![](/icons/29739de.gif)
SQL
![](/icons/29739dou.gif)
也可以存储OUTLINES
![](/icons/29739dou.gif)
并可被应用到应用中
![](/icons/29739dou2.gif)
但这不是
![](/icons/29739yi.gif)
定生效
![](/icons/29739dou.gif)
我们必须测试检查是否生效
![](/icons/29739dou2.gif)
但由于就算给了
![](/icons/29739cuowu.gif)
![](/icons/29739de.gif)
OUTLINES,数据库在执行
![](/icons/29739de.gif)
时候
![](/icons/29739dou.gif)
也只是忽略过去重新生成执行计划而不会返回
![](/icons/29739cuowu.gif)
![](/icons/29739dou.gif)
所以我们才敢放心
![](/icons/29739de.gif)
这么使用
![](/icons/29739dou2.gif)
当然在Oracle文档中并没有指明可以这样做
![](/icons/29739dou.gif)
文档中只是介绍说明
![](/icons/29739dou.gif)
假如存在OUTLINES
![](/icons/29739de.gif)
同时又在SQL中加了HINTS
![](/icons/29739dou.gif)
则会使用OUTLINES而忽略HINTS
![](/icons/29739dou2.gif)
这个功能在LECCO将发布
![](/icons/29739de.gif)
产品中会使用这
![](/icons/29739yi.gif)
功能
![](/icons/29739dou.gif)
这样可以将SQL EXPERT
![](/icons/29739de.gif)
改写SQL
![](/icons/29739de.gif)
能力和稳定执行计划
![](/icons/29739de.gif)
能力结合起来
![](/icons/29739dou.gif)
那么我们就对不能更改源代码
![](/icons/29739de.gif)
应用具有了相当强大
![](/icons/29739de.gif)
SQL优化能力
![](/icons/29739dou2.gif)
也许我们会有疑问
![](/icons/29739dou.gif)
假如稳定了执行计划
![](/icons/29739dou.gif)
那还搜集统计信息干吗?这是
![](/icons/29739yinwei.gif)
几个原因造成
![](/icons/29739de.gif)
![](/icons/29739dou.gif)
首先
![](/icons/29739dou.gif)
现在
![](/icons/29739de.gif)
执行计划对于未来发生了变化
![](/icons/29739de.gif)
数据未必就是合适
![](/icons/29739de.gif)
![](/icons/29739dou.gif)
存在着当前
![](/icons/29739de.gif)
执行计划不满足未来数据
![](/icons/29739de.gif)
变化后
![](/icons/29739de.gif)
效率
![](/icons/29739dou.gif)
而新
![](/icons/29739de.gif)
统计信息
![](/icons/29739de.gif)
情况下所产生
![](/icons/29739de.gif)
执行计划也并不是全部都合理
![](/icons/29739de.gif)
![](/icons/29739dou2.gif)
那这个时候
![](/icons/29739dou.gif)
我们可以采用新搜集
![](/icons/29739de.gif)
统计信息
![](/icons/29739dou.gif)
但是却对新统计信息下不良
![](/icons/29739de.gif)
执行计划采用Oracle提供
![](/icons/29739de.gif)
执行计划稳定性这个能力固定执行计划
![](/icons/29739dou.gif)
这样结合起来我们可以建立满足
![](/icons/29739de.gif)
高效
![](/icons/29739de.gif)
数据库运行环境
![](/icons/29739dou2.gif)
我们还需要关注
![](/icons/29739de.gif)
![](/icons/29739yi.gif)
个东西
![](/icons/29739dou.gif)
Oracle提供
![](/icons/29739de.gif)
dbms_stats包除了具有搜集统计信息
![](/icons/29739de.gif)
能力
![](/icons/29739dou.gif)
还具有把数据库中统计信息(statistics)export/import
![](/icons/29739de.gif)
能力
![](/icons/29739dou.gif)
还具有只搜集统计信息而使得统计信息不应用于数据库
![](/icons/29739de.gif)
能力(把统计信息搜集到
![](/icons/29739yi.gif)
个特定
![](/icons/29739de.gif)
表中而不是立即生效)
![](/icons/29739dou.gif)
在这个基础上我们就可以把统计信息export出来再import到
![](/icons/29739yi.gif)
个测试环境中
![](/icons/29739dou.gif)
再运行我们
![](/icons/29739de.gif)
应用
![](/icons/29739dou.gif)
在测试环境中我们观察最新
![](/icons/29739de.gif)
统计信息会导致哪些执行计划发生变化(DB EXPERT
![](/icons/29739de.gif)
Plan Version Tracer是模拟区别环境并自动检查区别环境中执行计划变化
![](/icons/29739de.gif)
工具)
![](/icons/29739dou.gif)
是变好了还是变差了
![](/icons/29739dou2.gif)
我们可以把变差
![](/icons/29739de.gif)
这
![](/icons/29739yi.gif)
部分在测试环境中使用h
![](/icons/29739int.gif)
s或者利用工具(SQL EXPERT是在重写SQL这
![](/icons/29739yi.gif)
领域目前最强有力
![](/icons/29739de.gif)
工具)产生良好
![](/icons/29739de.gif)
执行计划
![](/icons/29739de.gif)
SQL
![](/icons/29739dou.gif)
利用这些SQL可以产生OUTLINES,然后在产品数据库应用最新
![](/icons/29739de.gif)
统计信息
![](/icons/29739de.gif)
同时移植进这些OUTLINES
![](/icons/29739dou2.gif)
最后说
![](/icons/29739yi.gif)
下我们不得不使用执行计划稳定性能力
![](/icons/29739de.gif)
场合
![](/icons/29739dou2.gif)
我们假定Oracle
![](/icons/29739de.gif)
优化器
![](/icons/29739de.gif)
选择都是准确
![](/icons/29739de.gif)
![](/icons/29739dou.gif)
但是优化器选择
![](/icons/29739de.gif)
基础就是我们
![](/icons/29739de.gif)
SQL,这些SQL才从根本上决定了运行效率
![](/icons/29739dou.gif)
这是更重要
![](/icons/29739de.gif)
![](/icons/29739yi.gif)
个优化
![](/icons/29739de.gif)
环节
![](/icons/29739dou2.gif)
SQL是基础(当然数据库
![](/icons/29739de.gif)
设计是基础
![](/icons/29739de.gif)
基础)
![](/icons/29739dou.gif)
![](/icons/29739yi.gif)
个SQL写
![](/icons/29739de.gif)
好不好
![](/icons/29739dou.gif)
就相当于我们同样是要想去英国
![](/icons/29739dou.gif)
但是我
![](/icons/29739de.gif)
起点在珠海
![](/icons/29739dou.gif)
你
![](/icons/29739de.gif)
起点却在西藏
![](/icons/29739de.gif)
最边缘偏僻
![](/icons/29739de.gif)
![](/icons/29739yi.gif)
个地方
![](/icons/29739dou.gif)
那不管你做怎样
![](/icons/29739de.gif)
最优路线选择
![](/icons/29739dou.gif)
你都不如我在珠海去英国所花费
![](/icons/29739de.gif)
代价小