Oracle优化04-Optimizer优化器

简介: Oracle优化04-Optimizer优化器

思维导图


20161226210657790.png

Optimizer概述


Oracle数据库中的优化器是SQL分析和执行的优化工具,它负责制定SQL的执行计划,也就是它负责保证SQL执行效率最高.


比如优化器决定Oracle是以什么样的方式方式访问数据,是全表扫描(Full Table Scan) 、索引范围扫描(Index Range Scan)还是全索引快速扫描(INDEX Fast Full Scan, INDEX_FFS).


对于多表关联查询,它负责确定表之间以一种什么样的方式来关联,比如Hash Join 还是 NESTED LOOPS或者是MERGE JOIN。


这些因素直接决定着SQL的执行效率,所以优化器是SQL执行的核心,它做出的执行计划的好坏,直接决定了SQL的执行效率。


Oracle的优化器有两种

  • RBO 基于规则的优化器
  • CBO 基于代价的优化器

从ORACLE10G开始,RBO已经被弃用(但是我们依然可以通过HINT的方式使用它)。


RBO(Rule Based Optimizer)

RBO概述


在8i之前,ORACLE使用RBO(Rule Based Optimizer 基于规则的优化器)优化器。


它的执行机制非常简单,就是在优化器里面嵌入各种若干规则,执行的SQL符合那种规则,则按照对应的规则制定出相应的执行计划。


比如表上有索引,如果谓词上有索引的列存在,则ORACLE会选择索引,否则全表扫描。


又比如两个表关联的时候,按照表在SQL中的位置来决定哪个是驱动表,哪个是被驱动表。


RBO选择执行计划的优先级列表如下:

20161227080045607.png


其中,排名越靠前,Oracle认为效率越高。例如:按索引访问的效率肯定高于全表扫描,多字段复合索引的效率高于单字段索引,等等。可见,RBO策略其实很简单。而且在SQL语句存在多个索引时,由于无法判别各个索引的可选性,RBO的分析过程与语句的语法相关,如where条件顺序,或者随机选取。


通俗地讲,RBO就是不关心被访问对象的实际数据分布情况、索引效率等,仅凭想象去决定应该如何去访问数据库。可见,RBO是一种非常粗放型的优化器。


案例说明


思考:表中有索引,数据就必须选择索引吗?

首先创建一个数据分部非常不均匀的表

Connected to Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 
Connected as xxx@xgj
##建表
SQL> create table t as select 1 id ,object_name from dba_objects ;
Table created
##更新一条数据
SQL> update t set id=99 where rownum=1;
1 row updated
SQL> commit;
Commit complete
##创建索引
SQL> create index  idx_t on t(id);
Index created
SQL> select  id,count(1) from t group by id ;
        ID   COUNT(1)
---------- ----------
         1      35251
        99          1


现在表中的数据 id =1 的 35251条记录, id =99 的 1条记录。

如果发出两条这样的SQL

SQL> select * from t where t.id=1;
SQL> select * from t where t.id=99;


在RBO的年代里,执行计划是这样的


select * from t where t.id=99

##首先执行SQL
SQL>  select  /*+ rule  */  * from t where id=99;
         ID OBJECT_NAME
---------- -------------------
        99 ICOL$
##查找SQL对应的SQL_ID        
SQL>  SELECT sql_id, child_number, a.* from v$sql a  where a.SQL_TEXT  like '%select  /*+ rule  */  * from t where id=99%';
SQL_ID CHILD_NUMBER
------------- ------------
axszbzuk341sw            0
##通过DBMS_XPLAN.display_cursor查询执行计划
SQL>  select * from table(DBMS_XPLAN.display_cursor('axszbzuk341sw',0));  

20161227202332462.png


select * from t where t.id=1;

同样的方式我们得出执行计划

20161227202653775.png


