使用autotrace sqlplus系統參數:
SQL> set autotrace trace on
SQL> select * from dual;
DUM
---
X
Execution Plan
----------------------------------------------------------
Plan hash value: 272002086
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 2 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS FULL| DUAL | 1 | 2 | 2 (0)| 00:00:01 |
--------------------------------------------------------------------------
Statistics
----------------------------------------------------------
24 recursive calls
0 db block gets
6 consistent gets
4 physical reads
0 redo size
407 bytes sent via SQL*Net to client
385 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
各個資源解釋以下
Recursive Calls:sql
系統或者用戶執行sql時須要執行的相關內部遞歸sql。具體解釋可看下文
Number of recursive calls generated at both the user and system level.
Oracle Database maintains tables used for internal processing. When it needs to change these tables, Oracle Database generates an internal SQL statement, which in turn generates a recursive call. In short, 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 some other queries to get data dictionary information. These would be recursive calls. Space management, security checks, calling PL/SQL from SQL—all incur recursive SQL calls。
Recursive calls can be generated by the following activities:
一、An object requiring an additional extent for storage (dynamic extension)
二、Misses on the dictionary cache
三、Firing of database triggers
四、DDL statements
五、Execution of SQL statements within stored procedures, packages, functions, and anonymous PL/SQL blocks
六、Enforcement of referential integrity constraints
If Oracle is making an inordinate number of recursive calls, try to determine which of the previously listed activities is causing most of the recursive calls. Run the application through TKPROF with EXPLAIN PLAN to see what the application is doing.
Also, monitor the number of extents in the database to see if there is noticeable dynamic extension. If the recursive calls are caused by dynamic extension, you can reduce the number of calls by allocating larger extents to the relevant objects. A dictionary cache that is too small can also cause recursive calls.
DB Block Gets(當前請求的塊數目)
表示從當前內存中直接獲取,而不是經過undo表空間構造的一致性讀的塊數目。
Consistent Gets(數據請求總數在回滾段Buffer中的數據一致性讀所須要的數據塊)
這裏的概念 是在處理你這個操做的時候須要在一致性讀狀態上處理多少個塊,這些塊產生的主要緣由是由於因爲在你查詢的過程當中,因爲其餘會話對數據塊進行操做,而對所要 查詢的塊有了修改,可是因爲咱們的查詢是在這些修改以前調用的,因此須要對回滾段中的數據塊的前映像進行查詢,以保證數據的一致性。這樣就產生了一致性 讀。關於一致性讀,讀者可參考:
http://czmmiao.iteye.com/blog/1294307
Physical Reads(物理讀)
就是從磁盤上讀取數據塊的數量,其產生的主要緣由是:
一、 在數據庫高速緩存中不存在這些塊
二、 全表掃描
三、 磁盤排序
它們三者之間的關係大體可歸納爲:
邏輯讀指的是Oracle從內存讀到的數據塊數量。通常來講是'consistent gets' + 'db block gets'。當在內存中找不到所需的數據塊的話就須要從磁盤中獲取,因而就產生了'phsical reads'。
Sorts(disk):
Number of sort operations that required at least one disk write. Sorts that require I/O to disk are quite resource intensive. Try increasing the size of the initialization parameter SORT_AREA_SIZE.
磁盤中的排序
bytes sent via SQL*Net to client:
Total number of bytes sent to the client from the foreground processes.
經過網絡發送給客戶端的數據
bytes received via SQL*Net from client:
Total number of bytes received from the client over Oracle Net.
經過網絡從客戶端接收到的數據
SQL*Net roundtrips to/from client:
Total number of Oracle Net messages sent to and received from the client.
注意: autotrace 命令是先執行SQL,再出執行計劃,若是SQL耗時巨大,則不現實。
使用explain plan for語句:
SQL> explain plan for select * from dual;
Explained.
SQL> select * from table(DBMS_XPLAN.display);
Execution Plan
----------------------------------------------------------
Plan hash value: 2137789089
---------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 8168 | 16336 | 21 (0)| 00:00:01 |
| 1 | COLLECTION ITERATOR PICKLER FETCH| DISPLAY | | |
| |
---------------------------------------------------------------------------------------------
這樣就能夠在執行SQL以前查看執行計劃了
設置SQL_TRACE參數
設置當前session的 SQL_TRACE
SQL> alter session set SQL_TRACE=true;
SQL> select * from dual;
SQL> alter session set SQL_TRACE=false;
設置當前系統的 SQL_TRACE
SQL> alter system set SQL_TRACE=true;
對其餘用戶進行跟蹤設置:
SQL> select sid,serial#,username from v$session where username='XXX';
SID SERIAL# USERNAME
------ ---------- ------------------
127 31923 A
128 54521 B
129 48940 B
SQL> exec dbms_system.set_SQL_TRACE_in_session(127,31923,true);
SQL> select * from dual;
SQL> exec dbms_system.set_SQL_TRACE_in_session(127,31923,false);
而後使用oracle自帶的tkprof命令行工具格式化跟蹤文件。
使用10046事件進行查詢:
10046事件級別:
Lv1 - 啓用標準的SQL_TRACE功能,等價於SQL_TRACE
Lv4 - Level 1 + 綁定值(bind values)
Lv8 - Level 1 + 等待事件跟蹤
Lv12 - Level 1 + Level 4 + Level 8
全局設定:
..OracleHome/admin/SID/pfile中指定: EVENT="10046 trace name context forever,level 12"
當前session設定:
SQL> alter session set events '10046 trace name context forever, level 8';
SQL> select * from dual;
SQL> alter session set events '10046 trace name context off';
對其餘用戶進行設置:
SQL> select sid,serial#,username from v$session where username='XXX';
SID SERIAL# USERNAME
------ ---------- ------------------
127 31923 A
128 54521 B
129 48940 B
SQL> exec dbms_system.set_ev(127,31923,10046,8,'A');
SQL> select * from dual;
SQL> exec dbms_system.set_ev(127,31923,10046,0,'A');
注意:經常使用的查看執行計劃方法爲 autotrace和explain plan for。經過設置sql_trace和10046事件來查看執行計劃的狀況較少
注:沒法使用autotrace的解決辦法(9i):
SQL>start $ORACLE_HOME/rdbms/admin/utlxplan.sql;
SQL>create public synonym plan_table for plan_table;
SQL>grant ALL on plan_table to public;數據庫
如何讀懂執行計劃
建立實驗環境以下
SQL> create table t1(id int,name varchar2(500));
Table created.
SQL> create table t2(id int,name varchar2(500));
Table created.
SQL> create index ind_t1 on t1(id);
Index created.
SQL> create index ind_t2 on t2(id);
Index created.
SQL> create index ind_t2_name on t2(name);
Index created.
SQL> insert into t1 select object_id,object_name from dba_objects;
50430 rows created.
SQL> exec dbms_stats.gather_table_stats('HR','T1',cascade=>true,method_opt=>'for all indexed columns');
PL/SQL procedure successfully completed.
SQL> set autotrace trace explain
執行以下查詢
SQL> select * from t1 where id in (select ID FROM t2 where name='AA')
Execution Plan
----------------------------------------------------------
Plan hash value: 3232435503
--------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 293 | 5 (20)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| T1 | 1 | 28 | 2 (0)| 00:00:01 |
| 2 | NESTED LOOPS | | 1 | 293 | 5 (20)| 00:00:01 |
| 3 | SORT UNIQUE | | 1 | 265 | 2 (0)| 00:00:01 |
|* 4 | TABLE ACCESS FULL | T2 | 1 | 265 | 2 (0)| 00:00:01 |
|* 5 | INDEX RANGE SCAN | IND_T1 | 1 | | 1 (0)| 00:00:01 |
--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - filter("NAME"='AA')
5 - access("ID"="ID")
Note
-----
- dynamic sampling used for this statement
以上執行計劃中各列的意義以下
ID:序號,注意,ID的到小並不表明執行的前後順序
Operation:當前的操做內容
Rows列:當前操做的cardinality(9i),Oracle估算當前操做的返回結果集的行數,這個行源多是一個表,一個索引,也多是一個子查詢。
Bytes:操做的數據量大小
Cost(cpu):Oracle計算出來的一個數值(代價),用於說明SQL執行的代價。
Time:Oracle估算當前操做所需的時間。
執行計劃中縮進度越大,表示越早執行,當兩行的縮進同樣時,最上面的最早被執行。上面的執行計劃需從 縮進度 最大的行讀取,他是最早執行的步驟。具體過程爲: ID4->ID3->ID5->ID2->ID1->ID0
翻譯成文字以下:
在t2表中根據name=AA查找匹配的行,找出全部行並排序後與t1表上的索引ID_1中相應的ID進行關聯,而後重複,直到把排序的結果集掃描完,這 個過程叫作nested loops。當整個T2表掃描完後,會產生一個結果集,這個結果集是IND_T1的一個索引結果集。而後Oracle根據索引鍵值上的rowid去T1表 中找相應的記錄,即ID1.而後返回結果,即ID0。
上面的filter和access都是按照謂詞的條件對數據進行過濾的方式,他們的區別以下:
filter,表示謂詞條件的值並不會影響數據訪問路徑,只起到過濾的做用。
access,表示謂詞條件的值會影響數據的訪問路徑(表仍是索引,這裏是索引)。
結果集Rows對執行計劃的影響
當前操做的rows(9i中爲cardinality,10g替換爲rows)表示Oracle估算當 前操做的返回結果集的行數,這個行源多是一個表,一個索引,也多是一個子查詢。rows表示CBO估算當前操做預期獲取的記錄數,這個值對CBO作出 正確的執行計劃相當重要。若是CBO得到的rows不許確(一般是沒有作分析或者分析數據過舊致使的),在執行成本計算上會出現誤差,從而致使CBO產生 錯誤的出執行計劃 。具體可參加下列例子
SQL> create table t as select 1 id,object_name from dba_objects;
Table created.
SQL> update t set id=99 where rownum=1;
1 row updated.
SQL> commit;
Commit complete.
SQL> create index t_ind on t(id);
Index created.
採用dynamic_sampling(t 0)的方式禁止對t表的動態採用。
SQL> select /*+ dynamic_sampling(t 0) */ * from t where id=1;
Execution Plan
----------------------------------------------------------
Plan hash value: 1376202287
-------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 194 | 15326 | 50 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| T | 194 | 15326 | 50 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | T_IND | 77 | | 49 (0)| 00:00:01 |
-------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("ID"=1)
能夠看到CBO猜想ID爲1的數據有194條,相對於總數來講較少,故採用了索引而不是全表掃描。
SQL> select * from t where id=1;
Execution Plan
----------------------------------------------------------
Plan hash value: 1601196873
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 47242 | 3644K| 56 (4)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| T | 47242 | 3644K| 56 (4)| 00:00:01 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("ID"=1)
Note
-----
- dynamic sampling used for this statement
上面的查詢中,CBO經過動態採用,估算出表中的實際數據量爲47242,很是接近實際數據量50249,故採用了全表掃描。
可見,rows的多少直接影響了執行計劃的選擇
在多表關聯查詢或者SQL中有自查詢時,每一個關聯表或是自查詢的rows對主表的影響很是大,甚至能夠說,CBO就是依賴於各個關聯表或者子查詢rows值來計算出最後的執行計劃。
對於多表查詢,CBO使用每一個關聯表返回的行數決定使用什麼樣的訪問方式來作關聯(好比nested loops join或是hash join);對於子查詢,它的rows將決定子查詢是使用索引仍是使用全表掃描的方式訪問數據。
實驗步驟以下
採用cardinality(t2 1000)的方式告訴CBO從T2表中獲取10000條記錄;
SQL>select * from t1 where id in (select /*+ dynamic_sampling(t2 0) cardinality(t2 10000) */ id from t2 where name='AA')
Execution Plan
----------------------------------------------------------
Plan hash value: 2007208103
----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 50430 | 14M| | 288 (2)| 00:00:04 |
|* 1 | HASH JOIN SEMI | | 50430 | 14M| 1976K| 288 (2)| 00:00:04 |
| 2 | TABLE ACCESS FULL | T1 | 50430 | 1378K| | 56 (2)| 00:00:01 |
| 3 | TABLE ACCESS BY INDEX ROWID| T2 | 10000 | 2587K| | 1 (0)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | IND_T2_NAME | 1 | | | 1 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("ID"="ID")
4 - access("NAME"='AA')
經過 cardinality(t2 1000)的方式模擬出了子查詢中返回的結果集數,所以生成執行計劃時,CBO選擇了HASH JOIN SEMI。
當結果集變小時執行計劃以下
SQL> select * from t1 where id in (select /*+ dynamic_sampling(t2 0) cardinality(t2 1) */ id from t2 where name='AA')
Execution Plan
----------------------------------------------------------
Plan hash value: 2850018061
----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 293 | 4 (25)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID | T1 | 1 | 28 | 2 (0)| 00:00:01 |
| 2 | NESTED LOOPS | | 1 | 293 | 4 (25)| 00:00:01 |
| 3 | SORT UNIQUE | | 1 | 265 | 1 (0)| 00:00:01 |
| 4 | TABLE ACCESS BY INDEX ROWID| T2 | 1 | 265 | 1 (0)| 00:00:01 |
|* 5 | INDEX RANGE SCAN | IND_T2_NAME | 1 | | 1 (0)| 00:00:01 |
|* 6 | INDEX RANGE SCAN | IND_T1 | 1 | | 1 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
5 - access("NAME"='AA')
6 - access("ID"="ID")
能夠看到由於子查詢的結果集爲1,因此CBO選擇了netsted loops的方式進行關聯。
兩表關聯的執行計劃結果與上面相似,讀者可根據以下語句自行進行實驗,筆者在此也再也不贅述。
select /*+ dynamic_sampling(t2 10000) cardinality */ * from t1,t2 where t1.id=t2.id;
select /*+ dynamic_sampling(t2 1) cardinality */ * from t1,t2 where t1.id=t2.id;
以上的例子主要說明了rows對CBO生成執行計劃的影響,在看執行計劃時要特別關注。必定要注意每一個操做的rows值,若是這個值明顯不對,那麼極可能操做的表的分析數據出了問題或者表沒有分析。
DDL的執行計劃
從Oracle10g開始,能夠經過EXPLAIN PLAN FOR查看DDL語句的執行計劃了。
在9i及之前版本,Oracle只能看到DML的執行計劃,不過從10g開始,經過EXPLAIN PLAN FOR的方式,已經能夠看到DDL語句的執行計劃了。
這對於研究CREATE TABLE AS SELECT、CREATE MATERIALIZED VIEW AS SELECT以及CREATE INDEX,ALTER INDEX REBUILD等語句有很大的幫助。
舉個簡單的例子,Oracle的文檔上對於索引的創建有以下描述:
The optimizer can use an existing index to build another index. This results in a much faster index build.
若是看不到DDL的執行計劃,只能根據執行時間的長短去猜想Oracle的具體執行計劃,但這種方法沒有足夠的說服力。可是經過DDL的執行計劃,就使得結果一目瞭然了。下面就來驗證下 Oracle文檔上的說法
SQL> create table t_ddl as select * from dba_objects;
Table created.
SQL> explain plan for create index t_ddl_idx on t_ddl(object_name);
Explained.
SQL> select * from table(dbms_xplan.display);
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 1333762510
------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------
| 0 | CREATE INDEX STATEMENT | | 58238 | 3753K| 297 (1)| 00:00:04 |
| 1 | INDEX BUILD NON UNIQUE| T_DDL_IDX | | | | |
| 2 | SORT CREATE INDEX | | 58238 | 3753K| | |
| 3 | TABLE ACCESS FULL | T_DDL | 58238 | 3753K| 160 (2)| 00:00:02 |
------------------------------------------------------------------------------------
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Note
-----
- estimated index size: 5242K bytes
14 rows selected.
SQL> create index t_owner_name on t_ddl(owner,object_name);
Index created.
SQL> explain plan for create index t_name on t_ddl(object_name);
Explained.
SQL> select * from table(dbms_xplan.display);
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 2111373224
---------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------
| 0 | CREATE INDEX STATEMENT | | 58238 | 3753K| 297 (1)| 00:00:04 |
| 1 | INDEX BUILD NON UNIQUE| T_NAME | | | | |
| 2 | SORT CREATE INDEX | | 58238 | 3753K| | |
| 3 | INDEX FAST FULL SCAN| T_OWNER_NAME | | | | |
---------------------------------------------------------------------------------------
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Note
-----
- estimated index size: 5242K bytes
14 rows selected.
能夠看到,根據現有的建立索引建立索引的速度確實會更快。筆者個認爲,對於DDL的執行計劃,最有價值的是在進行DDL操做前的開銷評估。
SQL> SET AUTOT ON
SQL> CREATE INDEX IND_T_NAME ON T(OBJECT_NAME);
Index created.
注意,查看DDL的執行計劃須要使用EXPLAIN PLAN FOR,AUTOTRACE對於DDL是無效的。 緩存