一、如何查看执行计划

1.使用SQL

SQL>EXPLAINPLAN FOR sql语句;
SQL>SELECTplan_table_output FROM TABLE(DBMS_XPLAN.DISPLAY('PLAN_TABLE'));
示例:
SQL>EXPLAIN PLAN FOR SELECT * FROM TEST;
已解释。
SQL>SELECT plan_table_output FROM TABLE(DBMS_XPLAN.DISPLAY('PLAN_TABLE'));
或者:
SQL>  select * from table(dbms_xplan.display);

2.设置autotrace


序号

命令

解释

1

SET AUTOTRACE OFF

默认值,关闭autotrace.

2

SET AUTOTRACE ON EXPLAIN

只显示执行结果与执行计划

3

SET AUTOTRACE ON STATISTICS

只显示执行结果与统计信息

4

SET AUTOTRACE ON

显示执行结果、执行计划、统计信息。

5

SET AUTOTRACE TRACEONLY

不显示执行结果,仅显示执行计划与统计信息

SQL>set autotrace traceonly;
SQL>alter system flush shared_pool;
Systemaltered.
SQL>select * from TEST;
ExecutionPlan
----------------------------------------------------------
--------------------------------------------------------------------
|Id  | Operation         | Name      | Rows | Bytes | Cost (%CPU)|
--------------------------------------------------------------------
|   0 | SELECT STATEMENT  |          |     3 |  4335 |    3   (0)|
|   1 | TABLE ACCESS FULL| TEST |     3|  4335 |     3  (0)|
--------------------------------------------------------------------
Note
-----
   - 'PLAN_TABLE' is oldversion
Statistics
----------------------------------------------------------
      1770  recursive calls
          0 db block gets
        293 consistent gets
          3 physical reads
          0 redo size
       2152 bytes sent via SQL*Net to client
        381 bytes received via SQL*Net from client
          2 SQL*Net roundtrips to/from client
         25 sorts (memory)
          0 sorts (disk)
          3 rows processed
再次执行一遍
SQL>select * from TEST;
ExecutionPlan
----------------------------------------------------------
--------------------------------------------------------------------
|Id  | Operation         | Name      | Rows | Bytes | Cost (%CPU)|
--------------------------------------------------------------------
|   0 | SELECT STATEMENT  |          |     3 |  4335 |    3   (0)|
|   1 | TABLE ACCESS FULL| TEST |     3|  4335 |     3  (0)|
--------------------------------------------------------------------
Note
-----
   - 'PLAN_TABLE' is old version
Statistics
----------------------------------------------------------
0  recursive calls
          0 db block gets
          8 consistent gets
          0 physical reads
          0 redo size
       2152 bytes sent via SQL*Net to client
        381 bytes received via SQL*Net from client
          2 SQL*Net roundtrips to/from client
          0 sorts (memory)
          0 sorts (disk)
          3 rows processed
SQL>

执行计划中字段解释

ID: 一个序号,但不是执行的先后顺序。执行的先后根据缩进来判断。

Operation: 当前操作的内容。

Rows: 当前操作的Cardinality,Oracle估计当前操作的返回结果集。

Bytes:表示执行该步骤后返回的字节数

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

Time:Oracle 估计当前操作的时间。

谓词说明

PredicateInformation (identified by operation id):

---------------------------------------------------


1 - filter("ENGNAME"='lucy')

2 - access("PATIENTID"=205124)

Access: 表示这个谓词条件的值将会影响数据的访问路劲(表还是索引)。

Filter:表示谓词条件的值不会影响数据的访问路劲,只起过滤的作用。

这里会出现两种情况:

(1) 如果表没有做过分析,那么CBO可以通过动态采样的方式来获取分析数据,也可以或者正确的执行计划。

(2) 如果表分析过,但是分析信息过旧,这时CBO就不会在使用动态采样,而是使用这些旧的分析数据,从而可能导致错误的执行计划。

备注:在谓词中主要注意access,要考虑谓词的条件,使用的访问路径是否正确。

统计信息说明

redo size:DML生成的redo的大小。

bytes sent via SQL*Net to client:服务端发送了多少字节。

bytes received via SQL*Net from client:客户端发送了多少字节。

SQL*Net roundtrips to/from client客户端/服务器来回次数。

sorts (memory) 在内存执行的排序量。

sorts (disk)在磁盘上执行的排序量。

rows processed:处理/产生了多少行结果。

recursive calls

Numberof recursive calls generated at both the user and system level. Oracle Databasemaintains tables used for internal processing. When Oracle Database needs tomake a change to these tables, it internally generates an internal SQLstatement, which in turn generates a recursive call.

Inshort, recursive calls are basically SQL performed on behalf of your SQL. So,if you had to parse the query, for example, you might have had to run someother queries to get data dictionary information. These would be recursivecalls. Space management, security checks, calling PL/SQL from SQL—all incurrecursive SQL calls.