使用 hint /+ rule /的方式强制让ORACLE使用RBO优化器来产生执行计划,结果非常令人失望,不出所料,ORACLE在ID字段有索引的情况下,毫无悬念的全部选择了索引。


实际上我们知道,对于id=1,几乎所有的数据全都符合谓词条件,选择索引只能增加额外的开销(因为ORACLE首先要访问索引数据块,在索引上找到了相应的键值,然后按照键值上的rowid 再去访问表中相应的数据)。


既然我们几乎要访问所有表中的数据,那么全表扫描自然是最优的选择,很遗憾RBO做出了错误的选择。


CBO(Cost Based Optimizer)

CBO概述


从8i开始,ORACLE引入了CBO,它的思路是让ORACLE获取所有执行计划相关的信息,通过对这些信息进行计算分析,最后得出一个代价最小的执行计划作为最终的执行计划。


CBO基于成本的优化器,其特点如下。


(1)依赖于数据对象的统计信息,例如表的记录数、消耗的数据块数、索引的可选性(不同的记录值数量)等。


(2)使用精细的成本模型来评估SQL语句的执行性能。例如CPU消耗、逻辑读写(内存消耗)、I/O读写、网络传输量的消耗分析等。


(3)语句优化的整体质量高于RBO。


(4)Oracle的大量新特性只能在CBO下运行。例如:HASH_JOIN操作、Bitmap索引、物化视图等。


可见相比RBO,CBO是一种更加精确而有效的优化器。


案例说明


同样是使用RBO中的案例,我们来看下CBO的表现。

##减少影响,先清空shared_Pool中的数据
SQL> alter system flush shared_pool;
System altered
##对表做一次分析(cascade => true ,索引也会被一同分析)
SQL> exec dbms_stats.gather_table_stats(user,'t',cascade => true);
PL/SQL procedure successfully completed
SQL>select  /*+  all_rows */  *  from t where t.id=1;
SQL>SELECT sql_id, child_number, a.* from v$sql a  where a.SQL_TEXT  like '%select  /*+  all_rows */  *  from t where t.id=1%';
SQL>select * from table(DBMS_XPLAN.display_cursor('d6adsf8y8c6mu',0));  

20161227212157771.png


SQL>select  /*+  all_rows */  *  from t where t.id=99;
SQL>SELECT sql_id, child_number, a.* from v$sql a  where a.SQL_TEXT  like '%select  /*+  all_rows */  *  from t where t.id=99%';
SQL>select * from table(DBMS_XPLAN.display_cursor('8w6r8hzrkunnu',0));  

20161227212315190.png

可以看出 CBO表现的非常完美,

  • id=1 全表扫描 TABLE ACCESS FULL
  • id=99 索引扫描 INDEX RANGE SCAN


因为这样的选择代价是最小的,为了验证CBO选择的正确性,我们分别让这两条SQL各自做一次全表扫描和和选择索引。


全表扫描

select * from t where id=1;
Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
SQL> alter session set tracefile_identifier='mytest';
Session altered.
SQL> alter session set sql_trace=true;
Session altered.
SQL> select * from t where id=1;
.......
35251 rows selected.
SQL> alter session set sql_trace=false;
Session altered.
SQL> select name, value from v$parameter where name = 'user_dump_dest';
NAME                 VALUE
------------        ----------
user_dump_dest    /oracle/diag/rdbms/cc/cc/trace

ORACLE主机上查看

oracle@entel1:[/oracle/diag/rdbms/cc/cc/trace]$ls *mytest*
cc_ora_8752_mytest.trc  cc_ora_8752_mytest.trm


tkprof 工具汇总分析

oracle@entel1:[/oracle/diag/rdbms/cc/cc/trace]$tkprof cc_ora_8752_mytest.trc mytest.txt sys=no explain=xxx/xgj
TKPROF: Release 11.2.0.4.0 - Development on Tue Dec 27 23:24:32 2016
Copyright (c) 1982, 2011, Oracle and/or its affiliates.  All rights reserved.


