六种执行计划

Oracle提供了6种执行计划获取方法,各种方法侧重点不同。

explain plan for

例子:

  1. SQL> show user
  2. USER "HR"
  1. SQL> set linesize 1000
  2. SQL> set pagesize 2000
  3. SQL> explain plan for
  4. select *
  5. from employees,jobs
  6. where employees.job_id=jobs.job_id
  7. and employees.department_id=50;
  8. 已解释。
  1. SQL> select * from table(dbms_xplan.display());
  2. PLAN_TABLE_OUTPUT
  3. \----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  4. Plan hash value: 303035560
  5. \------------------------------------------------------------------------------------------
  6. | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
  7. \------------------------------------------------------------------------------------------
  8. | 0 | SELECT STATEMENT | | 45 | 4590 | 6 (17)| 00:00:01 |
  9. | 1 | MERGE JOIN | | 45 | 4590 | 6 (17)| 00:00:01 |
  10. | 2 | TABLE ACCESS BY INDEX ROWID| JOBS | 19 | 627 | 2 (0)| 00:00:01 |
  11. | 3 | INDEX FULL SCAN | JOB_ID_PK | 19 | | 1 (0)| 00:00:01 |
  12. |* 4 | SORT JOIN | | 45 | 3105 | 4 (25)| 00:00:01 |
  13. |* 5 | TABLE ACCESS FULL | EMPLOYEES | 45 | 3105 | 3 (0)| 00:00:01 |
  14. \------------------------------------------------------------------------------------------
  15. Predicate Information (identified by operation id):
  16. \---------------------------------------------------
  17. 4 - access("EMPLOYEES"."JOB_ID"="JOBS"."JOB_ID")
  18. filter("EMPLOYEES"."JOB_ID"="JOBS"."JOB_ID")
  19. 5 - filter("EMPLOYEES"."DEPARTMENT_ID"=50)
  20. 已选择19行。

优点:无需真正执行,快捷方便;

缺点:

  1. 没有输出相关统计信息,例如产生了多少逻辑读,多少次物理读,多少次递归调用的情况;
  2. 无法判断处理了多少行;
  3. 无法判断表执行了多少次

set autotrace on

用法:

命令 作用
SET AUTOT[RACE] OFF 停止AutoTrace
SET AUTOT[RACE] ON 开启AutoTrace,显示AUTOTRACE信息和SQL执行结果
SET AUTOT[RACE] TRACEONLY 开启AutoTrace,仅显示AUTOTRACE信息
SET AUTOT[RACE] ON EXPLAIN 开启AutoTrace,仅显示AUTOTRACE的EXPLAIN信息
SET AUTOT[RACE] ON STATISTICS 开启AutoTrace,仅显示AUTOTRACE的STATISTICS信息

例子:

  1. SQL> set autotrace on
  2. SQL> select * from employees,jobs where employees.job_id=jobs.job_id and employees.department_id=50;
  3. --输出结果(略)
  4. -- ...
  5. 已选择45行。
  6. 执行计划
  7. \----------------------------------------------------------
  8. Plan hash value: 303035560
  9. \------------------------------------------------------------------------------------------
  10. | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
  11. \------------------------------------------------------------------------------------------
  12. | 0 | SELECT STATEMENT | | 45 | 4590 | 6 (17)| 00:00:01 |
  13. | 1 | MERGE JOIN | | 45 | 4590 | 6 (17)| 00:00:01 |
  14. | 2 | TABLE ACCESS BY INDEX ROWID| JOBS | 19 | 627 | 2 (0)| 00:00:01 |
  15. | 3 | INDEX FULL SCAN | JOB_ID_PK | 19 | | 1 (0)| 00:00:01 |
  16. |* 4 | SORT JOIN | | 45 | 3105 | 4 (25)| 00:00:01 |
  17. |* 5 | TABLE ACCESS FULL | EMPLOYEES | 45 | 3105 | 3 (0)| 00:00:01 |
  18. \------------------------------------------------------------------------------------------
  19. Predicate Information (identified by operation id):
  20. \---------------------------------------------------
  21. 4 - access("EMPLOYEES"."JOB_ID"="JOBS"."JOB_ID")
  22. filter("EMPLOYEES"."JOB_ID"="JOBS"."JOB_ID")
  23. 5 - filter("EMPLOYEES"."DEPARTMENT_ID"=50)
  24. 统计信息
  25. \----------------------------------------------------------
  26. 0 recursive calls
  27. 0 db block gets
  28. 13 consistent gets
  29. 0 physical reads
  30. 0 redo size
  31. 5040 bytes sent via SQL*Net to client
  32. 433 bytes received via SQL*Net from client
  33. 4 SQL*Net roundtrips to/from client
  34. 1 sorts (memory)
  35. 0 sorts (disk)
  36. 45 rows processed

