SQL优化之执行计划的正确查看方式

关注我的微信公众号:pythonislover,领取python,大数据,SQL优化相关视频资料!~

Python大数据与SQL优化笔 QQ群:771686295

查看SQL执行计划是SQL优化的第一步,所以我们要能正确掌握查看执行计划的方法。

  1. 用AUTOTRACE查看执行计划

SQL> set autotUsage: SET AUTOT[RACE] {OFF | ON | TRACE[ONLY]} [EXP[LAIN]] [STAT[ISTICS]]
set auto on :运行SQL且显示执行SQL运行结果,执行计划,统计信息set auto trace :运行SQL且不显示执行SQL运行结果,显示执行计划,统计信息set auto trace exp :不运行查询SQL,但是运行DML SQL,且不显示执行SQL运行结果,统计信息,显示执行计划set auto trace stat :运行SQL且只显示统计信息set auto off :关闭AUTOTRACE 
SQL> set autot onSQL> select count(*) from emp;
  COUNT(*)----------        15

Execution Plan----------------------------------------------------------Plan hash value: 2937609675
-------------------------------------------------------------------| Id  | Operation        | Name   | Rows  | Cost (%CPU)| Time     |-------------------------------------------------------------------|   0 | SELECT STATEMENT |        |     1 |     1   (0)| 00:00:01 ||   1 |  SORT AGGREGATE  |        |     1 |            |          ||   2 |   INDEX FULL SCAN| PK_EMP |    14 |     1   (0)| 00:00:01 |-------------------------------------------------------------------

Statistics----------------------------------------------------------          1  recursive calls          0  db block gets          1  consistent gets          0  physical reads          0  redo size        526  bytes sent via SQL*Net to client        524  bytes received via SQL*Net from client          2  SQL*Net roundtrips to/from client          0  sorts (memory)          0  sorts (disk)          1  rows processed

########################################################
SQL>set autot traceSQL> select count(*) from emp;

Execution Plan----------------------------------------------------------Plan hash value: 2937609675
-------------------------------------------------------------------| Id  | Operation        | Name   | Rows  | Cost (%CPU)| Time     |-------------------------------------------------------------------|   0 | SELECT STATEMENT |        |     1 |     1   (0)| 00:00:01 ||   1 |  SORT AGGREGATE  |        |     1 |            |          ||   2 |   INDEX FULL SCAN| PK_EMP |    14 |     1   (0)| 00:00:01 |-------------------------------------------------------------------

Statistics----------------------------------------------------------          0  recursive calls          0  db block gets          1  consistent gets          0  physical reads          0  redo size        526  bytes sent via SQL*Net to client        524  bytes received via SQL*Net from client          2  SQL*Net roundtrips to/from client          0  sorts (memory)          0  sorts (disk)          1  rows processed
SQL>

############################################
SQL> set autot trace expSQL> select count(*) from emp;
Execution Plan----------------------------------------------------------Plan hash value: 2937609675
-------------------------------------------------------------------| Id  | Operation        | Name   | Rows  | Cost (%CPU)| Time     |-------------------------------------------------------------------|   0 | SELECT STATEMENT |        |     1 |     1   (0)| 00:00:01 ||   1 |  SORT AGGREGATE  |        |     1 |            |          ||   2 |   INDEX FULL SCAN| PK_EMP |    14 |     1   (0)| 00:00:01 |-------------------------------------------------------------------

##########################################
SQL> set autot trace statSQL> select count(*) from emp;

Statistics----------------------------------------------------------          0  recursive calls          0  db block gets          1  consistent gets          0  physical reads          0  redo size        526  bytes sent via SQL*Net to client        524  bytes received via SQL*Net from client          2  SQL*Net roundtrips to/from client          0  sorts (memory)          0  sorts (disk)          1  rows processed
SQL> set autot off

2. 使用EXPLAIN PLAN FOR查看执行计划(推荐,因为可以显示谓语信息)

SQL> explain plan forselect * from emp;  2
Explained.
SQL> select * from table(dbms_xplan.display);
PLAN_TABLE_OUTPUT--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------Plan hash value: 3956160932
--------------------------------------------------------------------------| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |--------------------------------------------------------------------------|   0 | SELECT STATEMENT  |      |    14 |   532 |     3   (0)| 00:00:01 ||   1 |  TABLE ACCESS FULL| EMP  |    14 |   532 |     3   (0)| 00:00:01 |--------------------------------------------------------------------------
8 rows selected.