截取部分关键信息:


20161227233107492.png


索引扫描

select /*+  index(t  IDX_T) */* from t where id=1;


SQL> alter session set tracefile_identifier='index_scan';
Session altered
SQL> alter session set sql_trace=true;
Session altered
SQL> select /*+  index(t  IDX_T) */* from t where id=1;
.......
35251 rows selected.
SQL> alter session set sql_trace=false;
Session altered.

获取到trc原文件后,使用tkprof分析汇总

oracle@entel1:[/oracle/diag/rdbms/cc/cc/trace]$tkprof cc_ora_8709_index_scan.trc index_scan.txt explain=xxx/xgj sys=no
TKPROF: Release 11.2.0.4.0 - Development on Tue Dec 27 23:35:57 2016
Copyright (c) 1982, 2011, Oracle and/or its affiliates.  All rights reserved.


截取关键信息:


20161227234438550.png


比较:

select * from t where id=1;--全表扫描了2470个数据块
select /*+  index(t  IDX_T) */* from t where id=1;--索引扫描了4892个数据块


显然这种情况下,CBO的选择是正确的,使用全表扫描的效率更高,速度更快。


同样的方式我们比较下

select * from t where id=99;
select /*+ full(t) */  * from t where id=99;


select * from t where id=99;

Connected to Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 
Connected as xxx@xgj
SQL> alter session set tracefile_identifier='full_scan';
Session altered
SQL> alter session set sql_trace=true;
Session altered
SQL> select * from t where id=99;
        ID OBJECT_NAME
---------- ------------------------
        99 ICOL$
SQL> alter session set sql_trace=false;
Session altered
SQL> 


在oracle主机上使用tkprof分析汇总

[root@entel1 ~]# su - oracle 
oracle@entel1:[/oracle]$cd  /oracle/diag/rdbms/cc/cc/trace
oracle@entel1:[/oracle/diag/rdbms/cc/cc/trace]$ls *full*
cc_ora_16197_full_scan.trc  cc_ora_16197_full_scan.trm
oracle@entel1:[/oracle/diag/rdbms/cc/cc/trace]$tkprof cc_ora_16197_full_scan.trc full_scan.txt explain=zmc/smart sys=no
TKPROF: Release 11.2.0.4.0 - Development on Wed Dec 28 23:41:24 2016
Copyright (c) 1982, 2011, Oracle and/or its affiliates.  All rights reserved.
oracle@entel1:[/oracle/diag/rdbms/cc/cc/trace]$cat full_scan.txt


部分关键信息如下:


20161228234457046.png


采用同样的方式我们分析下

select /+ full(t) / * from t where id=99;

20161228234857752.png


通过对比上述的执行计划,选择索引的只扫描了3个数据块,而全表扫描则扫描了135个数据块,再此证明了CBO选择的正确性。


从ORACLE10g开始,ORACLE已经彻底丢弃了RBO。

即使在表索引没有被分析的时候,Oracle依然会使用CBO,此时,ORACLE会使用一种叫做动态采样的技术,在分析SQL的时候,动态的搜集表、索引上的一些数据块,使用这些数据块的信息及字典表中关于这些对象的信息来计算出执行计划的代价,从而挑选出最优的执行计划。


举例说明:

SQL> drop table t ;
Table dropped
SQL> create table t (x int ,y varchar2(2000));
Table created
SQL> insert into t select object_id ,object_name from dba_objects;
35254 rows inserted
SQL> commit;
Commit complete
SQL> create index idx_t on t(x);
Index created
SQL>  alter session set tracefile_identifier='xgj_test';
Session altered
SQL> alter session set sql_trace=true;
Session altered
SQL> select count(1) from t where x<100;
  COUNT(1)
----------
        98
