思维导图


系列文章

Oracle-SQL Explain Plan解读

概述

如果要分析某条SQL的性能问题,通常来讲,我们首先要看SQL的执行计划,看看SQL的每一步执行计划是否存在问题。

如果某一条SQL平常执行的都很好,却有一天突然性能很差,如果排除了系统资源和阻塞的原因,那么基本上可以判断是执行计划出现了问题。

看懂执行计划变成了SQL优化(其实在大多数的情况下,SQL优化指的是SQL的性能问题定位)的先决条件。

在讨论SQL执行计划之前,我们需要知道执行计划当中一个非常重要的概念–Cardinality基数。


Cardinality基数

在我们看执行计划的每一步操作的时候,当前操作的Cardinality值表示CBO预期从一个行源(row source)返回的记录数。

一个行源可能是一个表、一个索引、也可能是一个子查询。

比如:


当CBO无法准确的获取到Cardinality时,将会发生什么?

在执行计划中, card 就是Cardinality的缩写,它表示CBO估算当前操作预期获取的记录数。

Cardinality的值对于CBO做出正确的执行计划来说至关重要,如果CBO获得的Cardinality值不够准确(通常是没有做分析或者分析数据过旧导致),在执行成本计算上就会出现偏差,从而导致CBO错误的制定出执行计划。

下面演示下当CBO无法准确的获取到Cardinality时,将会发生什么?

创建一个数据分布非常不均匀的表T

Connected to Oracle Database 11g Enterprise Edition Release 11.2.0.4.0
Connected as xxx@xgjSQL> drop table t;Table droppedSQL> create table t as select 1 id , object_name from dba_objects;Table createdSQL> update t set t.id=99 where rownum=1;1 row updatedSQL> commit;Commit completeSQL> create index t_ind on t(id);Index createdSQL> select id ,count(*) from t group by id ;ID   COUNT(*)
---------- ----------1      3525199          1

查看执行计划

##执行SQL
SQL> select /*+  dynamic_sampling(t 0)  */  * from t where id=1;SQL> SELECT sql_id, child_number, a.* from v$sql a  where a.SQL_TEXT  like '%select /*+  dynamic_sampling(t 0)  */  * from t where id=1%';SQL> select * from table(DBMS_XPLAN.display_cursor('bb22rwn4604yj',0));  

说明:

  • /+ dynamic_sampling(t 0) / :目的是让CBO无法通过动态采样获取表中实际数据的情况,此时CBO只能根据数据字典中标T的非常有限的信息(比如表的extends数量,数据块的数量)来猜测表中的数据
  • 从结果中可以看出,CBO猜测出id=1的数据为118条,而与实际上的数量 35251,相差甚远,所以CBO选择了索引而不是全表扫描

让我们看下实际的执行情况:

select  * from t where id=1;SELECT sql_id, child_number, a.* from v$sql a  where a.SQL_TEXT  like '%select  * from t where id=1%';select * from table(DBMS_XPLAN.display_cursor('3k9f6qpq9sbmn',0));  

通过动态采样(10g及以上版本,如果表没有做过分析,ORACLE会自动通过动态采样的方式来收集分析数据),CBO估算出来的表中数量为39257 (Cardinality) 和实际的数量 35251非常接近,CBO判断ID=1的数据基本上等同于表中的数据,所以选择了全表扫描。


下面我们做一下表和索引分析


##先查看下
SQL>   select num_rows, avg_row_len, blocks, last_analyzed  from user_tables  where table_name = 'T';NUM_ROWS AVG_ROW_LEN     BLOCKS LAST_ANALYZED
---------- ----------- ---------- -------------SQL> select blevel,leaf_blocks,distinct_keys,last_analyzed from user_indexes where table_name='T';BLEVEL LEAF_BLOCKS DISTINCT_KEYS LAST_ANALYZED
---------- ----------- ------------- -------------##对表和索引做分析
SQL>  exec   dbms_stats.gather_table_stats(user,'t',cascade => true);PL/SQL procedure successfully completed##重新查询信息 SQL> select num_rows, avg_row_len, blocks, last_analyzed  from user_tables  where table_name = 'T';NUM_ROWS AVG_ROW_LEN     BLOCKS LAST_ANALYZED
---------- ----------- ---------- -------------35252          22        144 2016-12-30 0:SQL>  select blevel,leaf_blocks,distinct_keys,last_analyzed from user_indexes where table_name='T';BLEVEL LEAF_BLOCKS DISTINCT_KEYS LAST_ANALYZED
---------- ----------- ------------- -------------1          69             2 2016-12-30 0:SQL> 