############################################查看高级执行计划SQL> explain plan forselect * from emp;  2
Explained.
SQL> select * from table(dbms_xplan.display(null,null,'advanced -projection'));
PLAN_TABLE_OUTPUT--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------Plan hash value: 3956160932
--------------------------------------------------------------------------| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |--------------------------------------------------------------------------|   0 | SELECT STATEMENT  |      |    14 |   532 |     3   (0)| 00:00:01 ||   1 |  TABLE ACCESS FULL| EMP  |    14 |   532 |     3   (0)| 00:00:01 |--------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):-------------------------------------------------------------
PLAN_TABLE_OUTPUT--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
   1 - SEL$1 / EMP@SEL$1
Outline Data-------------
  /*+      BEGIN_OUTLINE_DATA      FULL(@"SEL$1" "EMP"@"SEL$1")      OUTLINE_LEAF(@"SEL$1")      ALL_ROWS
PLAN_TABLE_OUTPUT--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------      DB_VERSION('11.2.0.1')      OPTIMIZER_FEATURES_ENABLE('11.2.0.1')      IGNORE_OPTIM_EMBEDDED_HINTS      END_OUTLINE_DATA  */
27 rows selected.

#####################################################查看内存中缓存的SQL的真实执行计划,关键在于找到SQL_IDSQL> select * from table(dbms_xplan.display_cursor(sql_id =>'c7gvq5j6k07aq',cursor_child_no=>0,format =>'ALL ALLSTATS LAST NOTE ADVANCED -projection'));
PLAN_TABLE_OUTPUT------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------SQL_ID  c7gvq5j6k07aq, child number 0------------------------------------- select b.USERNAME,b.sid,b.SERIAL#,a.sql_fulltext,b.status,a.object_status,b.event,b.STATE,b.WAIT_TIME,b.SECONDS_IN_WAIT,b.TIME_REMAINING_MICRO,b.WAIT_TIME_MICRO,a.sql_id,b.PROGRAM from v$sql a,v$session b whereb.sql_address=a.address
Plan hash value: 3784909302
----------------------------------------------------------------------------------------------------------------| Id  | Operation                 | Name              | E-Rows |E-Bytes| Cost (%CPU)|  OMem |  1Mem | Used-Mem |----------------------------------------------------------------------------------------------------------------|   0 | SELECT STATEMENT          |                   |        |       |     1 (100)|       |       |          ||   1 |  NESTED LOOPS             |                   |      1 |  2307 |     0   (0)|       |       |          ||   2 |   NESTED LOOPS            |                   |      1 |  2260 |     0   (0)|       |       |          ||   3 |    MERGE JOIN CARTESIAN   |                   |    100 |   208K|     0   (0)|       |       |          ||*  4 |     FIXED TABLE FULL      | X$KGLCURSOR_CHILD |      1 |  2042 |     0   (0)|       |       |          ||   5 |     BUFFER SORT           |                   |    100 |  9100 |     0   (0)|  2048 |  2048 | 2048  (0)||   6 |      FIXED TABLE FULL     | X$KSLWT           |    100 |  9100 |     0   (0)|       |       |          ||*  7 |    FIXED TABLE FIXED INDEX| X$KSUSE (ind:1)   |      1 |   127 |     0   (0)|       |       |          ||*  8 |   FIXED TABLE FIXED INDEX | X$KSLED (ind:2)   |      1 |    47 |     0   (0)|       |       |          |----------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):-------------------------------------------------------------
   1 - SEL$66C44981   4 - SEL$66C44981 / X$KGLCURSOR_CHILD@SEL$4   6 - SEL$66C44981 / W@SEL$7   7 - SEL$66C44981 / S@SEL$7   8 - SEL$66C44981 / E@SEL$7