优点:

  1. 可以输出运行时的相关统计信息(产生多少逻辑读、多少次递归调用、多少次物理读等);
  2. 虽然要等语句执行完才能输出执行计划,但是可以有traceonly开关来控制返回结果不打屏输出;

缺点:

  1. 必须要等SQL语句执行完,才出结果;
  2. 无法看到表被访问了多少次;

statistics_level=all

步骤一:ALTER SESSION SET STATISTICS_LEVEL=ALL;

步骤二:执行待分析的SQL

步骤三:select * from table(dbms_xplan.display_cursor(null,null,’allstats last’));

注释:

  1. 第3步还有1种方法,
  1. select * from table(dbms_xplan.display_cursor(null,null,'advanced')); --得到的信息更详细

例子:

  1. SQL> alter session set statistics_level=all;
  2. SQL> select * from employees,jobs where employees.job_id=jobs.job_id and employees.department_id=50;
  3. --输出结果
  4. --...
  5. 已选择45行。
  6. SQL> set linesize 1000
  7. SQL> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
  8. PLAN_TABLE_OUTPUT
  9. \-----------------------------------------------------------------------------------------------------------------------------------
  10. SQL_ID d8jzhcdwmd9ut, child number 0
  11. \-------------------------------------
  12. select * from employees,jobs where employees.job_id=jobs.job_id and
  13. employees.department_id=50
  14. Plan hash value: 303035560
  15. \----------------------------------------------------------------------------------------------------------------------------------------
  16. | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads | OMem | 1Mem | Used-Mem |
  17. \----------------------------------------------------------------------------------------------------------------------------------------
  18. | 0 | SELECT STATEMENT | | 1 | | 45 |00:00:00.01 | 13 | 8 | | | |
  19. PLAN_TABLE_OUTPUT
  20. \-------------------------------------------------------------------------------------------------------------------------------------
  21. | 1 | MERGE JOIN | | 1 | 45 | 45 |00:00:00.01 | 13 | 8 | | | |
  22. | 2 | TABLE ACCESS BY INDEX ROWID| JOBS | 1 | 19 | 19 |00:00:00.01 | 6 | 2 | | | |
  23. | 3 | INDEX FULL SCAN | JOB_ID_PK | 1 | 19 | 19 |00:00:00.01 | 3 | 1 | | | |
  24. |* 4 | SORT JOIN | | 19 | 45 | 45 |00:00:00.01 | 7 | 6 | 6144 | 6144 | 6144 (0)|
  25. |* 5 | TABLE ACCESS FULL | EMPLOYEES | 1 | 45 | 45 |00:00:00.01 | 7 | 6 | | | |
  26. \----------------------------------------------------------------------------------------------------------------------------------------
  27. Predicate Information (identified by operation id):
  28. \---------------------------------------------------
  29. 4 - access("EMPLOYEES"."JOB_ID"="JOBS"."JOB_ID")
  30. PLAN_TABLE_OUTPUT
  31. \-----------------------------------------------------------------------------------------------------------------------------
  32. filter("EMPLOYEES"."JOB_ID"="JOBS"."JOB_ID")
  33. 5 - filter("EMPLOYEES"."DEPARTMENT_ID"=50)
  34. 已选择25行。

关键字解读:

  1. starts:SQL执行的次数;
  2. E-Rows:执行计划预计返回的行数;
  3. R-Rows:执行计划实际返回的行数;
  4. A-Time:每一步执行的时间(HH:MM:SS.FF),根据这一行可知SQL耗时在哪些地方;
  5. Buffers:每一步实际执行的逻辑读或一致性读;
  6. Reads:物理读;
  7. OMem:OMem为最优执行模式所需的内存评估值, 这个数据是由优化器统计数据以及前一次执行的性能数据估算得出的;
  8. 1Mem:1Mem为one-pass模式所需的内存评估值,当工作区大小无法满足操作所需的大小时,需要将部分数据写入临时磁盘空间中(如果仅需要写入一次就可以完成操作,就称一次通过,One-Pass;否则为多次通过,Multi-Pass).该列数据为语句最后一次执行中,单次写磁盘所需要的内存大小,这个由优化器统计数据以及前一次执行的性能数据估算得出的
  9. Used_Mem:Used-Mem则为当前操作实际执行时消耗的内存,括号里面为(发生磁盘交换的次数,1次即为One-Pass,大于1次则为Multi_Pass,如果没有使用磁盘,则显示0)