查看执行计划

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

通过对表的分析,CBO就可以获取到T表和索引的充足的信息。

上面的截图,CBO从分析数据中,获取到了id=99的数据6 rows , 所以选择了索引。

我们更新下数据

SQL>update t set id=99;
SQL>commit;

将id 全部更新为99 , 因为没有对表进行分析,所以CBO知道的信息还是旧的,重新查询 下 我们可以看到 CBO依然认为表T中的数据很少,依然选择的是索引。

SQL> exec   dbms_stats.gather_table_stats(user,'t',cascade => true);PL/SQL procedure successfully completed
## 如果使用 DBMS_XPLAN.display_cursor 查询执行计划的话,需要清空shared_pool ,或者换个SQL(目的是不匹配到shared_pool中的缓存)
SQL> alter system flush shared_pool;System altered##重新执行SQL
SQL> select  *  from t a  where a.id=99;##获取SQL_ID等
SQL> SELECT sql_id, child_number, a.* from v$sql a  where a.SQL_TEXT  like '%select  *  from t a  where a.id=99%';##查看执行计划
SQL> select * from table(DBMS_XPLAN.display_cursor('fa0r3kc8y5239',0));  

重新对表分析后,CBO获取了正确的Cardinality值。T表中id=99的数据3w+,所以全表扫描是最佳执行计划。



Cardinality是如何影响多表查询的?

在多表关联查询或者SQL中有子查询时,每个关联表或者子查询的Cardinality的值对主查询的影响非常大,甚至可以说,CBO就是依赖于各个关联表或者子查询Cardinality值来计算出最后的执行计划。

对于多表查询,CBO使用每个关联表返回的行数(Cardinality)决定使用设么样的方式来做表关联(比如Nested loops ,sm 或者 hash join),

对于子查询,它的Cardinality将决定子查询是使用索引还是使用全表扫描的方式访问数据。

举例说明:

数据如下:

Connected to Oracle Database 11g Enterprise Edition Release 11.2.0.4.0
Connected as xxx@xgjSQL> create table t1 (id  int , name varchar2(1000));Table createdSQL> create table t2 (id  int , name varchar2(1000));Table createdSQL> create index  ind_t1 on t1(id);Index createdSQL> create index  ind_t2 on t2(id);Index createdSQL> create index ind_t2_name on t2(name);Index createdSQL> insert into t1 select object_id ,object_name from dba_objects ;35258 rows insertedSQL> commit;Commit completeSQL> insert into t2 values(1,'XGJ');1 row insertedSQL> commit;Commit complete##仅对t1的表和索引进行分析
SQL> exec dbms_stats.gather_table_stats(user,'t1',cascade => true,method_opt => 'for all indexed columns');PL/SQL procedure successfully completedSQL> select * from t1 where id in (select /*+  dynamic_sampling(t2 0)  cardinality(t2 10000) */   id  from t2  where name = 'XGJ');ID            NAME
------------- ----------------------SQL>select * from v$sql a where a.SQL_TEXT
like '% /*+  dynamic_sampling(t2 0)  cardinality(t2 10000) */  %';##DBMS_XPLAN.display_cursor中查看执行计划
SQL>select * from table(dbms_xplan.display_cursor('2c7hx20a3bhxx',0));

解析:

我们发出的子查询的SQL,同时使用了hint

/*+  dynamic_sampling(t2 0)  cardinality(t2 10000) */
  • dynamic_sampling(t2 0):禁止动态采用
  • cardinality(t2 10000):告诉CBO从T2表中取10000条记录

通过这种方式,我们模拟子查询中返回的结果数,同时为了让CBO完全依赖这个信息生成执行计划,我们禁止了子查询使用动态采样(dynamic_sampleing 设置为0)。

可以看到,当CBO得到来自于子查询的结果集(Rows )的记录为10000条时,采用了hash join semi的执行计划,hash join通常适用于两张关联的表都比较大的时候


如果我们把子查询的结果集变得很小会怎样呢?

来看我们下面的栗子:

SQL> select * from t1 where id in (select /*+  dynamic_sampling(t2 0)  cardinality(t2  1) */   id  from t2  where name = 'XGJ');ID       NAME
------ ----------

同样的 我们来看下执行计划:

