Oracle之SQL優化專題03-如何看懂SQL的執行計劃

專題第一篇《Oracle之SQL優化專題01-查看SQL執行計劃的方法》講到了查看SQL執行計劃的方法,並介紹了各類方法的應用場景,那麼這一篇就主要介紹下如何看懂SQL的執行計劃。畢竟若是SQL的執行計劃都看不懂,那優化就無從談起了。html

關於如何看懂SQL的執行計劃,我把它簡單分爲3個部分:session

1.判斷執行計劃的執行順序

口訣:先子後父,先上後下。
通常簡單的執行計劃能夠直接根據這個口訣來判斷執行計劃的執行順序。相似的口訣還有"最右最上"之類,其表達的意思都是同樣的,選擇一種本身易接受的口訣記憶便可。
舉一個簡單的例子:ide

SQL> show user  
USER is "SYS"
SQL> alter session set current_schema=scott;
SQL> alter session set statistics_level = ALL;
SQL> select a.empno, a.ename, b.dname, a.job, a.sal from emp a, dept b
  2  where a.deptno = b.deptno and empno = 7788
  3  ;

     EMPNO ENAME      DNAME          JOB              SAL
---------- ---------- -------------- --------- ----------
      7788 SCOTT      RESEARCH       ANALYST         3000

SQL> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID  19ktq5t44xsyp, child number 0
-------------------------------------
select a.empno, a.ename, b.dname, a.job, a.sal from emp a, dept b where
a.deptno = b.deptno and empno = 7788

Plan hash value: 2385808155

--------------------------------------------------------------------------------------------------
| Id  | Operation                    | Name    | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
--------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |         |      1 |        |      1 |00:00:00.01 |       4 |
|   1 |  NESTED LOOPS                |         |      1 |      1 |      1 |00:00:00.01 |       4 |
|   2 |   TABLE ACCESS BY INDEX ROWID| EMP     |      1 |      1 |      1 |00:00:00.01 |       2 |
|*  3 |    INDEX UNIQUE SCAN         | PK_EMP  |      1 |      1 |      1 |00:00:00.01 |       1 |
|   4 |   TABLE ACCESS BY INDEX ROWID| DEPT    |      1 |      1 |      1 |00:00:00.01 |       2 |
|*  5 |    INDEX UNIQUE SCAN         | PK_DEPT |      1 |      1 |      1 |00:00:00.01 |       1 |
--------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("EMPNO"=7788)
   5 - access("A"."DEPTNO"="B"."DEPTNO")


24 rows selected.

執行計劃的執行順序(按Id區分)就是 3 -> 2 -> 5 -> 4 -> 1 -> 0.優化

這裏須要特別注意的是:
1)實際上這個所謂的口訣獲得的執行順序只是爲了方便咱們理解操做數據的順序,而執行計劃實際上是按照Id從上往下遞歸調用的,簡單說其實優化器首先判斷是一條select語句,涉及多表關聯,關聯方式採用了NL Join,而後這個NL Join包含分別對EMP和DEPT索引回表的訪問。this

2)理解了第一點,那對於SQL語句中select部分含有標量子查詢的部分不遵循這個口訣就更好理解了(由於實際這個標量子查詢是最後執行的)。
舉一個簡單例子說明這類標量子查詢:code

SQL> select a.empno, a.ename, b.dname, a.job, a.sal, (select * from dual) x from emp a, dept b
  2  where a.deptno = b.deptno and empno = 7788
  3  ;

     EMPNO ENAME      DNAME          JOB              SAL X
---------- ---------- -------------- --------- ---------- -
      7788 SCOTT      RESEARCH       ANALYST         3000 X

SQL> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID  dpbagkc0c6ctv, child number 0
-------------------------------------
select a.empno, a.ename, b.dname, a.job, a.sal, (select * from dual) x
from emp a, dept b where a.deptno = b.deptno and empno = 7788

Plan hash value: 1121436124