Outline Data-------------
  /*+      BEGIN_OUTLINE_DATA      IGNORE_OPTIM_EMBEDDED_HINTS      OPTIMIZER_FEATURES_ENABLE('11.2.0.1')      DB_VERSION('11.2.0.1')      ALL_ROWS      OUTLINE_LEAF(@"SEL$66C44981")      MERGE(@"SEL$1B7D9AE9")      MERGE(@"SEL$641071AC")      OUTLINE(@"SEL$1")      OUTLINE(@"SEL$1B7D9AE9")      MERGE(@"SEL$68B588A0")      OUTLINE(@"SEL$641071AC")      MERGE(@"SEL$07BDC5B4")      OUTLINE(@"SEL$5")      OUTLINE(@"SEL$68B588A0")      MERGE(@"SEL$7")      OUTLINE(@"SEL$2")      OUTLINE(@"SEL$07BDC5B4")      MERGE(@"SEL$4")      OUTLINE(@"SEL$6")      OUTLINE(@"SEL$7")      OUTLINE(@"SEL$3")      OUTLINE(@"SEL$4")      FULL(@"SEL$66C44981" "X$KGLCURSOR_CHILD"@"SEL$4")      FULL(@"SEL$66C44981" "W"@"SEL$7")      FULL(@"SEL$66C44981" "S"@"SEL$7")      FULL(@"SEL$66C44981" "E"@"SEL$7")      LEADING(@"SEL$66C44981" "X$KGLCURSOR_CHILD"@"SEL$4" "W"@"SEL$7" "S"@"SEL$7" "E"@"SEL$7")      USE_MERGE_CARTESIAN(@"SEL$66C44981" "W"@"SEL$7")      USE_NL(@"SEL$66C44981" "S"@"SEL$7")      USE_NL(@"SEL$66C44981" "E"@"SEL$7")      END_OUTLINE_DATA  */
Predicate Information (identified by operation id):---------------------------------------------------
   4 - filter("INST_ID"=USERENV('INSTANCE'))   7 - filter(("S"."INST_ID"=USERENV('INSTANCE') AND BITAND("S"."KSSPAFLG",1)<>0 AND              BITAND("S"."KSUSEFLG",1)<>0 AND "S"."KSUSESQL"="KGLHDPAR" AND "S"."INDX"="W"."KSLWTSID"))   8 - filter("W"."KSLWTEVT"="E"."INDX")
Note-----   - Warning: basic plan statistics not available. These are only collected when:       * hint 'gather_plan_statistics' is used for the statement or       * parameter 'statistics_level' is set to 'ALL', at session or system level

84 rows selected.

########################################################查看AWR报告中的SQL执行计划select * from table(dbms_xplan.display_cursor('&sql_id',0));  select * from table(dbms_xplan.display_cursor('&sql_id',1));  

3.查看A-Time真实执行计划

关键在于/*+ gather_plan_statistics*/ hint

SQL> select /*+ gather_plan_statistics*/ count(*) from emp;
  COUNT(*)----------        15
SQL> SELECT * FROM TABLE(dbms_xplan.display_cursor(NULL,NULL,'ALLSTATS LAST'));
PLAN_TABLE_OUTPUT------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------SQL_ID  cn2q98tcvnkd6, child number 0-------------------------------------select /*+ gather_plan_statistics*/ count(*) from emp
Plan hash value: 2937609675
-------------------------------------------------------------------------------------| Id  | Operation        | Name   | Starts | E-Rows | A-Rows |   A-Time   | Buffers |-------------------------------------------------------------------------------------|   0 | SELECT STATEMENT |        |      1 |        |      1 |00:00:00.01 |       1 ||   1 |  SORT AGGREGATE  |        |      1 |      1 |      1 |00:00:00.01 |       1 ||   2 |   INDEX FULL SCAN| PK_EMP |      1 |     14 |     15 |00:00:00.01 |       1 |-------------------------------------------------------------------------------------

14 rows selected.

Starts: 这个操作执行次数

E-Rows:优化器评估的行数

A-Rows:真实的操作行数

A-Time:真实的操作累计时间

Buffers:累计的逻辑读

Reads:累计的物理读

获取执行计划的方法大概就上面这些,下面说说我个人看执行计划时候的会注意的地方

  1. recursive calls 表示递归调用,SQL第一次发生硬解析,会隐含地调用一些内部SQL,所以第一次执行SQL的是这个参数可能会大于1,但是当第二次执行SQL时候就不需要调用了,这个参数就会变成0, 如果这个参数一直很大,就要看看SQL里面是不是调用了一些自定义函数导致的,这样可能会导致性能问题。

  2. consistent gets 表示逻辑读,SQL优化的关键就是减少逻辑读的大小

  3. 如果一个SQL的consistent gets 大于SQL中所有表的总段大小,则说明这个SQL的优化空间比较大。一般而言,每获取一行开销5个以下的逻辑读是属于可以接受的范围的,为什么不关注物理读,因为每次执行的时候,物理读会随着BUFFER CACHE的命中而变化的,而逻辑读却是保持不变的。