我们在查询中将子查询的返回值设置为1,即

Cardinality(t2,1)

此时CBO选择了两个表通过nested loop join 进行关联的执行计划,因为子查询只有1条记录,这个时候CBO会选择最适合择偶张情况的netsted loops join关联方式。


从这个试验中我们可以得到如下结论:

子查询的Cardinality的值,直接影响了主查询的执行计划,如果CBO对子查询的Cardinality判断有误,那么饿主查询的执行计划很有可能是错误的。


再看量表关联的查询情况,还是用上面的栗子

##上面执行后 user_index有值了...清掉先
select num_rows, avg_row_len, blocks, last_analyzed  from user_tables  where table_name = 'T2';select blevel,leaf_blocks,distinct_keys,last_analyzed from user_indexes where table_name='T2';SQL> analyze table t2 delete statistic2  ;Table analyzedSQL> alter system flush shared_pool;System alteredSQL> select /*+  dynamic_sampling(t2 0)  cardinality(t2 10000) */  * from t1,t2 where t1.id=t2.id;....省略输出

查看执行计划

这个栗子中,CBO认为T2关联的数据足够多,而且T1又足够大,所以在这种情况下,hash join 是最合适的。

SQL>select /*+  dynamic_sampling(t2 0)  cardinality(t2  1) */  * from t1,t2 where t1.id=t2.id;....省略输出

查看执行计划:

这时候因为T2给CBO提供的信息,只有1条记录做关联查询,所以CBO选择了nested loop join .

总结

以上的例子主要说明Cardinality对CBO生成执行计划的影响,所以我们在看多表查询的时候,一定要注意每个操作返回的Cardinality的值,如果这个值明显的不对,那么很有可能操作的表的分析数据出了问题,或者没有分析。

比如在上面的栗子中,我们确切的知道T2表的数据很小,而在执行计划中却显示10000条,这显然不对了,这个时候叫就要检查问题所在,看看T2表是不是曾经有很多数据,删除之后没有做重新分析等等。


SQL的执行计划

如果一条SQL的性能出现了问题,首先应该看一下它的执行计划,以便确定(或者猜测)问题的所在。

生成SQL的执行计划时Oracle在对SQL做硬分析时的一个非常重要的步骤,它制定出一个方案告诉Oracle在执行这条SQL时以什么样的方式访问数据: 索引扫描? 全表扫描? , 是hash join 还是 netsted loops join 等。


如何得到一个SQL的执行计划

  • explain plan for
  • SQLPLUS命令 set autotrace on
  • 第三方软件提供的GUI工具,比如Toad ,PL/SQL Developer

具体看: Oracle-SQL Explain Plan解读


如何看懂一个SQL的执行计划

首先得到一个SQL的执行计划

我们使用select * from table(DBMS_XPLAN.display_cursor(.......))的方式获取select * from t1 ,t2 where t1.id = t2.id如下的执行计划:

其中,rows列 就是我们上面说到的 Card(Cardinality) 9i以前的版本使用的是Card.


如何阅读呢?

我们首先从说缩进度最大的行读取,它是最先被执行的步骤

ID=3和ID=4是最先被执行的。

当两行缩进一样时,最上面的最先被执行,在这里就是 ID=3的先执行,然后是ID=4的。

然后是缩进次之的,

这里就是

紧接着锁进再次之的,以此类推

在这里就是是ID=1的 ,最后是ID=0的。

我们也可在PL/SQL中F5查看执行计划后,一步步的跟踪

这就是这个SQL的执行过程。

大致意思是:

从T2表读取第一行数据 是否符合条件

如果符合就拿出一行来,然后到索引IND_T1 中找到对应的值,然后重复,直到把整个T2表全表扫描完,这个过程就叫NESTED LOOPS .

当T2表被扫描完之后,会产生一个结果集,这个结果集是 IND_T1的一个索引集,然后ORACLE根据说印键值上的rowid 去T1表找对应的记录,就是这一步: Operation TABLE ACCESS BY INDEX ROWID

然后还有个NESTED LOOPS .(疑惑待思考)

最后将结果返回: Operation SELECT STATEMENT


执行计划中的值说明

  • ID列: 是一个序号,注意,它的大小并不是执行的先后顺序。

  • Operation列: 是当前的操作内容。

  • Rows 列: 就是当前操作的cardinality,Oracle估算当前操作的返回结果集

  • Cost(cpu): Oracle计算出来的一个数值(代价),用于说明SQL执行的代价

  • Time列: Oracle估算当前操作的时间。