SQL> alter session set sql_trace=false;
Session altered
##查看SQL_ID
SQL> SELECT sql_id, child_number, a.* from v$sql a  where a.SQL_TEXT  like '% select count(1) from t where x<100%';
##根据SQL_ID查看执行计划
SQL>select * from table(DBMS_XPLAN.display_cursor('5yk2jzsm7cpnh',0));  


如下:


20161229000810189.png

可以看到,当表没有做分析的时候,ORACLE 会采用动态采样来搜集统计信息,这个动作只发生在SQL执行的第一次,即硬分析阶段使用,后续的软分析将不再使用动态采样,直接使用第一次SQL硬分析时生成的执行计划。


同时我们也采集了SQL_TRACE的信息,我们使用tkprof分析汇总后来看下

TKPROF: Release 11.2.0.4.0 - Development on Thu Dec 29 00:00:16 2016
Copyright (c) 1982, 2011, Oracle and/or its affiliates.  All rights reserved.
Trace file: cc_ora_16197_xgj_test.trc
Sort options: default
********************************************************************************
count    = number of times OCI procedure was executed
cpu      = cpu time in seconds executing 
elapsed  = elapsed time in seconds executing
disk     = number of physical reads of buffers from disk
query    = number of buffers gotten for consistent read
current  = number of buffers gotten in current mode (usually for update)
rows     = number of rows processed by the fetch or execute call
********************************************************************************
SQL ID: 96ckjhxq51nwk Plan Hash: 0
alter session set sql_trace=true
call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        0      0.00       0.00          0          0          0           0
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        2      0.00       0.00          0          0          0           0
Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 38  (ZMC)
********************************************************************************
SQL ID: gjz1ws759xbbp Plan Hash: 1807638002
SELECT /* OPT_DYN_SAMP */ /*+ ALL_ROWS IGNORE_WHERE_CLAUSE 
  NO_PARALLEL(SAMPLESUB) opt_param('parallel_execution_enabled', 'false') 
  NO_PARALLEL_INDEX(SAMPLESUB) NO_SQL_TUNE */ NVL(SUM(C1),:"SYS_B_0"), 
  NVL(SUM(C2),:"SYS_B_1") 
FROM
 (SELECT /*+ IGNORE_WHERE_CLAUSE NO_PARALLEL("T") FULL("T") 
  NO_PARALLEL_INDEX("T") */ :"SYS_B_2" AS C1, CASE WHEN "T"."X"<:"SYS_B_3" 
  THEN :"SYS_B_4" ELSE :"SYS_B_5" END AS C2 FROM "ZMC"."T" SAMPLE BLOCK 
  (:"SYS_B_6" , :"SYS_B_7") SEED (:"SYS_B_8") "T") SAMPLESUB
call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        1      0.00       0.00          0         69          0           1
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        3      0.00       0.00          0         69          0           1
Misses in library cache during parse: 1
Misses in library cache during execute: 1
Optimizer mode: ALL_ROWS
Parsing user id: 38  (ZMC)   (recursive depth: 1)
Number of plan statistics captured: 1
Rows (1st) Rows (avg) Rows (max)  Row Source Operation
---------- ---------- ----------  ---------------------------------------------------
         1          1          1  SORT AGGREGATE (cr=69 pr=0 pw=0 time=3244 us)
     11794      11794      11794   TABLE ACCESS SAMPLE T (cr=69 pr=0 pw=0 time=1955 us cost=2 size=525 card=21)
error during execute of EXPLAIN PLAN statement
ORA-00907: missing right parenthesis
parse error offset: 493
********************************************************************************
SQL ID: 1pr6146q5bau5 Plan Hash: 3306840186
SELECT /* OPT_DYN_SAMP */ /*+ ALL_ROWS opt_param('parallel_execution_enabled',
   'false') NO_PARALLEL(SAMPLESUB) NO_PARALLEL_INDEX(SAMPLESUB) NO_SQL_TUNE 
  */ NVL(SUM(C1),:"SYS_B_0"), NVL(SUM(C2),:"SYS_B_1"), NVL(SUM(C3),:"SYS_B_2")
