一. 10053事件
當一個SQL出現性能問題的時候,可以使用SQL_TRACE 或者 10046事件來跟蹤SQL. 通過生成的trace來了解SQL的執行過程。 我們在查看一條SQL的執行計划的時候,只能看到CBO 最終告訴我們的執行計划結果,但是不知道CBO 是根據什么來做的。 如果遇到了執行計划失真,如:一個SQL語句,很明顯oracle應該使用索引,但是執行計划卻沒有使用索引。無法進行分析判斷。
而10053事件就提供了這樣的功能。它產生的trace文件提供了Oracle如何選擇執行計划,為什么會得到這樣的執行計划信息。
10053事件生成trace文件目錄和SQL_TRACE一樣。
在Oracle 10g中,SQL_TRACE生成的trace文件默認路勁是$ORACLE_BASE/admin/SID/udump.
在Oracle 11g,trace 默認路徑在:$ORACLE_BASE/diag/rdbms/orcl/orcl/trace目錄下
對於10053事件的trace文件,我們只能直接閱讀原始的trace文件,不能使用tkprof工具來處理,tkprof工具只能用來處理sql_trace 和 10046事件產生的trace文件。
10053事件有兩個級別:
Level 2:2級是1級的一個子集,它包含以下內容:
Column statistics
Single Access Paths
Join Costs
Table Joins Considered
Join Methods Considered (NL/MS/HA)
Level 1: 1級比2級更詳細,它包含2級的所有內容,在加如下內容:
Parameters used by the optimizer
Index statistics
1.1啟用10053事件:

ALTER SESSION SET EVENTS='10053 trace name context forever, level 1'; ALTER SESSION SET EVENTS='10053 trace name context forever, level 2';
1.2關閉10053事件:

ALTER SESSION SET EVENTS '10053 trace name context off';
說明:
(1)sqlplus中打開autotrace看到的執行計划實際上是用explain plan 命令得到的,explain plan 命令不會進行bind peeking。應該通過v$sql_plan查看SQL的真實的執行計划。
(2)10053只對CBO有效,而且如果一個sql語句已經解析過,就不會產生新的trace信息。
二. 實驗10053事件:
1.設定當前的trace 文件
1.1 設定trace 文件名稱

SQL> alter session set tracefile_identifier='10053事件'; 會話已更改。
設置標識的目的就是方便我們查找生成的trace文件。我們只需要在trace目錄查找文件名里帶有標識的文件即可。
1.2直接用如下SQL直接查出,當前的trace文件名。

SELECT d.VALUE || '/' || LOWER (RTRIM (i.INSTANCE, CHR (0))) || '_ora_' || p.spid || '.trc' AS "trace_file_name" FROM (SELECT p.spid FROM v$mystat m, v$session s, v$process p WHERE m.statistic# = 1 AND s.SID = m.SID AND p.addr = s.paddr) p, (SELECT t.INSTANCE FROM v$thread t, v$parameter v WHERE v.NAME = 'thread' AND (v.VALUE = 0 OR t.thread# = TO_NUMBER (v.VALUE))) i, (SELECT VALUE FROM v$parameter WHERE NAME = 'user_dump_dest') d;
2.啟動10053事件

SQL> ALTER SESSION SET EVENTS='10053 trace name context forever, level 1';
3.執行事務

SQL> select * from pub_user u, pub_department dept where u.department_id = dept.department_id; SQL>Explain plan for select * from pub_user u, pub_department dept where u.department_id = dept.department_id;
4.關閉10053事件

SQL> ALTER SESSION SET EVENTS '10053 trace name context off';
三. 查看生成的trace文件
在此之前設置了標識,所以直接進入trace目錄,找到含有 ‘10053事件’標識的trace 文件。
Trace file D:\oracle\product\10.2.0\admin\dw\udump/10053事件.trc
四、10053事件內容解析
1. Predicate Move-Around (PM)(對SQL語句的謂詞進行分析、重寫,把它改為最符合邏輯的SQL語句)
2. 解釋trace文件用到的一些縮寫的指標定義
3. Peeked values of the binds in SQL statement(綁定變量的描述)
4. Bug Fix Control Environment(一些修復的bug信息)
5. PARAMETERS WITH DEFAULT VALUES(性能相關的初始化參數)
6. BASE STATISTICAL INFORMATION(SQL引用對象的基本信息)
7. CBO計算每個對象單獨訪問的代價
8. CBO計算列出兩個表關聯方式,並計算出每一種關聯方式的代價,最終選擇最小的cost
五、實驗:10053事件的妙用
在我們寫sql時,一條明顯可以查詢出來數據的語句,為什么我們寫完之后卻不返回數據?這時,10053可以解答我們的疑問。
見如下order by 查不出數據實驗:

---10.2.0.1版本加了order by查不出數據實驗 Drop table test1 purge; Drop table test2 purge; create table test1 (id number(20),name varchar2(20)); insert into test1 values (1,'A'); insert into test1 values (2,'A'); insert into test1 values (3,'A'); insert into test1 values (4,'A'); insert into test1 values (5,'B'); insert into test1 values (6,'B'); insert into test1 values (7,'C'); insert into test1 values (8,'C'); insert into test1 values (9,'C'); insert into test1 values (10,'C'); create table test2 (id number(20),name varchar2(20)); insert into test2 values (1,'A'); insert into test2 values (2,'A'); insert into test2 values (3,'A'); insert into test2 values (4,'A'); insert into test2 values (5,'A'); insert into test2 values (6,'A'); insert into test2 values (7,'A'); insert into test2 values (8,'B'); insert into test2 values (9,'C'); insert into test2 values (10,'C');

SELECT * FROM (SELECT * FROM (SELECT INNER_TABLE.*, ROWNUM OUTER_TABLE_ROWNUM FROM (select test2.* from test2, (SELECT t.id, t.name FROM test1 T WHERE T.id = (SELECT MAX(T1.id) FROM test1 T1 WHERE T.name = T1.name)) test1 where test2.name = test1.name order by test2.name ---加上order by就沒有數據 ) INNER_TABLE) WHERE OUTER_TABLE_ROWNUM <= 18) OUTER_TABLE WHERE OUTER_TABLE_ROWNUM > 0;

SELECT * FROM (SELECT * FROM (SELECT INNER_TABLE.*, ROWNUM OUTER_TABLE_ROWNUM FROM (select test2.* from test2, (SELECT t.id, t.name FROM test T WHERE T.id in (SELECT MAX(T1.id) FROM test T1 group by name)) test1 where test2.name = test1.name order by test2.name) INNER_TABLE) WHERE OUTER_TABLE_ROWNUM <= 18) OUTER_TABLE WHERE OUTER_TABLE_ROWNUM > 0;