还有些重要信息。比如

Predicate Information (identified by operation id):
---------------------------------------------------   4 - access("T1"."ID"="T2"."ID")

这一段是来说明谓词信息和数据获取的方式,它的意思在ID=4的那一列,通过访问索引寻找数据,而不是访问原表数据。

还有个常见的方式 filter ,我们这里来解释下access和filter的区别

如果执行计划显示access, 就表示这个谓词条件的值将会影响数据的访问路径(全表还是索引,这里是索引)

如果执行计划显示filter,表示谓词条件的值并不会影响数据的访问路径,只起到过滤的作用。


执行计划的最后一步是

这一步提示用户CBO当前使用的技术,需要用户咋分析执行计划时考虑到这些因素,比如现在提示这些信息时,当前表使用了动态采样,通过这个提示,我们就知道这个表可能没有做过分析。

举例:

Connected to Oracle Database 11g Enterprise Edition Release 11.2.0.4.0
Connected as xxx@xgjSQL> create table t(x int);Table createdSQL>  begin
   for i in 1 .. 1000 loop
     insert into t values (i);
   end loop;
   commit;
 end;
 /SQL> select a.SQL_ID ,a.CHILD_NUMBER from v$sql a where a.SQL_TEXT like 'select * from t';SQL_ID CHILD_NUMBER
------------- ------------
89km4qj1thh13            0
SQL> select * from table(dbms_xplan.display_cursor('89km4qj1thh13',0));PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
SQL_ID  89km4qj1thh13, child number 0
-------------------------------------
select * from t
Plan hash value: 1601196873
--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |       |       |     2 (100)|          |
|   1 |  TABLE ACCESS FULL| T    |     1 |    13 |     2   (0)| 00:00:01 |
--------------------------------------------------------------------------
Note
-----
   - dynamic sampling used for this statement (level=2)17 rows selectedSQL> 

我们手工执行进行表分析后,重新查看下

##表分析
SQL> exec dbms_stats.gather_table_stats(user,'t');PL/SQL procedure successfully completed##因为是从shared_pool中加载数据,如果不清空,会影响执行计划
SQL> alter system flush shared_pool;System altered
SQL>select * from t;
....
SQL>select a.SQL_ID ,a.CHILD_NUMBER,a.SQL_TEXT from v$sql a where a.SQL_TEXT like 'select * from t%';
....
SQL> select * from table(dbms_xplan.display_cursor('ckzaraqcmkr2f',0));PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
SQL_ID  ckzaraqcmkr2f, child number 0
-------------------------------------
select * from t
Plan hash value: 1601196873
--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |       |       |     3 (100)|          |
|   1 |  TABLE ACCESS FULL| T    |  1000 |  4000 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------13 rows selectedSQL> 

执行计划中已经没有动态采样的提示信息了。


第一次执行SQL,CBO发现表没有做表分析,于是使用动态采样的方式获取数据信息。

SQL第二次执行时,CBO发现表已经分析过了,于是就不会再使用动态分析,而是直接使用分析数据。

这里会出现两种情况:

  • 如果没做表分析,CBO可以通过动态采样的方式来分析数据,也可以获取到正确的执行计划
  • 如果分析过,但是分析信息过旧,这时候CBO不会再使用动态采样,而是使用这些旧的分析数据,有可能导致错误的执行信息

总结

以上我们阐述了执行计划输出的全部内容。 当我们在看执行计划时,不能只看执行计划的本身,还要看下面的谓词和提示信息,这都非常有帮助。

