Database Tools For Performance Monitoring
Database Tools For Performance Monitoring
oracledba
co.uk
Debugging / Trace
.co.uk
Autotrace
10046 trace
Debugging
Error handling
2
oracledba
Pop quiz #1
.co.uk
SQL> select rowid from MY_TABLE;
ROWID
------------------
AAAG+dAALAAACaKAAA How do two different
tables have the same
rowid ?
SQL> select rowid from A_DIFFERENT_TABLE;
ROWID
------------------
AAAG+dAALAAACaKAAA
3
oracledba
Pop quiz #2
.co.uk
• In a table, how many bytes does a ROWID consume
– A) 0
– B) 6
– C) 8
4
oracledba
Tools
.co.uk
• Autotrace
• Database trace
5
oracledba
Autotrace
.co.uk
• Used in SQL Plus
– get the most recent version
– requires a little DBA setup
6
oracledba
Autotrace example
.co.uk
SQL> set autotrace on
SQL> select inc_id, incident_ref from ims_incident
2 where off_id_officer_dealing = 1234;
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=19 Bytes=1615)
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'IMS_INCIDENT' (Cost=2 Card=19 Bytes=1615)
2 1 INDEX (RANGE SCAN) OF 'INC_OFF1_DEAL_FK_I' (NON-UNIQUE) (Cost=1 Card=19)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
2 consistent gets
0 physical reads
0 redo size
2384 bytes sent via SQL*Net to client
315 bytes received via SQL*Net from client
1 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
7
0 rows processed
oracledba
Autotrace example
.co.uk
SQL> set autotrace on statistics
SQL> select inc_id, incident_ref from ims_incident
2 where off_id_officer_dealing = 1234;
INC_ID INCIDENT_REF
---------- -----------------------------------
100001
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
2 consistent gets
0 physical reads
0 redo size
2384 bytes sent via SQL*Net to client
315 bytes received via SQL*Net from client
1 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
0 rows processed
8
oracledba
Autotrace example
.co.uk
SQL> set autotrace traceonly statistics
SQL> select inc_id, incident_ref from ims_incident
2 where off_id_officer_dealing = 1234;
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
2 consistent gets
0 physical reads
0 redo size
2384 bytes sent via SQL*Net to client
315 bytes received via SQL*Net from client
1 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
0 rows processed
9
oracledba
Autotrace example
.co.uk
SQL> set autotrace on traceonly explain
SQL> select inc_id, incident_ref from ims_incident
2 where off_id_officer_dealing = 1234;
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=19 Bytes=1615)
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'IMS_INCIDENT' (Cost=2 Card=19 Bytes=1615)
2 1 INDEX (RANGE SCAN) OF 'INC_OFF1_DEAL_FK_I' (NON-UNIQUE) (Cost=1 Card=19)
10
oracledba
What to look for
.co.uk
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=19 Bytes=1615)
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'IMS_INCIDENT' (Cost=2 Card=19 Bytes=1615)
2 1 INDEX (RANGE SCAN) OF 'INC_OFF1_DEAL_FK_I' (NON-UNIQUE) (Cost=1 Card=19)
11
oracledba
What to look for
.co.uk
Statistics
---------------------------------------------
0 recursive calls
0 db block gets SQL statements executed in order
2 consistent gets to execute your SQL statement.
0 physical reads
0 redo size
2384 bytes sent via SQL*Net to client • triggers
315 bytes received via SQL*Net from client • parsing
1 SQL*Net roundtrips to/from client
0 sorts (memory) • temporary space
0 sorts (disk) • plsql functions
0 rows processed
12
oracledba
What to look for
.co.uk
Statistics
---------------------------------------------
0 recursive calls
0 db block gets Blocks read in "CURRENT" mode.
2 consistent gets
0 physical reads
0 redo size
Current mode get is a retrieve of a
2384 bytes sent via SQL*Net to client block as it exists right now.
315 bytes received via SQL*Net from client
1 SQL*Net roundtrips to/from client
0 sorts (memory) • update
0 sorts (disk) • insert
0 rows processed • delete
• full scans
13
oracledba
What to look for
.co.uk
Statistics
---------------------------------------------
0 recursive calls
0 db block gets
Request for consistent read of a
2 consistent gets block.
0 physical reads
0 redo size
2384 bytes sent via SQL*Net to client
May require reads to the undo
315 bytes received via SQL*Net from client (rollback) information.
1 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk) Probably the key one to focus on.
0 rows processed
• query tuning
• array fetching
• application processes / timings
14
oracledba
What to look for
.co.uk
Statistics
---------------------------------------------
0 recursive calls
0 db block gets Physical reads from the datafiles
2 consistent gets into the buffer cache.
0 physical reads
0 redo size
2384 bytes sent via SQL*Net to client • Reading from datafiles
315 bytes received via SQL*Net from client • Reading from temp segments
1 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
0 rows processed
15
oracledba
What to look for
.co.uk
Statistics
---------------------------------------------
0 recursive calls
0 db block gets The total amount of redo generated
2 consistent gets in bytes during the execution of this
0 physical reads
0 redo size
statement.
2384 bytes sent via SQL*Net to client
315 bytes received via SQL*Net from client • batch up DML
1 SQL*Net roundtrips to/from client
0 sorts (memory) • use SQL not PL/SQL
0 sorts (disk) • avoid overfrequent commits
0 rows processed
16
oracledba
What to look for
.co.uk
Statistics
---------------------------------------------
0 recursive calls
0 db block gets The total number of bytes
2 consistent gets sent/received between the client
0 physical reads
0 redo size
and the server.
2384 bytes sent via SQL*Net to client
315 bytes received via SQL*Net from client Total number of SQL*Net
1 SQL*Net roundtrips to/from client
0 sorts (memory) messages sent to and received
0 sorts (disk) from the client.
0 rows processed
17
oracledba
What to look for
.co.uk
Statistics
---------------------------------------------
0 recursive calls
0 db block gets Sorts done in the user's session
2 consistent gets memory (sort area). Controlled via
0 physical reads
0 redo size
sort_area_size database
2384 bytes sent via SQL*Net to client parameter.
315 bytes received via SQL*Net from client
1 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
0 rows processed
18
oracledba
What to look for
.co.uk
Statistics
---------------------------------------------
0 recursive calls
0 db block gets Sorts that use the disk (temporary
2 consistent gets tablespace) because the sort
0 physical reads
0 redo size
exceeded the users sort area size.
2384 bytes sent via SQL*Net to client
315 bytes received via SQL*Net from client DBA concern primarily
1 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk) Upgrade to ver 9
0 rows processed
19
oracledba
What to look for
.co.uk
Statistics
---------------------------------------------
0 recursive calls
0 db block gets Rows processed by modifications
2 consistent gets or returned from a SELECT
0 physical reads
0 redo size
statement.
2384 bytes sent via SQL*Net to client
315 bytes received via SQL*Net from client • was the result expected?
1 SQL*Net roundtrips to/from client
0 sorts (memory) • updates with subqueries
0 sorts (disk)
0 rows processed
20
oracledba
Database Trace
.co.uk
• The definitive performance measurement tool
– need access to server, OR
– need client software on PC
• Enabling trace
– alter session set sql_trace = true;
– alter session set events = ‘10046 trace name context forever, level nnn’;
21
oracledba
Database Trace for Developers
.co.uk
• Trace level
– 1 standard trace
– 4 standard + bind variable values
– 8 standard trace + wait events
– 12 standard trace + bind variable values + wait events
22
oracledba
Database trace
.co.uk
SQL> alter session set sql_trace = true;
FILENAME
--------------------------------------------------------------
/apps/oracle/admin/IMSDEV/udump/imsdev_ora_6560.trc
23
oracledba
The raw data
.co.uk
/apps/oracle/admin/IMSDEV/udump/imsdev_ora_6669.trc
Oracle8i Enterprise Edition Release 8.1.7.4.0 - Production
With the Partitioning option
JServer Release 8.1.7.4.0 - Production
ORACLE_HOME = /apps/oracle/8.1.7.4
System name: SunOS
Node name: gomez
Release: 5.8
Version: Generic_108528-16
Machine: sun4u
Instance name: IMSDEV
Redo thread mounted by this instance: 1
Oracle process number: 17
Unix process pid: 6669, image: oracle@gomez (TNS V1-V3)
24
oracledba
The raw data
.co.uk
PARSING IN CURSOR #1 len=29 dep=0 uid=156 oct=3 lid=156
tim=2762254524 hv=1415343577 ad='81e07370'
select count(*) from nominal
END OF STMT
PARSE #1:c=20,e=23,p=4,cr=533,cu=0,mis=1,r=0,dep=0,og=4,tim=2762254524
EXEC #1:c=0,e=0,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=4,tim=2762254524
WAIT #1: nam='SQL*Net message to client' ela= 0 p1=1650815232 p2=1 p3=0
WAIT #1: nam='file open' ela= 0 p1=0 p2=0 p3=0
WAIT #1: nam='db file sequential read' ela= 2 p1=9 p2=5033 p3=1
WAIT #1: nam='db file scattered read' ela= 2 p1=9 p2=5034 p3=3
FETCH #1:c=0,e=4,p=4,cr=3,cu=4,mis=0,r=1,dep=0,og=4,tim=2762254528
WAIT #1: nam='SQL*Net message from client' ela= 0 p1=1650815232 p2=1 p3=0
FETCH #1:c=0,e=0,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=0,tim=2762254528
WAIT #1: nam='SQL*Net message to client' ela= 0 p1=1650815232 p2=1 p3=0
25
oracledba
The raw data
.co.uk
PARSING IN CURSOR #2 len=283 dep=1 uid=0 oct=3 lid=0
tim=2762255287 hv=955191413 ad='826270fc'
select obj#,type#,ctime,mtime,stime,status,dataobj#,flags,oid$,
spare1 from obj$ where owner#=:1 and name=:2 and namespace=:3
and(remoteowner=:4 or remoteowner is null and :4 is
null)and(linkname=:5 or linkname is null and :5 is
null)and(subname=:6 or subname is null and :6 is null)
END OF STMT
PARSE #2:c=0,e=0,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=4,tim=2762255287
EXEC #2:c=0,e=0,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=4,tim=2762255287
FETCH #2:c=0,e=0,p=0,cr=2,cu=0,mis=0,r=0,dep=1,og=4,tim=2762255287
EXEC #2:c=0,e=0,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=4,tim=2762255287
FETCH #2:c=0,e=0,p=0,cr=3,cu=0,mis=0,r=1,dep=1,og=4,tim=2762255287
=====================
PARSING IN CURSOR #3 len=46 dep=1 uid=0 oct=3 lid=0
tim=2762255287 hv=2918884618 ad='823c04ec'
select node,owner,name from syn$ where obj#=:1
END OF STMT
PARSE #3:c=0,e=0,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=4,tim=2762255287
EXEC #3:c=0,e=0,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=4,tim=2762255287
FETCH #3:c=0,e=0,p=0,cr=3,cu=0,mis=0,r=1,dep=1,og=4,tim=2762255287
EXEC #2:c=0,e=0,p=0,cr=0,cu=0,mis=0,r=0,dep=1,og=4,tim=2762255287
FETCH #2:c=0,e=0,p=0,cr=3,cu=0,mis=0,r=1,dep=1,og=4,tim=2762255287
26
oracledba
The raw data – a closer look
.co.uk
len = length of SQL dep = recursive depth uid = user executing
oct = oracle command type
lid = access user id
tim = time snapshot
ad = sql address
PARSING IN CURSOR #1 len=29 dep=0 uid=156 oct=3 lid=156
tim=2762254524 hv=1415343577 ad='81e07370' hv = hash value
PARSE #1:c=20,e=23,p=4,cr=533,cu=0,mis=1,r=0,dep=0,og=4,tim=2762254524
e = elapsed cr = consistent reads mis = library cache miss dep = recursive depth tim = time snapshot
EXEC #1:c=0,e=0,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=4,tim=2762254524
WAIT #1: nam='db file sequential read' ela= 2 p1=9 p2=5033 p3=1
FETCH #1:c=0,e=4,p=4,cr=3,cu=4,mis=0,r=1,dep=0,og=4,tim=2762254528
27
ela = elapsed p1,p2,p3 = dependent on wait
oracledba
Cleaning it up
.co.uk
tkprof imsdev_ora_6669.trc xyz.prf sort=… sys=…
select count(*)
from nominal
.co.uk
select count(*)
from nominal
29
oracledba
A closer look
.co.uk
select count(*)
from nominal
30
oracledba
A closer look
.co.uk
select count(*)
from nominal
31
oracledba
A closer look
.co.uk
select count(*)
from nominal
.co.uk
select count(*)
from nominal
33
oracledba
A closer look
.co.uk
select count(*)
from nominal
34
oracledba
A closer look
.co.uk
select count(*)
from nominal
35
oracledba
A closer look
.co.uk
select count(*)
from nominal
36
oracledba
Database Trace
.co.uk
• Options
– sort controls order of SQL output (tkprof no parameters)
– sys omit/include recursive SQL from output
– record present list of SQL’s in order executed
37
oracledba
Debugging in the database
.co.uk
• Package dbpk_debug (for database routines)
dbpk_debug.msg('Up to here in code');
38
oracledba
Example
.co.uk
PROCEDURE dbp_convert_nominal AS
BEGIN
dbpk_debug.msg('Start');
IF pc_sex_code IS NULL THEN
lc_sex_code := dbpk_common.dbf_string('SEX NOT KNOWN');
lc_sex_desc := NULL;
ELSE
lc_sex_code := pc_sex_code;
lc_sex_desc := pc_sex_desc;
END IF;
etc etc
39
oracledba
Debugging in the database
.co.uk
• Automatic tracking of
– module name
– line number
• Efficient
– supports approx. 5000 executions per second
• External tracking
– view messages in CLIENT_INFO field from any other session
– can dynamically write to file if required
• Linked in with trace
– trace facilities also controlled with same procedure
40
oracledba
Database Trace for Developers
.co.uk
• "dbpk_debug" controlled with the "init" procedure
SQL> desc TRACE_MODULES
Name Null? Type
----------------------- -------- -------------
USER_SESSION NOT NULL VARCHAR2(30)
TRACE_LEVEL NUMBER(2)
DEBUG_FILE VARCHAR2(100)
DEBUG_DIR VARCHAR2(100)
DEBUG_LEVEL NUMBER(2)
41
oracledba
Database Trace for Developers
.co.uk
SQL> begin
2 dbpk_debug.init('PD12345',
3 '/tmp','debug.dat',
4 dbpk_debug.debug_on);
5 end;
6 / Write debug calls to file for anything run by user PD12345
SQL> begin
2 dbpk_debug.init('35-1342',
3 '/tmp','debug.dat',
4 dbpk_debug.debug_off);
5 end;
6 / Stop writing debug information to file for session 35,1342
42
oracledba
Database Trace for Developers
.co.uk
SQL> set serveroutout on
SQL> exec DBPK_DEBUG.SHOW_SESSIONS
43
oracledba
Database Trace for Developers
.co.uk
SQL> select * from all_trace_files;
USER_SESSION FILENAME DT
------------------ ------------------------------ ---------
10-38245 im1dev_ora_23246.trc 16/OCT/03
26-5276 im1dev_ora_15905.trc 16/OCT/03
PD71986 im1dev_ora_23296.trc 16/OCT/03
SQL> @gettrace
Enter value for user_session: 10-38256
INFO
-------------------------------------
Processing file im1dev_ora_23246.trc
.co.uk
SQL> @getdebug
Enter value for file: /tmp/debug.dat
INFO
-------------------------------------
Processing file /tmp/debug.dat
(appears in notepad)
45
oracledba
Debugging in Forms
.co.uk
• Forms counterpart lk_debug
lk_debug.msg(m=>'Jumping to CTRL_ABC');
lk_debug.msg('10','WHEN-NEW-FORM-INSTANCE',
m=>'Jumping to CTRL_ABC');
46
oracledba
Error handling
.co.uk
• Guideline #1
– It is an EXCEPTION HANDLER
– If you can take remedial action
• include the handler
– If you get no value from handling the error
• do NOT handle it
– The calling level is the only routine that needs handlers
47
oracledba
Guideline #1
.co.uk
procedure PROC1(p_nom_id number, p_nom_date OUT date) is
begin
select nom_date_from
into p_nom_date
from nominal Problem with the query?
where nom_id = p_nom_id; Data integrity problem?
exception
when too_many_rows then
Maybe ok?
p_nom_date := null;
Is NOM_DATE_FROM nullable?
when no_data_found then
Have we just created work for caller?
p_nom_date := null;
when others then
raise; Waste of code – gains nothing
end;
48
oracledba
Error handling
.co.uk
• Guideline #2
– PL/SQL programs are single logical units of work
– Do not interrupt transaction flow
49
oracledba
Guideline #2
.co.uk
procedure PROC2(p_nom_id number) is
x number;
begin
insert into NOMINAL values (p_nom_id);
x := 1 / 0;
50
oracledba
Guideline #2
.co.uk
WHEN-BUTTON-PRESSED
begin
post;
delete from OLD_NOM where NOM_ID = :block.nom_id;
proc2;
update OTHER_TABLE set OTHER_COLUMNS = …
end;
Data corruption
51
oracledba
Guideline #2
.co.uk
procedure PROC2(p_nom_id number) is
x number;
begin
insert into NOMINAL values (p_nom_id);
insert into NOM_AUDIT values (p_nom_id);
x := 1 / 0;
update RECORD_COUNT set CNT = CNT + 1;
end;
NONE !!!
52
oracledba
Error handling
.co.uk
• Guideline #3
– Return codes are bad bad news
– Breaks rule number #2
53
oracledba
Guideline #3
.co.uk
procedure PROC2(p_nom_id number, p_ret_code OUT number) is
x number;
begin
insert into NOMINAL values (p_nom_id);
insert into NOM_AUDIT values (p_nom_id);
x := 1 / 0;
update RECORD_COUNT set CNT = CNT + 1;
p_ret_code := 0;
exception
when others then
p_ret_code := 1;
end;
54
oracledba
Guideline #3
.co.uk
WHEN-BUTTON-PRESSED
declare
ln_err_code number;
begin
PROC2(:block.nom_id, ln_err_code);
if ln_err_code = 1 then
What do I here ?
- don't know why it fell over (no error code)
- I've got a massive transaction mess (two
pending inserts that the trigger does not
know about)
- if I raise a new exception, I obfuscate the
location of where the code failed
55
oracledba
Error handling
.co.uk
• Guideline #4
– Re-cast errors only if you provide additional information
56
oracledba
Guideline #4
.co.uk
procedure PROC1(p_nom_id number, p_nom_date OUT date) is
l_error_pos number;
begin
l_error_pos := 10;
select nom_date_from We gain nothing over what Oracle
into p_nom_date would have given us anyway
from nominal
where nom_id = p_nom_id;
exception
when too_many_rows then
raise_application_error(-20000,
'TOO_MANY_ROWS error at pos '||l_error_pos||' in PROC1');
end;
57
oracledba
Guideline #4
.co.uk
procedure PROC1(p_nom_id number, p_nom_date OUT date) is
l_error_pos number;
begin
l_error_pos := 10;
select nom_date_from
into p_nom_date Now we know the offending row
from nominal
where nom_id = p_nom_id;
exception
when too_many_rows then
raise_application_error(-20000,
'TOO_MANY_ROWS for NOM_ID='||p_nom_id);
end;
58
oracledba
Error handling
.co.uk
• Guideline #5
– If you need to close cursors, your code is probably wrong
59
oracledba
Guideline #5
.co.uk
procedure PROC2(p_nom_id number) is
cursor CUR1 is …
cursor CUR2 is …
begin Waste of code and effort
…
…
exception
when others then
if CUR1%IS_OPEN then close CUR1; end if;
if CUR2%IS_OPEN then close CUR1; end if;
raise;
end;
60
oracledba
Guideline #5
.co.uk
package PKG1
cursor CUR1 is …
cursor CUR2 is …
procedure PROC2; Valid but were those cursors
end; really needed in the package?
package body PKG1
procedure PROC2(p_nom_id number) is
begin
…
…
exception
when others then
if CUR1%IS_OPEN then close CUR1; end if;
if CUR2%IS_OPEN then close CUR1; end if;
raise;
end;
end;
61