SQL是一种非过程化的语言,我们定义的sql语句仅仅告诉数据库我们需要什么样的数据,并没有告诉数据库如何获取这些数据。而执行计划正式数据库获取这些数据的方式和步骤。在日常开发中,为了优化数据库的需要,我们常常需要获取sql语句的执行计划,通过它判断
SQL是一种非过程化的语言,我们定义的sql语句仅仅告诉数据库我们需要什么样的数据,并没有告诉数据库如何获取这些数据。而执行计划正式数据库获取这些数据的方式和步骤。在日常开发中,为了优化数据库的需要,我们常常需要获取sql语句的执行计划,通过它判断语句执行性能的高低,进而进行优化。那么怎么获得执行计划呢?
下面分别详细介绍下及几种获取执行计划的方式:
explain plan命令可以将一条SQL语句预估的执行计划插入到计划表plan table,然后查询该表来获取预估的执行计划。 explain plan命令将SQL语句的执行计划插入到计划表,并不会执行隐式commit,所以如果想要持久化到数据库,需要显示commit。
提示:explain plan命令获得的执行计划是预估的,因为它并不是该SQL语句真正执行时的执行计划,随着oracle库中参数、表数据等因素的变化(比如表的数据的太少,虽然某些字段上建了索引,在执行时索引并不一定真正其效果,但是explain plan获得执行计划可能是走索引的),预估的执行计划往往和实际是有出入的。
预估的执行计划要存入plan table表,所以执行explain plan之前要确保plan table表的存在。在oracle 10g之前,plan table表是需要手动创建的,从oracle 10g开始,oracle会自动创建全局plan table表(表名为PLAN_TABLE),供所有用户使用。
如何要创建表plan_table,或创建其他的计划表呢?
找到$ORACLE_HOME/rdbms/admin/utlxplan.sql脚本文件,可以根据该脚本创建自己想要的计划表,只需要根据情况修改表名plan_table即可。
提示:关于计划表plan_table介绍请参考《Oracle 10g版本计划表PLAN_TABLE列的说明》
EXPLAIN PLAN
[SET STATEMENT_ID = 'statement_id']
[INTO table_name]
FOR sql_statement
说明:
1、方括号[]中的语句是可选的。
2、相关选项说明
<span></span>
选项 |
说明 |
statement_id |
标识存入表plan_table的sql语句的执行计划,不同sql语句的执行计划可以共用相同的statement_id标识,但是为了进行区分,尽量唯一,可以通过statement_id作为条件查询plan_table表中的执行计划。 |
table_name |
执行计划表的名称,如果没有指定,默认表名plan_table,如果指定的表不是plan_table表,而是自定义的,必须要确保该表和标准的计划表结果相同。 |
sql_statement |
sql语句,该sql语句可以包含绑定变量。 |
下面介绍两种查询执行计划的方法:使用dbms_xplan包、自定义层次查询语句,这两种方法都是从计划表查询数据,通过使用dbms_xplan包查询执行计划,默认计划表名为plan_table, 而且查询出来的参数数据也是固定的;但是通过自定义的层次查询语句不但可以自由指定计划表,还可以根据情况选择查询参数。
语句如下:
<span></span>
SELECT * FROM TABLE(dbms_xplan.display());
SQL> explain plan 2 for select * from t_student where gid = 1; Explained SQL> SELECT * FROM TABLE(dbms_xplan.display()); PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------- Plan hash value: 1947974170 -------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU) -------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 595 | 0 (0) | 1 | TABLE ACCESS BY INDEX ROWID| T_STUDENT | 1 | 595 | 0 (0) |* 2 | INDEX UNIQUE SCAN | SYS_C0010947 | 1 | | 0 (0) -------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("GID"=1) 14 rows selected
查询语句如下:
<span></span>
SELECT RTRIM(LPAD(' ', 2*LEVEL)|| RTRIM(operation)||' '|| RTRIM(options)||' '|| object_name) query_plan, cost, cardinality, time FROM plan_table CONNECT BY PRIOR id = parent_id START WITH ID = 0;
如:
SQL> explain plan 2 for select * from t_student where gid = 1; Explained SQL> SELECT RTRIM(LPAD(' ', 2*LEVEL)|| 2 RTRIM(operation)||' '|| 3 RTRIM(options)||' '|| 4 object_name) query_plan, cost, cardinality, time 5 FROM plan_table 6 CONNECT BY PRIOR id = parent_id 7 START WITH ID = 0; QUERY_PLAN COST CARDINALITY TIME -------------------------------------------------------------------------------- --------------------------------------- --------------------------------------- --------------------------------------- SELECT STATEMENT 0 1 1 TABLE ACCESS BY INDEX ROWID T_STUDENT 0 1 1 INDEX UNIQUE SCAN SYS_C0010947 0 1 1
上面1.1章节介绍查询的执行计划,都是预估的执行计划。而动态性能视图中缓存的SQL执行信息,则是真实的执行计划。
下面介绍一下几个动态视图:
通过动态性能视图查询较高SQL的执行计划的思路是:
1.通过挖掘V$SQL中具有较高时间消耗、CPU或IO需求的SQL语句。这一步通过自定义SQL完成。比如消耗时间最多的前10条语句,
查询语句如下:
<span></span>
SELECT sql_id, child_number, sql_text, elapsed_time FROM ( SELECT sql_id, child_number, sql_text, elapsed_time, cpu_time, disk_reads, RANK () OVER (ORDER BY elapsed_time DESC) AS elapsed_rank FROM v$sql) WHERE elapsed_rank <p><span><span><span> 2.<span>通过使用</span></span><span><span>V$SQL_PLAN和V$SQL_PLAN_STATICS找到不尽如人意的SQL语句的执行计划。这一步不需要我们写SQL,DBMS_XPLAN已经为提供可查询方法DBMS_XPLAN.display_cursor().具体使用请参考《DBMS_XPLAN包中函数的使用》。</span></span></span></span></p> <p><span><span><span><span><br> </span></span></span></span></p> <p><span><span><span> <span>例子:</span></span></span></span></p> <p><span><span></span></span></p><pre class="brush:php;toolbar:false">SQL> SELECT sql_id, child_number, sql_text, elapsed_time 2 FROM ( SELECT sql_id, child_number, sql_text, elapsed_time, cpu_time, disk_reads, 3 RANK () OVER (ORDER BY elapsed_time DESC) AS elapsed_rank 4 FROM v$sql) 5 WHERE elapsed_rank SELECT * FROM TABLE(DBMS_XPLAN.display_cursor('at5b8k7swu5w4', '0', 'TYPICAL')); PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------- SQL_ID at5b8k7swu5w4, child number 0 ------------------------------------- select * from t_student where gid = 2 Plan hash value: 1947974170 -------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU) -------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 1 (100) | 1 | TABLE ACCESS BY INDEX ROWID| T_STUDENT | 1 | 595 | 0 (0) |* 2 | INDEX UNIQUE SCAN | SYS_C0010947 | 1 | | 0 (0) -------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("GID"=2) 19 rows selected
工具EXPLAIN PLAN和DBMS_XPLAN可以获取预估执行计划和缓存中的执行信息,他们是主要的SQL调优工具,但是他们只是获取单个SQL的执行信息。如果想获取特定事务或是应用优化一批SQL语句,跟踪(Tracing)是个有效的方法。跟踪可以提供EXPLAIN PLAN与DBMS_XPLAIN能提供的所有信息,并且更加准确详尽,因为它是执行SQL时的真实的执行计划。
<span> SQL跟踪包括两个基本组成部分: </span><span> 1. SQL跟踪:在当前会话或其他会话中启动跟踪。 </span><span><span> 2. tkprof工具:格式化跟踪文件,因为生成的跟踪文件对使用者不容易读取,使用该工具可以转换为使用者跟容易读取的格式。具体请参加《tkprof命令格式化分析跟踪文件》</span></span>
<span><span> <strong>启动跟踪的方式有: </strong></span></span><span><span> 1. <span>启动</span>当前会话的跟踪 </span></span><span><span> 2.启动其他会话的跟踪 </span></span><span><span> 3.使用登陆触发器启动跟踪</span></span>
下面分别详细介绍3种跟踪启动的方式。
启动当前会话的跟踪的两种方法:
<span><span> <strong> 1.设置参数</strong><span><strong>SQL_TRACE 为true</strong>,语句如下: </span></span><span> <span> ALTER SESSION SET </span></span><span><span>SQL_TRACE </span><span>= TRUE;</span></span></span>
该语句创建基本的跟踪,跟踪信息包括SQL语句的执行统计信息与执行计划,但不会包含绑定变量的
值,也不包含等待各种事件花费的时间。如果要获取这些信息,请使用第2种启动方式。
<span> <strong> 2.<span>调用DBMS_SESSION方法</span></strong><span>,语法如下:</span> </span><span><span><span> <span>DBMS_SESSION.session_trace_enable ( </span></span></span><span><span><span> waits IN BOOLEAN DEFAULT TRUE, </span></span></span><span><span><span> binds IN BOOLEAN DEFAULT FALSE</span></span></span><span><span></span></span><span><span><span> );</span></span></span></span>
下面介绍下函数参数的含义:
参数 | 说明 |
waits | 如果为TRUE,将收集等待信息;如果为FALSE,则不收集。 |
binds | 如果为TRUE,将收集绑定变量信息;如果为FALSE,则不收集。 |
<span></span>
调用例子,在Command Window中输入如下命令:
<span></span>
begin dbms_session.session_trace_enable(true, true); end; /
<span></span>
执行完后,执行如下语句查询是否执行成功:
<span></span>
SQL> SELECT t.sql_trace, t.sql_trace_waits, t.sql_trace_binds FROM v$session t WHERE t.sid = USERENV('SID'); SQL_TRACE SQL_TRACE_WAITS SQL_TRACE_BINDS --------- --------------- --------------- ENABLED TRUE TRUE
SQL ID: 518m7y4zt6xkp<span><span><span> Plan Hash: 1947974170 select * from t_student where gid = 1 call count cpu elapsed disk query current rows ------- ------ -------- ---------- ---------- ---------- ---------- ---------- Parse 1 0.01 0.01 0 0 0 0 Execute 1 0.00 0.00 0 0 0 0 Fetch 1 0.00 0.01 2 2 0 1 ------- ------ -------- ---------- ---------- ---------- ---------- ---------- total 3 0.01 0.02 2 2 0 1 Misses in library cache during parse: 1 Optimizer mode: ALL_ROWS Parsing user id: 79 Rows Row Source Operation ------- --------------------------------------------------- 1 TABLE ACCESS BY INDEX ROWID T_STUDENT (cr=2 pr=2 pw=0 time=0 us cost=1 size=19 card=1) 1 INDEX UNIQUE SCAN SYS_C0010947 (cr=1 pr=1 pw=0 time=0 us cost=0 size=0 card=1)(object id 73110) Elapsed times include waiting on following events: Event waited on Times Max. Wait Total Waited ---------------------------------------- Waited ---------- ------------ SQL*Net message to client 2 0.00 0.00 SQL*Net message from client 2 0.04 0.04 Disk file operations I/O 1 0.00 0.00 db file sequential read 2 0.01 0.01</span></span></span>
启动会话跟踪之后,如何才能找到当前会话的跟踪文件呢?有两种方法
下面语句用来查询当前会话跟踪文件:
select p.TRACEFILE from v$session s, v$process p
where s.PADDR = p.ADDR
and s.SID = USERENV('SID');
说明:USERENV('SID')得到的是当前会话的SID。
如下:
<span></span>
SQL> select p.TRACEFILE from v$session s, v$process p 2 where s.PADDR = p.ADDR 3 and s.SID = USERENV('SID'); TRACEFILE -------------------------------------------------------------------------------- d:\oracle\diag\rdbms\david\orcl\trace\orcl_ora_7220.trc
<span></span>
为会话指定一个跟踪文件的标识符,通过设置tracefile_identifier参数实现:
ALTER SESSION SET tracefile_identifier = 标识符
如果我们设定tracefile_identifier为LGL,即执行:
SQL> alter session set tracefile_identifier=LGL;
SQL> show parameter user_dump_dest; NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ user_dump_dest string d:\oracle\diag\rdbms\david\orcl\trace
通过DBMS_MONITOR包中的session_trace_enable()方法启动别的会话的跟踪,语法如下:
<span><span><span><span> <span>DBMS_MONITOR.<span>session_trace_enable( </span></span></span></span></span><span><span><span><span><span> session_id IN BINARY DEFAULT NULL, </span></span></span></span></span><span><span><span><span><span> serial_num IN BINARY DEFAULT NULL, </span></span></span></span></span><span><span><span><span><span> waits IN BOOLEAN DEFAULT TRUE, </span></span></span></span></span><span><span><span><span><span> binds <span> IN BOOLEAN DEFAULT FALSE </span></span></span></span></span></span><span><span><span><span><span> )</span></span></span></span></span></span>
下面介绍下函数参数的含义:
<span></span>
参数 |
说明 |
session_id | 对应V$SESSION视图中的列SID的值。 |
serial_num | 对应V$SESSION视图中的列SERIAL#的值。 |
waits |
如果为TRUE,将收集等待信息;如果为FALSE,则不收集。 |
binds |
如果为TRUE,将收集绑定变量信息;如果为FALSE,则不收集。 |
<span></span>
SQL> SELECT t.sid, t.serial#, t.sql_trace FROM v$session t WHERE LOWER(t.service_name) LIKE '%david%'; SID SERIAL# SQL_TRACE ---------- ---------- --------- 7 249 DISABLED 8 41 ENABLED 71 125 ENABLED
<span><span> 通过上面查询结果可知SID为7的会话的没有启动会话跟踪,使用<span>session_trace_enable()方法启动SID为7的 </span></span></span><span><span><span>会话的跟踪,打开Command Window窗口,执行如下语句:</span></span></span>
<span></span>
begin dbms_monitor.session_trace_enable(session_id =>7, serial_num =>249, waits =>true, binds =>true); end; /
再次查询SID为7的会话跟踪情况,语句如下:
<span></span>
SQL> SELECT t.sid, t.serial#, t.sql_trace FROM v$session t WHERE t.sid = 7; SID SERIAL# SQL_TRACE ---------- ---------- --------- 7 249 ENABLED
从查询结果可以看出,SID为7的会话已经启动了跟踪。
<span></span>
CREATE OR REPLACE TRIGGER trace_login_trigger AFTER LOGON ON DATABASE BEGIN --设置用户ADMIN的会话跟踪 IF USER = 'ADMIN' THEN --设置跟踪文件标识 EXECUTE IMMEDIATE 'alter session set tracefile_identifier=ADMINCC'; --启动会话跟踪 DBMS_SESSION.session_trace_enable( waits =>TRUE, binds =>FALSE ); END IF; END; /