如上红色字体所标注,第二次查询的时候,因为数据字典的信息已经放在cache里,所以第二次的recursive call为0. 如果第二次也没有完全cache,那么也是会产生recursive call,但次数比第一次少。

DB Block Gets

Number of times a CURRENT block wasrequested.
Current mode blocks are retrieved as they exist right now, not in a consistentread fashion.Normally, blocks retrieved for a query are retrieved as theyexisted when the query began. Current mode blocks are retrieved as they existright now, not from a previous point in time. During a SELECT, you mightsee current mode retrievals due to reading the data dictionary to find the extentinformation for a table to do a full scan (because you need the "rightnow" information, not the consistent read). During a modification, youwill access the blocks in current mode in order to write to them.

consistent gets:

Number of times a consistent read wasrequested for a block.

Thisis how many blocks you processed in "consistent read" mode. This willinclude counts of blocks read from the rollback segment in order to roll back ablock.

Thisis the mode you read blocks in with a SELECT, for example.

Also,when you do a searched UPDATE/DELETE, you read the blocks in consistent readmode and then get the block in current mode to actually do the modification.

consistent gets direct:

Number of times a consistent read wasrequested for a block bypassing the buffer cache (for example, direct loadoperation). This is a subset of "consistent gets" statistics value.

consistent gets from cache:

Numberof times a consistent read was requested for a block from buffer cache. This isa subset of "consistent gets" statistics value.


physical reads

Totalnumber of data blocks read from disk. This number equals the value of"physical reads direct" plus all reads into buffer cache.

physical reads cache

Totalnumber of data blocks read from disk into the buffer cache. This is a subset of"physical reads" statistic

physical reads direct

Numberof reads directly from disk, bypassing the buffer cache. For example, in highbandwidth, data-intensive operations such as parallel query, reads of diskblocks bypass the buffer cache to maximize transfer rates and to prevent thepremature aging of shared data blocks resident in the buffer cache.


PhysicalReads通常是我们最关心的,如果这个值很高,说明要从磁盘请求大量的数据到Buffer Cache里,通常意味着系统里存在大量全表扫描的SQL语句,这会影响到数据库的性能,因此尽量避免语句做全表扫描,对于全表扫描的SQL语句,建议增加相关的索引,优化SQL语句来解决。

关于physicalreads ,db block gets 和consistentgets这三个参数之间有一个换算公式:

数据缓冲区的使用命中率=1- ( physical reads / (db block gets + consistent gets) )。

在SQL语句里体现如下:

用以下语句可以查看数据缓冲区的命中率:

SQL>SELECTname, value FROM v$sysstat WHERE name IN ('db block gets', 'consistentgets','physical reads');

查询出来的结果BufferCache的命中率应该在90%以上,否则需要增加数据缓冲区的大小。



针对以上3个概念进行的说明解释及关系如下:
1
、DB Block Gets(当前请求的块数目)
当前模式块意思就是在操作中正好提取的块数目,而不是在一致性读的情况下而产生的块数。正常的情况下,一个查询提取的块是在查询开始的那个时间点上存在的数据块,当前块是在这个时刻存在的数据块,而不是在这个时间点之前或者之后的数据块数目。
2、Consistent Gets(数据请求总数在回滚段Buffer中的数据一致性读所需要的数据块)
这里的概念是在处理你这个操作的时候需要在一致性读状态上处理多少个块,这些块产生的主要原因是因为由于在你查询的过程中,由于其他会话对数据块进行操作,而对所要查询的块有了修改,但是由于我们的查询是在这些修改之前调用的,所以需要对回滚段中的数据块的前映像进行查询,以保证数据的一致性。这样就产生了一致性读。
3、Physical Reads(物理读)
就是从磁盘上读取数据块的数量,其产生的主要原因是:
1、 在数据库高速缓存中不存在这些块
2、 全表扫描
3、 磁盘排序
它们三者之间的关系大致可概括为:
逻辑读指的是Oracle从内存读到的数据块数量,一般来说是'consistent gets' + 'db block gets'。当在内存中找不到所需的数据块的话就需要从磁盘中获取,于是就产生了'phsical reads'。

网络摘要:
consistent gets
在判断一段SQL的性能时非常有用,通常来讲比较两段SQL的性能好坏不是看谁的执行时间短,而是看谁的consistent gets小。不过这也不是绝对的,下面这个例子就是一个反例:
ETL@RACTEST> create table test( a int);
Table created.
Elapsed: 00:00:00.05
ETL@RACTEST> ETL@RACTEST> begin
2 for i in 1..10000 loop
3 insert into test values (i);
4 end loop;
5 end;
6 /

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.44
ETL@RACTEST> set autot trace
ETL@RACTEST> ETL@RACTEST> select * from test;
10000 rows selected.
Elapsed: 00:00:00.05

Execution Plan
----------------------------------------------------------
Plan hash value: 1357081020