构造脚本:drop table t;create table test  as select * from dba_objects t;insert into t select * from test;insert into t select * from test;insert into t select * from test;insert into t select * from test;insert into t select * from test;update t set object_id=rownum;commit;SQL> analyze table t compute statistics for table for all indexes for all indexed columns;表已分析。SQL> select count(*) from t;  COUNT(*)----------   1680640--------------------------------------------------------------------------------进行autotrace 分析:SQL> set autotrace traceonlySQL> select * from t where object_id<101;已选择100行。已用时间:  00: 00: 04.22执行计划----------------------------------------------------------Plan hash value: 1357081020--------------------------------------------------------------------------| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |--------------------------------------------------------------------------|   0 | SELECT STATEMENT  |      |   100 |  9700 |  5134   (3)| 00:01:02 ||*  1 |  TABLE ACCESS FULL| T    |   100 |  9700 |  5134   (3)| 00:01:02 |--------------------------------------------------------------------------Predicate Information (identified by operation id):---------------------------------------------------   1 - filter("OBJECT_ID"<101)统计信息----------------------------------------------------------          0  recursive calls          0  db block gets      22990  consistent gets          0  physical reads          0  redo size       5317  bytes sent via SQL*Net to client        465  bytes received via SQL*Net from client          8  SQL*Net roundtrips to/from client          0  sorts (memory)          0  sorts (disk)        100  rows processed

本查询最终返回100行,完成时间是4秒,产生了 22990个逻辑读,根据22990/100=229的比例计算,平均每获取一行开销了229个逻辑读,远不足获取单行记录小于5个逻辑读的标准,由此我们可以判断,此SQL相当低效,经分析是缺少相应的索引。

在建了索引后,产生的效果如下,返回100行产生了18个逻辑读,根据18/100=0.18的比例计算,平均每获取一行开销了0.18个逻辑读,符合获取单行记录小于5个逻辑读的标准,效率也有了极大提升,完成时间仅0.1秒

SQL> alter   table  t add   constraint  PK_t   primary key(object_id);表已更改。SQL> analyze table t compute statistics for table for all indexes for all indexed columns;表已分析。SQL>  select * from t where object_id<101;已选择100行。已用时间:  00: 00: 00.01执行计划----------------------------------------------------------Plan hash value: 115135762--------------------------------------------------------------------------------------------------------------------------------------| Id  | Operation                   | Name      | Rows  | Bytes | Cost (%CPU)| Time    |--------------------------------------------------------------------------------------------------------------------------------------|   0 | SELECT STATEMENT           |            |   100 |  9700 |     5   (0)| 00:00:01 ||   1 |  TABLE ACCESS BY INDEX ROWID|    T       |   100 |  9700 |     5   (0)| 00:00:01 ||*  2 |   INDEX RANGE SCAN         | PK_TEST    |   100 |       |      3  (0)| 00:00:01  |---------------------------------------------------------------------------------------------------------------------------------------Predicate Information (identified by operation id):---------------------------------------------------   2 - access("OBJECT_ID"<101)统计信息----------------------------------------------------------          1  recursive calls          0  db block gets         18  consistent gets          0  physical reads          0  redo size       5317  bytes sent via SQL*Net to client        465  bytes received via SQL*Net from client          8  SQL*Net roundtrips to/from client          0  sorts (memory)          0  sorts (disk)        100  rows processed

4.rows processed 表示SQL一共返回的行数,这个参数也是很重要的,根据这个参数我们可以大概知道SQL走HASH JOIN合适还是走NEST LOOP合适,一般rows processed比较大一般走HASH JOIN比较好,但是rows processed比较小走NEST LOOP比较好。

5.优化器的执行计划很多时候都是错误的,这时候可能会导致性能问题,比如驱动表本来1000W行,结果被评估成10行,这样大概率走NEST LOOP,这样就会出现问题,这时候我们就可以用select /*+ gather_plan_statistics*/ count(*) from emp; 这种方式去查看SQL的真实执行计划,去看看SQL的真实执行时间,每行返回的真实返回数据,真实的执行次数。

比如

Starts: 这个操作执行次数  #驱动表行数评估行数出错,被驱动表这个次数会比较大,可能会出现问题。

E-Rows与A-Rows 差距比较大的时候,也可能会出现问题

出现问题之后的解决办法大概如下:

  1. 收集表的统计信息,直方图信息

  2. 使用hint走我们想要的执行计划

  3. 修改SQL

今天就写到这里。

个人意见,望指正。

猜你喜欢

转载自blog.csdn.net/yrg5101/article/details/88915152