优点:

  1. 可以清晰的从starts得出表被访问多少次;
  2. 可以从E-Rows和A-Rows得到预测的行数和真实的行数,从而可以准确判断Oracle评估是否准确;
  3. 虽然没有准确的输出运行时的相关统计信息,但是执行计划中的Buffers就是真实的逻辑读的数值;

缺点:

  1. 必须要等执行完后才能输出结果;
  2. 无法控制结果打屏输出,不像autotrace可以设置traceonly保证不输出结果;
  3. 看不出递归调用,看不出物理读的数值

dbms_xplan.display_cursor获取

步骤1:

  1. select * from table( dbms_xplan.display_cursor('&sql_id') ); --该方法是从共享池得到,如果SQL已被age outshare pool,则查找不到

注释:

  1. 还有1种方法,
  1. select * from table( dbms_xplan.display_awr('&sql_id') ); --该方法是从awr性能视图里面获取

2. 如果有多个执行计划,可用以下方法查出:

  1. select * from table(dbms_xplan.display_cursor('&sql_id',0));
  2. select * from table(dbms_xplan.display_cursor('&s ql_id',1));

例子:

  1. SQL> select * from table(dbms_xplan.display_cursor('5hkd01f03y43d'));
  2. PLAN_TABLE_OUTPUT
  3. \--------------------------------------------------------------------------------
  4. SQL_ID 5hkd01f03y43d, child number 0
  5. \-------------------------------------
  6. select * from test where table_name = 'LOG$'
  7. Plan hash value: 2408911181
  8. \--------------------------------------------------------------------------------
  9. | Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
  10. \--------------------------------------------------------------------------------
  11. | 0 | SELECT STATEMENT | | | | 2 (100)|
  12. | 1 | TABLE ACCESS BY INDEX ROWID| TEST | 1 | 241 | 2 (0)|
  13. |* 2 | INDEX RANGE SCAN | IDX_TEST_1 | 1 | | 1 (0)|
  14. \--------------------------------------------------------------------------------
  15. Predicate Information (identified by operation id):
  16. \---------------------------------------------------
  17. 2 - access("TABLE_NAME"='LOG$')
  18. 19 rows selected

注释:如何查看1个sql语句的sql_id,可直接查看v$sql

Oracle查看执行计划总结 - 图1

优点:

  1. 知道sql_id即可得到执行计划,与explain plan for一样无需执行;
  2. 可得到真实的执行计划

缺点:

  1. 没有输出运行的统计相关信息;
  2. 无法判断处理了多少行;
  3. 无法判断表被访问了多少次;

事件10046 trace跟踪

步骤1:

  1. alter session set events '10046 trace name context forever,level 12'; --开启追踪

步骤2:执行sql语句;

步骤3:

  1. alter session set events '10046 trace name context off'; --关闭追踪

步骤4:

  1. select tracefile from v$process where addr=(select paddr from v$session where sid=(select sid from v$mystat where rownum<=1)); -- 找到跟踪后产生的文件

步骤5:

  1. tkprof trc文件 目标文件 sys=no sort=prsela,exeela,fchela --格式化命令

例子:

见:使用10046追踪执行计划demo

优点:

1.可以看出sql语句对应的等待事件;

2.如果函数中有sql调用,函数中有包含sql,将会被列出,无处遁形;

3.可以方便的看处理的行数,产生的逻辑物理读;

4.可以方便的看解析时间和执行时间;

5.可以跟踪整个程序包

缺点:

1.步骤繁琐;

2.无法判断表被访问了多少次;

3.执行计划中的谓词部分不能清晰的展现出来

awrsqrpt.sql

步骤1:@?/rdbms/admin/awrsqrpt.sql

步骤2:选择你要的断点(begin snap和end snap)

步骤3:输入要查看的sql_id

例子:

见:使用awrsqrpt.sql查看执行计划demo

如何选择

选择时一般遵循以下规则:

  1. 如果sql执行很长时间才出结果或返回不了结果,用方法1:explain plan for
  2. 跟踪某条sql最简单的方法是方法1:explain plan for,其次是方法2:set autotrace on
  3. 如果相关查询某个sql多个执行计划的情况,只能用方法4:dbms_xplan.display_cursor或方法6:awrsqrpt.sql
  4. 如果sql中含有函数,函数中有含有sql,即存在多层调用,想准确分析只能用方法5:10046追踪
  5. 想法看到真实的执行计划,不能用方法1:explain plan for和方法2:set autotrace on
  6. 想要获取表被访问的次数,只能用方法3:statistics_level = all