Running a SQL trace is something that all DBAs do to varying degrees. Let’s say you are working on optimizing a SQL statement, and experimenting with some different hints for indexes and optimizer directives. This kind of effort typically goes something like this:
- modify the SQL statement
- enable tracing
- run the statement
- disable tracing
- disconnect
- retrieve the trace file
- use a profiler to process the trace file
this might be Method-R mrskew,Oracle tkprof, or something of your own. - delete the trace file if no longer needed
That process is OK if all you need to do is look at a couple of trace files, but quickly becomes tedious for any serious optimization effort as there will be many iterations of this process. This is the kind of job that just cries out for some simple automation.
Let’s walk though automating much of this process using Sqlplus, ssh and some profiling tools.
First let’s consider the environment:
- Oracle 11.2 database on a remote server
- Workstation has 11.2 client software installed
- ssh is setup for connecting to the oracle user on the database server
- some profiling tools are available
Let’s get started with the script that is the subject of our ‘tuning’ effort.
-- sql2trace.sql select * from dual;
As you can see there is not really going to be any tuning done in this article; it is all about the process.
The following script tracefile_identifier_demo.sql is used to setup the trace environment by collecting some information about the database host the process owner, and then setting the tracefile_identifier parameter. The values for these are then used to set sqlplus define variables.
-- tracefile_identifier_demo.sql -- column variables to capture host, owner and tracefile name col tracehost new_value tracehost noprint col traceowner new_value traceowner noprint col tracefile new_value tracefile noprint set term off head off feed off -- get oracle owner select username traceowner from v$process where pname = 'PMON'; -- get host name select host_name tracehost from v$instance; -- set tracefile identifier alter session set tracefile_identifier = 'MYTRACEFILE'; select value tracefile from v$diag_info where name = 'Default Trace File'; set term on head on feed on -- do your tracing here alter session set events '10046 trace name context forever, level 12'; -- run your SQL here @@sql2trace alter session set events '10046 trace name context off'; -- disconnect to ensure all trace data flushed -- the disconnect must be done in the called script -- otherwise the values of the defined vars are lost -- now get the trace file, or other processing --@@mrskew '&&traceowner@&&tracehost' '&&tracefile' @@tkprof '&&traceowner@&&tracehost' '&&tracefile'
This article began as an idea to write about tracefile_identier, hence the script name.
Most of this script is quite straightforward:
- set column command initiated define variables to capture host, process owner and tracefile name
- collect the data
- enable tracing
- run the target script
- disable tracing
- call the tkprof.sql script to run tkprof
The interesting bit is found in tkprof.sql.
-- tkprof.sql col ssh_target new_value ssh_target noprint col scp_filename new_value scp_filename noprint set term off feed off verify off echo off select '&&1' ssh_target from dual; select '&&2' scp_filename from dual; set feed on term on verify on disconnect host ssh &&ssh_target 'cat &&scp_filename' | tkprof /dev/stdin ./tkprof.out sort=exeqry sys=no host cat ./tkprof.out
There are a couple of things to take notice of in tkprof.sql. Did you notice the disconnect statement? There are couple of points of interest about that. Prior to 11g it was necessary to disconnect from Oracle to ensure that all cursors were closed and all STAT and row source operation rows were written to the trace file. Disconnecting the session is not necessary in Oracle 11g+.
Another interesting bit about this disconnect statement is its placement. At first the disconnect statement was in the main script. The problem was that the define variables would all lose their values prior to calling the tkprof.sql script, and so the call would fail; and so the disconnect command is in the called script.
Finally the trace output is retrieved via ssh and piped to tkprof. Notice that there is no need to actually copy the file, rather the contents of the file are simple sent to STDOUT and piped to tkprof.
The tkprof command does not read from STDIN. If for instance you try this; cat somefile | tkprof – ./tkprof.out sort=exeqry; tkprof will exit with an error that an input file is needed. That problem is circumvented by using the file /dev/stdin.
Put it all together and it looks like this:
11:34:11 JKSTILL@oravm > @tracefile_identifier_demo Session altered. Elapsed: 00:00:00.00 D - X 1 row selected. Elapsed: 00:00:00.00 Session altered. Elapsed: 00:00:00.00 TKPROF: Release 11.2.0.3.0 - Development on Thu Aug 27 11:34:18 2015 Copyright (c) 1982, 2011, Oracle and/or its affiliates. All rights reserved. Host key fingerprint is de:ad:be:ed:a2:d6:63:4b:rx:77:fd:1c:e1:36:2b:88 +--[ RSA 2048]----+ | | | | | | | . . | | S +. | | ..ox.o | | o+.F.* o | | 99+o.o.= . | | . ..+y.ooo | +-----------------+ TKPROF: Release 11.2.0.3.0 - Development on Thu Aug 27 11:34:18 2015 Copyright (c) 1982, 2011, Oracle and/or its affiliates. All rights reserved. Trace file: /dev/stdin Sort options: exeqry ******************************************************************************** count = number of times OCI procedure was executed cpu = cpu time in seconds executing elapsed = elapsed time in seconds executing disk = number of physical reads of buffers from disk query = number of buffers gotten for consistent read current = number of buffers gotten in current mode (usually for update) rows = number of rows processed by the fetch or execute call ******************************************************************************** SQL ID: a5ks9fhw2v9s1 Plan Hash: 272002086 select * from dual call count cpu elapsed disk query current rows ------- ------ -------- ---------- ---------- ---------- ---------- ---------- Parse 1 0.00 0.00 0 0 0 0 Execute 1 0.00 0.00 0 0 0 0 Fetch 2 0.00 0.00 0 2 0 1 ------- ------ -------- ---------- ---------- ---------- ---------- ---------- total 4 0.00 0.00 0 2 0 1 Misses in library cache during parse: 0 Optimizer mode: ALL_ROWS Parsing user id: 90 Number of plan statistics captured: 1 Rows (1st) Rows (avg) Rows (max) Row Source Operation ---------- ---------- ---------- --------------------------------------------------- 1 1 1 TABLE ACCESS FULL DUAL (cr=2 pr=0 pw=0 time=22 us cost=2 size=2 card=1) 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 log file sync 1 0.00 0.00 SQL*Net message from client 2 0.00 0.00 ******************************************************************************** SQL ID: 06nvwn223659v Plan Hash: 0 alter session set events '10046 trace name context off' call count cpu elapsed disk query current rows ------- ------ -------- ---------- ---------- ---------- ---------- ---------- Parse 1 0.00 0.00 0 0 0 0 Execute 1 0.00 0.00 0 0 0 0 Fetch 0 0.00 0.00 0 0 0 0 ------- ------ -------- ---------- ---------- ---------- ---------- ---------- total 2 0.00 0.00 0 0 0 0 Misses in library cache during parse: 0 Parsing user id: 90 ******************************************************************************** OVERALL TOTALS FOR ALL NON-RECURSIVE STATEMENTS call count cpu elapsed disk query current rows ------- ------ -------- ---------- ---------- ---------- ---------- ---------- Parse 2 0.00 0.00 0 0 0 0 Execute 2 0.00 0.00 0 0 0 0 Fetch 2 0.00 0.00 0 2 0 1 ------- ------ -------- ---------- ---------- ---------- ---------- ---------- total 6 0.00 0.00 0 2 0 1 Misses in library cache during parse: 0 Elapsed times include waiting on following events: Event waited on Times Max. Wait Total Waited ---------------------------------------- Waited ---------- ------------ SQL*Net message to client 3 0.00 0.00 SQL*Net message from client 3 0.00 0.00 log file sync 1 0.00 0.00 OVERALL TOTALS FOR ALL RECURSIVE STATEMENTS call count cpu elapsed disk query current rows ------- ------ -------- ---------- ---------- ---------- ---------- ---------- Parse 0 0.00 0.00 0 0 0 0 Execute 1 0.00 0.00 0 0 3 1 Fetch 0 0.00 0.00 0 0 0 0 ------- ------ -------- ---------- ---------- ---------- ---------- ---------- total 1 0.00 0.00 0 0 3 1 Misses in library cache during parse: 0 2 user SQL statements in session. 1 internal SQL statements in session. 3 SQL statements in session. ******************************************************************************** Trace file: /dev/stdin Trace file compatibility: 11.1.0.7 Sort options: exeqry 1 session in tracefile. 2 user SQL statements in trace file. 1 internal SQL statements in trace file. 3 SQL statements in trace file. 3 unique SQL statements in trace file. 218 lines in trace file. 0 elapsed seconds in trace file.
The same process was used to run the trace data through the Method-R mrskew command:
-- mrskew.sql col ssh_target new_value ssh_target noprint col scp_filename new_value scp_filename noprint set term off feed off verify off echo off select '&&1' ssh_target from dual; select '&&2' scp_filename from dual; set feed on term on verify on
--disconnect host ssh &&ssh_target 'cat &&scp_filename' | mrskew
The results of calling mrskew.sql rather than tkprof.sql:
CALL-NAME DURATION % CALLS MEAN MIN MAX
————————— ——– —— —– ——– ——– ——–
SQL*Net message from client 0.003733 74.1% 3 0.001244 0.001004 0.001663
log file sync 0.001300 25.8% 1 0.001300 0.001300 0.001300
SQL*Net message to client 0.000008 0.2% 3 0.000003 0.000002 0.000003
PARSE 0.000000 0.0% 2 0.000000 0.000000 0.000000
FETCH 0.000000 0.0% 2 0.000000 0.000000 0.000000
CLOSE 0.000000 0.0% 2 0.000000 0.000000 0.000000
EXEC 0.000000 0.0% 2 0.000000 0.000000 0.000000
————————— ——– —— —– ——– ——– ——–
TOTAL (7) 0.005041 100.0% 15 0.000336 0.000000 0.001663
These scripts can all be found at https://github.com/jkstill/tracefile_identifier
If you have ideas about how to improve these, please feel free to clone the repo, make some changes and issue a pull request.
If you don’t know what all of that means, might I suggest this article? Git for Beginners
The next time you have some tracing to do, why not give this method a try? Doing so will save you time and make you more productive.