--------------------------------------------------------------------------------------------------
| Id  | Operation                    | Name    | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
--------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |         |      1 |        |      1 |00:00:00.01 |       4 |
|   1 |  TABLE ACCESS FULL           | DUAL    |      1 |      1 |      1 |00:00:00.01 |       2 |
|   2 |  NESTED LOOPS                |         |      1 |      1 |      1 |00:00:00.01 |       4 |
|   3 |   TABLE ACCESS BY INDEX ROWID| EMP     |      1 |      1 |      1 |00:00:00.01 |       2 |
|*  4 |    INDEX UNIQUE SCAN         | PK_EMP  |      1 |      1 |      1 |00:00:00.01 |       1 |
|   5 |   TABLE ACCESS BY INDEX ROWID| DEPT    |      1 |      1 |      1 |00:00:00.01 |       2 |
|*  6 |    INDEX UNIQUE SCAN         | PK_DEPT |      1 |      1 |      1 |00:00:00.01 |       1 |
--------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   4 - access("EMPNO"=7788)
   6 - access("A"."DEPTNO"="B"."DEPTNO")


25 rows selected.

執行計劃的執行順序(按Id區分),若是按口訣來看就是 1 -> 4 -> 3 -> 6 -> 5 -> 2 -> 0.
而因爲這其中1是標量子查詢,實際要最後執行,因此真實的執行順序爲 4 -> 3 -> 6 -> 5 -> 2 -> 1 -> 0.orm

2.理解執行計劃每步的含義

以上面的執行計劃爲例,要清楚知道每一列表明的含義:htm

  • Id
    操做id號;對象

  • Operation
    這一列,能夠看到SQL的每一步操做,深刻理解就須要清楚表的主要訪問方式(TABLE ACCESS FULL、TABLE ACCESS BY INDEX ROWID、TABLE ACCESS BY USER ROWID),索引的主要訪問方式(INDEX UNIQUE SCAN、INDEX RANGE SCAN、INDEX FULL SCAN、INDEX FAST FULL SCAN、INDEX SKIP SCAN),多表查詢還須要清楚錶鏈接的方法(NESTED LOOP JOIN、SORT MERGE JOIN、HASH JOIN、MERGE JOIN CARTESIAN)和順序(先訪問驅動表)等知識;blog

  • Name
    這一列,就是操做的具體對象名稱;

  • Starts
    這一列,表明訪問次數;好比在NESTED LOOP JOIN中,被驅動表的具體訪問次數就能夠依據Starts的數值來判斷。

  • E-Rows
    預估返回行數;

  • A-Rows
    實際返回行數;

  • A-Time
    實際執行時間;

  • Buffers
    邏輯讀塊/次;

若是是其餘方式,看到的執行計劃內容有所不一樣,好比可能就是下面這樣:

Execution Plan
----------------------------------------------------------
Plan hash value: 1121436124

----------------------------------------------------------------------------------------
| Id  | Operation                    | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |         |     1 |    38 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS FULL           | DUAL    |     1 |     2 |     2   (0)| 00:00:01 |
|   2 |  NESTED LOOPS                |         |     1 |    38 |     2   (0)| 00:00:01 |
|   3 |   TABLE ACCESS BY INDEX ROWID| EMP     |     1 |    25 |     1   (0)| 00:00:01 |
|*  4 |    INDEX UNIQUE SCAN         | PK_EMP  |     1 |       |     0   (0)| 00:00:01 |
|   5 |   TABLE ACCESS BY INDEX ROWID| DEPT    |     1 |    13 |     1   (0)| 00:00:01 |
|*  6 |    INDEX UNIQUE SCAN         | PK_DEPT |     1 |       |     0   (0)| 00:00:01 |
----------------------------------------------------------------------------------------

能夠看到,前三列沒有區別,後面只有Rows、Bytes、Cost (%CPU)、Time等信息。

3.瞭解執行計劃相關的信息

執行計劃下面還有相關的信息,相似以下:

Predicate Information (identified by operation id):
---------------------------------------------------

   4 - access("EMPNO"=7788)
   6 - access("A"."DEPTNO"="B"."DEPTNO")

Note
-----
   - SQL plan baseline SQL_PLAN_9sdj6nc4ybu5x2b78d17a used for this statement

Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          6  consistent gets
          0  physical reads
          0  redo size
        879  bytes sent via SQL*Net to client
        520  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed
  • 謂詞信息(Predicate Information)
    標識出哪一步有access、filter這樣的操做;

  • Note部分(Note)
    若是SQL執行中用到了動態採樣、基數反饋、自適應遊標共享、SQL_Profile、SPM等,會在這裏顯示出來;

  • 統計信息(Statistics) 這裏指的是SQL執行實際發生的遞歸調用、邏輯讀、物理讀、排序、處理行數等統計信息。

相關文章
相關標籤/搜索