Oracle优化05-执行计划相关推荐

  1. oracle 优化器之执行计划

    什么是执行计划 执行计划显示了执行一个sql语句所需步骤的详细信息.这些步骤代表了一组数据库操作它们会消费和生产行数据.这些操作的顺序以及它们的实现取决于查询优化器对查询转换和物理优化技术的联合使用. ...

  2. oracle 优化器 执行计划

    1.优化器种类   rule  choose  first_rows_n  first_rows  all_rows(10g后默认) 2.访问表的方法:全表扫描(TABLE ACCESS FULL) ...

  3. Oracle查看SQL执行计划的方式

    Oracle查看SQL执行计划的方式 获取Oracle sql执行计划并查看执行计划,是掌握和判断数据库性能的基本技巧.下面案例介绍了多种查看sql执行计划的方式: 基本有以下几种方式: 1.通过sq ...

  4. oracle 产看执行计划_ODBA 技能SPM计划

    OBA技能1-获取执行计划 OBA技能2-执行计划顺序 OBA技能3-执行计划顺序表连接 ODBA 技能4实战执行计划 ODBA 技能5 固定执行计划 因为每次统计信息作业在收集完信息后,会触发ACS ...

  5. 资源放送丨《Oracle数据库SQL执行计划的取得和解析》PPT视频

    前段时间,墨天轮邀请Oracle优化方面的资深专家 陈晓辉 老师分享了<Oracle数据库SQL执行计划的取得和解析>,在这里我们共享一下PPT和视频,供大家参考学习. Oracle优化资 ...

  6. 2场直播丨Oracle数据库SQL执行计划的取得和解析、一次特殊的 Oralce 硬解析性能问题的技术分享...

    1. Oracle数据库SQL执行计划的取得和解析- 2021.02.23 2月23日(周二)晚八点直播课,Oracle优化资深专家陈晓辉,以专业ORACLE数据库技术支持工程师的角度讲解SQL文的执 ...

  7. oracle的解析計劃,Oracle中获取执行计划的几种方法分析

    1. 预估执行计划 - Explain PlanExplain plan以SQL语句作为输入,得到这条SQL语句的执行计划,并将执行计划输出存储到计划表中. 首先,在你要执行的SQL语句前加expla ...

  8. Oracle 查看 SQL执行计划

    Oracle 查看 SQL执行计划 SQL性能分析 执行计划可以用来分析SQL的性能 一.查看执行计划的方法 1. 设置autotrace set autotrace off: 此为默认值,即关闭au ...

  9. oracle 数据执行计划,Oracle 常见的执行计划步骤(explain结果的Description数据参考)...

    在PL/SQL中 执行explain plain for  select ... 查看Description 数据参考: Oracle常见的执行计划步骤 这里我们介绍一些常见的执行计划中的步骤及算法. ...

  10. oracle full table scan,ORACLE优化之执行规划(1) - TABLE FULL SCAN/INDEX FULL SCAN

    ORACLE优化之执行规划(1) - TABLE FULL SCAN/INDEX FULL SCAN TABLE FULL SCAN 全表扫描,表示表中所有记录都被访问到.如果表很大, 该操作对查询性 ...

最新文章

  1. 人脸服务器如何与门禁系统对接,人脸识别门禁系统终端设备接口说明
  2. php跨域cookie共享使用方法
  3. 注意!Apache下这些与Hadoop相关的开源项目要退休了!
  4. python3编译器怎么下载_Python编译器及Sublime Text3安装及开发环境配置
  5. 13个知识点,系统整理Python时间处理模块Datetime
  6. 详解mysql int类型的长度值问题
  7. 图片还原去遮挡_斗罗:这届coser太强,动画刚播出两天,战损三哥就被还原了...
  8. 语音识别之DTW算法的应用(Python)
  9. jQuery treeTable
  10. 基于决策树的高层次语义图像检索
  11. springboot 动态数据源问题
  12. 微信小程序_文档_01_入门
  13. OTRS安装使用系列之三——不同的OTRS…
  14. 《简约至上》读书笔记
  15. 工作站和台式机的区别是什么
  16. 小友VUI SDK 初探
  17. 京东后台面试(很全面,很符合现在的面试!!)
  18. Hutool - 对于图片的基本处理
  19. 谭浩强 C++面向对象程序设计 118页第9题 9.商店销售某一商品,商店每天公布统一的折扣(discout)。同时允许销售人员在销售时灵活掌握售价(Price),在此基础上,对一次购10件以上者,还
  20. 【数据分析】:什么是数据分析?

热门文章

  1. Leetcode 48. 旋转图像 (每日一题 20210813)
  2. Leetcode 62.不同路径 (每日一题 20210701)
  3. 深度学习~图神经网络GNN
  4. NLPCC:预训练在小米的推理优化落地
  5. 装机人员工具_吕梁采购气伏式包装机-哪家好-强盛包装机械
  6. python if main_python中if __name__ == '__main__' :main(()
  7. matplotlib-plot-绘制折线图
  8. LeetCode题组:第206题-反转链表
  9. LeetCode-动态规划背包题-1049. 最后一块石头的重量 II
  10. 微服务接口限流的设计与思考(附GitHub框架源码)