--------------------------------------------------------------------------
| Id |Operation | Name |Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | |10000| 126K| 6 (0)|00:00:01 |
| 1 | TABLE ACCESS FULL| TEST | 10000| 126K| 6 (0)|00:00:01 |
--------------------------------------------------------------------------
Note
-----
- dynamic sampling used for this statement
Statistics
----------------------------------------------------------
0 recursivecalls
0 dbblock gets
690 consistent gets
0 physicalreads
0 redosize
214231 bytes sent via SQL*Net toclient
7791 bytes received viaSQL*Net from client
668 SQL*Netroundtrips to/from client
0 sorts(memory)
0 sorts(disk)
10000 rows processed

可以看到select *读了690个内存块。

ETL@RACTEST> select * from test order by 1;
10000 rows selected.
Elapsed: 00:00:00.04
Execution Plan
----------------------------------------------------------
Plan hash value: 2007178810

---------------------------------------------------------------------------
| Id |Operation | Name |Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECTSTATEMENT | | 10000| 126K| 7 (15)|00:00:01 |
| 1 | SORT ORDERBY | | 10000| 126K| 7 (15)|00:00:01 |
| 2 | TABLE ACCESS FULL| TEST | 10000| 126K| 6 (0)|00:00:01 |
---------------------------------------------------------------------------

Note
-----
- dynamic sampling used for this statement

Statistics
----------------------------------------------------------
0 recursivecalls
0 dbblock gets
23 consistentgets
0 physicalreads
0 redosize
174288 bytes sent via SQL*Net toclient
7791 bytes received viaSQL*Net from client
668 SQL*Netroundtrips to/from client
1 sorts(memory)
0 sorts(disk)
10000 rows processed
再看一下order by,竟然只有23个逻辑读!
1. select * from test;
2. select * from test order by 1;
第1个SQL比第2个SQL效率高是毋庸置疑的。但是为什么第2个SQL的consistentgets如此之少,我起初也是百思不得其解,最终我在ASK TOM中找到了答案。原因有二:
一:通常情况下,不在logicalRAM buffer中的数据要通过physical reads来读取,而physical reads后通常会紧跟着一个consistent gets。因此一般情况下consistent gets是要比physical reads大的。但是有一个特例,如果physical reads得到的数据直接用于HASH或者SORT,则只记为physical reads不记为consistent gets。所以加上order by后有可能physical reads多但consistent gets少。不过这个原因不是我这里现象产生的原因,因为我这个实验里根本没有physical reads。
二:arraysize的影响。arraysize是指读取数据时一次读取得到的行数。这个值默认为15,使用show arraysize命令可以查看。一个数据块例如有100条记录,那么并不是读取这个块一次就能取到所有数据,以arraysize=15为例,就要有100/15=7次consistent gets。把arraysize设置得大一点可以降低consistent gets,不过有时候可能会消耗更多的资源。如果我们做selectcount(0) from test;操作,那么Oracle会把arraysize暂时设为test的行数,因此consistent gets会很少:
ETL@RACTEST> select count(0) from test;
Elapsed: 00:00:00.00
Execution Plan
----------------------------------------------------------
Plan hash value: 1950795681

-------------------------------------------------------------------
| Id |Operation | Name |Rows | Cost (%CPU)| Time |
-------------------------------------------------------------------
| 0 | SELECTSTATEMENT | | 1| 6 (0)| 00:00:01 |
| 1 | SORTAGGREGATE | | 1| | |
| 2 | TABLE ACCESS FULL| TEST | 10000| 6 (0)| 00:00:01 |
-------------------------------------------------------------------

Note
-----
- dynamic sampling used for this statement
Statistics
----------------------------------------------------------
0 recursivecalls
0 dbblock gets
23 consistentgets
0 physicalreads
0 redosize
515 bytes sent viaSQL*Net to client
465 bytes receivedvia SQL*Net from client
2 SQL*Netroundtrips to/from client
0 sorts(memory)
0 sorts(disk)
1 rowsprocessed

可以看到select count(0)只需要23个逻辑读。一共10000条数据,10000/15=666.667,好,667+23=690!和第1个SQL的consistent gets竟然惊人的一致!这不是巧合,这就是consistent gets的计算公式。
我们还可以发现select count(0)和第2个SQL的consistent gets竟然也惊人地一致,都是23!TOM的解释是:
在select * from test order by 1;时,Oracle也把arraysize临时设为test表的行数,它把所有数据先全部取出来放到sort区做排序,而在sort区的读取就不算在consistent gets里了。所以虽然第2个SQL和selectcount(0)的consistent gets相同,但它的效率一定比select count(0)低,我们看执行计划里的COST便可以得知,第2个SQL的COST为7,select count(0)的COST为6,第1个SQL的COST也为6。(COST相同并不代表执行效率完全相同)好了,现在明白了吧,这第二个原因就是我的实验现象产生的原因!


备注:部分信息摘自网络。