FROM
 (SELECT /*+ NO_PARALLEL("T") INDEX("T" IDX_T) NO_PARALLEL_INDEX("T") */ 
  :"SYS_B_3" AS C1, :"SYS_B_4" AS C2, :"SYS_B_5" AS C3  FROM "ZMC"."T" "T" 
  WHERE "T"."X"<:"SYS_B_6" AND ROWNUM <= :"SYS_B_7") SAMPLESUB
call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        1      0.00       0.00          4          2          0           1
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        3      0.00       0.00          4          2          0           1
Misses in library cache during parse: 1
Misses in library cache during execute: 1
Optimizer mode: ALL_ROWS
Parsing user id: 38  (ZMC)   (recursive depth: 1)
Number of plan statistics captured: 1
Rows (1st) Rows (avg) Rows (max)  Row Source Operation
---------- ---------- ----------  ---------------------------------------------------
         1          1          1  SORT AGGREGATE (cr=2 pr=4 pw=0 time=301 us)
        98         98         98   VIEW  (cr=2 pr=4 pw=0 time=318 us cost=33 size=390 card=10)
        98         98         98    COUNT STOPKEY (cr=2 pr=4 pw=0 time=216 us)
        98         98         98     INDEX RANGE SCAN IDX_T (cr=2 pr=4 pw=0 time=213 us cost=33 size=429 card=33)(object id 79508)
Rows     Execution Plan
-------  ---------------------------------------------------
      0  SELECT STATEMENT   MODE: HINT: ALL_ROWS
      1   SORT (AGGREGATE)
     98    VIEW
     98     COUNT (STOPKEY)
     98      INDEX   MODE: ANALYZED (RANGE SCAN) OF 'IDX_T' (INDEX)
上面的信息就是ORACLE做动态采样的SQL语句。
********************************************************************************
SQL ID: 5yk2jzsm7cpnh Plan Hash: 1500240790
select count(1) 
from
 t where x<100
call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          2          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        1      0.00       0.00          0          2          0           1
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        3      0.00       0.00          0          4          0           1
Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 38  (ZMC)
Number of plan statistics captured: 1
Rows (1st) Rows (avg) Rows (max)  Row Source Operation
---------- ---------- ----------  ---------------------------------------------------
         1          1          1  SORT AGGREGATE (cr=2 pr=0 pw=0 time=43 us)
        98         98         98   INDEX RANGE SCAN IDX_T (cr=2 pr=0 pw=0 time=110 us cost=2 size=1274 card=98)(object id 79508)
Rows     Execution Plan
-------  ---------------------------------------------------
      0  SELECT STATEMENT   MODE: ALL_ROWS
      1   SORT (AGGREGATE)
     98    INDEX   MODE: ANALYZED (RANGE SCAN) OF 'IDX_T' (INDEX)
上面是SQL本身执行时的实际信息
********************************************************************************
OVERALL TOTALS FOR ALL NON-RECURSIVE STATEMENTS
call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        2      0.00       0.00          0          2          0           0
Execute      2      0.00       0.00          0          0          0           0
Fetch        1      0.00       0.00          0          2          0           1
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        5      0.00       0.00          0          4          0           1
Misses in library cache during parse: 2
OVERALL TOTALS FOR ALL RECURSIVE STATEMENTS
call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        2      0.00       0.00          0          0          0           0
Execute      2      0.00       0.00          0          0          0           0
Fetch        2      0.00       0.00          4         71          0           2
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        6      0.00       0.00          4         71          0           2
Misses in library cache during parse: 2
Misses in library cache during execute: 2
    4  user  SQL statements in session.
    0  internal SQL statements in session.
    4  SQL statements in session.
    2  statements EXPLAINed in this session.
********************************************************************************
Trace file: cc_ora_16197_xgj_test.trc
Trace file compatibility: 11.1.0.7
Sort options: default
       1  session in tracefile.
       4  user  SQL statements in trace file.
       0  internal SQL statements in trace file.
       4  SQL statements in trace file.
       4  unique SQL statements in trace file.
       2  SQL statements EXPLAINed using schema:
           ZMC.prof$plan_table
             Default table was used.
             Table was created.
             Table was dropped.
      97  lines in trace file.
      37  elapsed seconds in trace file.
相关文章
|
4月前
|
达摩院 开发者 容器
「达摩院MindOpt」优化形状切割问题(MILP)
在制造业,高效地利用材料不仅是节约成本的重要环节,也是可持续发展的关键因素。无论是在金属加工、家具制造还是纺织品生产中,原材料的有效利用都直接影响了整体效率和环境影响。
「达摩院MindOpt」优化形状切割问题(MILP)
|
5月前
|
人工智能 自然语言处理 达摩院
MindOpt 云上建模求解平台:多求解器协同优化
数学规划是一种数学优化方法,主要是寻找变量的取值在特定的约束情况下,使我们的决策目标得到一个最大或者最小值的决策。
|
21天前
|
存储 Oracle 数据管理
Oracle 12c的自动数据优化(ADO)与热图:数据管理的“瘦身”与“透视”艺术
【4月更文挑战第19天】Oracle 12c的ADO和热图技术革新数据管理。ADO智能清理无用数据,优化存储,提升查询速度,实现数据&quot;瘦身&quot;;热图则以直观的视觉表示展示数据分布和状态,助力识别性能瓶颈,犹如数据的&quot;透视&quot;工具。这两项技术结合,强化数据管理,为企业业务发展保驾护航。
|
4月前
|
存储 达摩院 调度
「达摩院MindOpt」优化FlowShop流水线作业排班问题
在企业在面临大量多样化的生产任务时,如何合理地安排流水线作业以提高生产效率及确保交货期成为了一个重要的问题。
「达摩院MindOpt」优化FlowShop流水线作业排班问题
|
10月前
|
达摩院 调度
使用达摩院MindOpt优化交通调度_最大化通行量—线性规划问题
在数学规划中,网络流问题是指一类基于网络模型的流量分配问题。网络流问题的目标是在网络中分配资源,使得网络的流量满足一定的限制条件,并且使得某些目标函数最小或最大化。网络流问题通常涉及一个有向图,图中每个节点表示一个资源,每条边表示资源之间的关系。边上有一个容量值,表示该边上最多可以流动的资源数量。流量从源节点开始流出,经过一系列中间节点,最终到达汇节点。在这个过程中,需要遵守一定的流量守恒和容量限制条件。
|
6月前
|
存储 SQL Oracle
Oracle优化避免索引失效
Oracle优化避免索引失效
200 0
|
6月前
|
API Python
MindOpt V1.0优化种植计划问题,新的建模方法
种植计划是指农业生产中针对不同农作物的种植时间、面积和种植方式等方面的规划安排。根据具体情况进行合理的规划和安排,以实现农作物的高产、优质和可持续发展。
MindOpt V1.0优化种植计划问题,新的建模方法
|
6月前
|
SQL Oracle 关系型数据库
Oracle优化问题
Oracle优化问题
|
8月前
|
SQL Oracle 关系型数据库
Oracle数据库优化的总结及优化方法
Oracle数据库优化的总结及优化方法
57 0
|
10月前
|
达摩院 供应链 JavaScript
网络流:优化仓储物流调度问题-达摩院MindOpt
仓储物流调度是指在物流供应链中,对仓储和运输(运输路线、成本)进行协调和安排的过程。主要包含物流计划、运输调度、运发管理、库存管理等重要环节。随着网络、电商行业的迅速发展,仓储物流调度对于企业来说也非常重要,优秀的调度方案可以帮助降低库存成本、物流配送的效率、成本等等等,从而给企业带来降本增效。
网络流:优化仓储物流调度问题-达摩院MindOpt