performance tuning a quick intoduction

162
©OraInternals Riyaj Shamsudeen Performance tuning A Brief Introduction By Riyaj Shamsudeen

Upload: riyaj-shamsudeen

Post on 13-Dec-2014

62 views

Category:

Technology


1 download

DESCRIPTION

Quick introduction to performance tuning

TRANSCRIPT

Page 1: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Performance tuning A Brief Introduction

By Riyaj Shamsudeen

Page 2: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen 2

Who am I?

  18 years using Oracle products/DBA   OakTable member   Oracle ACE   Certified DBA versions 7.0,7.3,8,8i,9i &10g   Specializes in RAC, performance tuning,

Internals and E-business suite   Chief DBA with OraInternals   Co-author of “Expert Oracle Practices” ‘2009   Co-author of “Pro Oracle SQL” ‘2010   Email: [email protected]   Blog : orainternals.wordpress.com   URL: www.orainternals.com

Page 3: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen 3

Disclaimer

These slides and materials represent the work and opinions of the author and do not constitute official positions of my current or past employer or any other organization. This material has been peer reviewed, but author assume no responsibility whatsoever for the test cases.

If you corrupt your databases by running my scripts, you are solely responsible for that.

This material should not should not be reproduced or used without the authors' written permission.

Page 4: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen 4

Agenda

  Tracing SQL execution

  Analyzing Trace files

  Understanding Explain plans

  Joins

  Writing optimal SQL

  Good hints and no-so good hints

  Effective indexing

  Partitioning for performance

Page 5: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

SQL Tracing

  Easiest way to trace SQL execution is with the following SQL statements:

  But, this level of tracing does not provide all the relevant needed details.

  For example, this level does not tell us whether the SQL spent time waiting for I/O or for a lock.

alter session set sql_trace=true;

exec dbms_session.set_sql_trace(sql_trace=>true);

Page 6: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Waits

  SQL statement execution is in one of two states:   On CPU executing the statement (or)   Waiting for an event such as I/O, lock etc.

  It is important to understand time spent in the events.

  For example, SQL statement waiting for I/O will wait for one of these events:

  db file sequential read   db file scattered read etc.

Page 7: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Tracing with waits

  SQL statement executions can be traced with waits printed to a trace file using:

alter session set events '10046 trace name context forever, level 8';

exec dbms_session.session_trace_enable(waits => true);

Trace level is a 4 bit integer:

level 1 : Lowest level, same as sql_trace=true

level 4 : Level 1 + captures bind variables

level 8 : Level 1 + captures wait events at SQL level

level 12: level 1 + captures bind and wait events at SQL level

Page 8: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Trace file explained …1 PARSING IN CURSOR #3 len=83 dep=0 uid=173 oct=3 lid=173 tim=12972441295985 hv=855947554 ad='fa2e5530' select transaction_id from mtl_transaction_accounts where transaction_id=1682944981 END OF STMT PARSE #3:c=10000,e=5080,p=0,cr=0,cu=0,mis=1,r=0,dep=0,og=1,tim=12972441295971 EXEC #3:c=0,e=198,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=1,tim=12972441296397 WAIT #3: nam='SQL*Net message to client' ela= 8 driver id=1650815232 #bytes=1 p3=0 obj#=38149 tim=12972441296513 WAIT #3: nam='db file sequential read' ela= 8337 file#=803 block#=213006 blocks=1 obj#=38172 tim=12972441305307 WAIT #3: nam='db file sequential read' ela= 12840 file#=234 block#=65037 blocks=1 obj#=38172 tim=12972441318487 WAIT #3: nam='gc cr grant 2-way' ela= 807 p1=803 p2=213474 p3=1 obj#=38172 tim=12972441320096 WAIT #3: nam='db file sequential read' ela= 4095 file#=803 block#=213474 blocks=1 obj#=38172 tim=12972441324278

  Highlighted lines indicates that SQL statement is being parsed.

Page 9: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Trace file explained …2 PARSING IN CURSOR #3 len=83 dep=0 uid=173 oct=3 lid=173 tim=12972441295985 hv=855947554 ad='fa2e5530' select transaction_id from mtl_transaction_accounts where transaction_id=1682944981 END OF STMT PARSE #3:c=10000,e=5080,p=0,cr=0,cu=0,mis=1,r=0,dep=0,og=1,tim=12972441295971 EXEC #3:c=0,e=198,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=1,tim=12972441296397 WAIT #3: nam='SQL*Net message to client' ela= 8 driver id=1650815232 #bytes=1 p3=0 obj#=38149 tim=12972441296513

WAIT #3: nam='db file sequential read' ela= 8337 file#=803 block#=213006 blocks=1 obj#=38172 tim=12972441305307 WAIT #3: nam='db file sequential read' ela= 12840 file#=234 block#=65037 blocks=1 obj#=38172 tim=12972441318487 WAIT #3: nam='db file sequential read' ela= 4095 file#=803 block#=213474 blocks=1 obj#=38172 tim=12972441324278

Event waited for Elapsed time (micro seconds)

  Later, we will show how tkprof utility can aggregate these events and print formatted output.

Page 10: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Tracing with binds

  SQL statement executions can be traced with bind variable values also:

alter session set events '10046 trace name context forever, level 12';

exec dbms_session.session_trace_enable(waits => true,binds=>true);

  Generally, you wouldn’t need to trace with binds unless you want to find the bind variables also.

  If there are many executions of SQL statements (millions) in a process then turning on trace with binds can reduce performance.

Page 11: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen 11

Agenda

  Tracing SQL execution

  Analyzing Trace files

  Understanding Explain plans

  Joins

  Writing optimal SQL

  Good hints and no-so good hints

  Effective indexing

  Partitioning for performance

Page 12: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

tkprof

  Trace files generated from SQLTrace is not exactly readable.

tkprof trace_file outfile sort=option explain=user/pwd sys=no

  tkprof utility provides formatted output from the trace files.

tkprof devl_ora_123.trc /tmp/devl_ora_tkp.out sort=exeela,fchela Example:

  tkprof comes with help options. Just type tkprof and enter to see help.

Page 13: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Sort option

  Sort option in tkprof is useful. For example, sort=exeela will sort the SQL statements by Elapsed time at execution step.

  Top SQL statements by elapsed time will show up at the top of the tkprof output file.

  Generally, I use sort=exeela, fchela to sort the SQL statements.

  Other common options are: execpu, fchcpu, prsela etc.

Page 14: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

explain option

  But, SQL Trace also prints execution plan in the trace file.

  Explain=userid/password@dev can be supplied to print execution plan of that SQL statement.

  Explain option logs on to the database and executes explain plan for every SQL in the trace file.

  It is a good practice not to use explain option and use the execution plan in the trace file itself.

  If you are using SQLPlus or TOAD to execute SQL, make sure to close the connection or exit so that trace files will be complete.

Page 15: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

More on tracing…

  Trace at program level

  From concurrent program screen, check enable trace box. (level 8)

  Trace can be enabled at user level: Beware every click is tracing.

  Using alter session command:

alter session set events ' 10046 trace name context forever, level 12';

  Using dbms_system in another session (mostly used by DBAs):

Exec dbms_System.set_ev( sid, serial#, 10046, 12, '');

Exec dbms_System.set_Sql_Trace_in_session(sid, serial#, true);

Page 16: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen 16

Agenda

  Tracing SQL execution

  Analyzing Trace files

  Understanding Explain plans

  Joins

  Writing optimal SQL

  Good hints and no-so good hints

  Effective indexing

  Partitioning for performance

Page 17: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Execution plan

  Understanding execution plan is an important step in resolving performance issues.

explain plan for select t1.id, t1.vc10 , t2.id From t1,t2 where t1.id=t2.id and t2.id <1000;

Select * from table(dbms_xplan.display);

  There are many ways to generate execution plans and easiest way is:

Page 18: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Simple SELECT Select * from table(dbms_xplan.display);

-------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |

-------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 975 | 22425 | 512 (1)| 00:00:02 | |* 1 | HASH JOIN | | 975 | 22425 | 512 (1)| 00:00:02 |

|* 2 | INDEX RANGE SCAN | T2_N1 | 976 | 5856 | 5 (0)| 00:00:01 | | 3 | TABLE ACCESS BY INDEX ROWID| T1 | 1000 | 17000 | 506 (1)| 00:00:02 |

|* 4 | INDEX RANGE SCAN | T1_N1 | 1000 | | 5 (0)| 00:00:01 | --------------------------------------------------------------------------------------

Predicate Information (identified by operation id): ---------------------------------------------------

1 - access("T1"."ID"="T2"."ID") 2 - access("T2"."ID"<1000) 4 - access("T1"."ID"<1000)

(i) Index T2_N1 was scanned with access predicate t2.id <1000 [ Step 2] (ii) Index T1_N1 was scanned with access predicate t1.id <1000 [ Step 4] (iii) Table T1 is accessed to retrieve non-indexed column using rowids returned from step 2. [ Step 3]. (iv) Rows from step 2 and 3 are joined to create the final result set.[Step 1]

Execution sequence

Page 19: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Predicates

Set lines 120 pages 0

Select * from table(dbms_xplan.display);

------------------------------------------------------------------------------------------------------ | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| ------------------------------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 1 | 214 | 9 (23)| | 1 | SORT ORDER BY | | 1 | 214 | 9 (23)| |* 2 | TABLE ACCESS BY INDEX ROWID | RCV_STAGING_LINES | 1 | 155 | 4 (25)| | 3 | NESTED LOOPS | | 1 | 214 | 8 (13)| |* 4 | TABLE ACCESS BY INDEX ROWID| RCV_STAGING_HEADERS | 1 | 59 | 5 (20)| |* 5 | INDEX RANGE SCAN | RCV_STAGING_HEADERS_N5 | 20 | | 4 (25)| |* 6 | INDEX RANGE SCAN | RCV_STAGING_LINES_N3 | 1 | | 3 (34)| ------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id): --------------------------------------------------- 2 - filter("RSL"."RMA_NUMBER"=:Z AND "RSL"."RMA_LINE_NUMBER"=TO_NUMBER(:Z))

4 - filter("RSH"."RMA_NUMBER"=:Z AND "RSH"."REQUEST_ID"=TO_NUMBER(:Z)) 5 - access("RSH"."STATUS"='PROCESSED')

6 - access("RSH"."SOURCE_HEADER_ID"="RSL"."SOURCE_HEADER_ID")

  Predicates are very useful to debug performance issues.

  Cardinality estimate is a good guess. 20 rows are returned in step 5 , but just one row returned from the table (step 4).

Page 20: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Index selection.

Select * from table(dbms_xplan.display);

------------------------------------------------------------------------------------------------------ | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| ------------------------------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 1 | 206 | 9 (23)| | 1 | SORT ORDER BY | | 1 | 206 | 9 (23)| |* 2 | TABLE ACCESS BY INDEX ROWID | RCV_STAGING_LINES | 1 | 155 | 4 (25)| | 3 | NESTED LOOPS | | 1 | 206 | 8 (13)| | 4 | TABLE ACCESS BY INDEX ROWID| RCV_STAGING_HEADERS | 1 | 51 | 5 (20)| |* 5 | INDEX RANGE SCAN | RCV_STAGING_HEADERS_N5 | 1 | | 4 (25)| |* 6 | INDEX RANGE SCAN | RCV_STAGING_LINES_N3 | 1 | | 3 (34)| ------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id): --------------------------------------------------- 2 - filter("RSL"."RMA_NUMBER"=:Z AND "RSL"."RMA_LINE_NUMBER"=TO_NUMBER(:Z)) 5 - access("RSH"."STATUS"='PROCESSED' AND "RSH"."RMA_NUMBER"=:Z AND "RSH"."REQUEST_ID"=TO_NUMBER(:Z)) 6 - access("RSH"."SOURCE_HEADER_ID"="RSL"."SOURCE_HEADER_ID")

create index apps.rcv_staging_headers_n5 on apps.RCV_STAGING_HEADERS (status, rma_number, request_id ) compress 1 compute statistics;

  Notice that all predicates are applied in step 5 using index.

  Cardinality estimates improved from 20 to 1.

Page 21: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Package: dbms_xplan

  Dbms_xplan is a package available from 9i. It has many rich features.

  dbms_xplan package has following features:   Print execution plan for recently explained SQL statement:

dbms_xplan.display;   Print execution plan for a SQL statement executed by passing sql_id or hash_value:

dbms_xplan.display_cursor ('&sqlid', '', '');   Print execution plan for a SQL statement captured by AWR report:

dbms_xplan.display_awr ('&sqlid', '', '');

Page 22: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Autotrace deprecated

  Many of us are used to autotrace in SQL*Plus. Use dbms_xplan instead ( from 10g onwards):

select t1.id, t1.vc10 , t2.id From t1,t2 where t1.id=t2.id and t2.id <1000;

Select * from table (dbms_xplan.display_cursor);

SQL_ID cd6h52abfgfg3, child number 0 ------------------------------------- select t1.id, t1.vc10 , t2.id From t1,t2 where t1.id=t2.id and t2.id <1000

Plan hash value: 3286489634 -------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 512 (100)| | |* 1 | HASH JOIN | | 975 | 22425 | 512 (1)| 00:00:02 | ...

Page 23: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

But..   dbms_xplan accesses v$sql, v$sql_plan_statistics_all and v$sql_plan internally. So, you need to have select access on these fixed views to execute dbms_xplan.

  Executing dbms_xplan.display_cursor without specifying any sql_id retrieves the execution plan for the query executed last.

  From 10g onwards, serveroutput is on by default. You need to disable serveroutput to see the execution plan correctly:

Select * from table (dbms_xplan.display_cursor);

SQL_ID 9babjv8yq8ru3, child number 1 BEGIN DBMS_OUTPUT.GET_LINES(:LINES, :NUMLINES); END;

Page 24: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Display_cursor   Suppose, you want to find the execution plan of a SQL statement executed recently, then display_cursor is handy.

  Find sql_id or hash_value of the SQL statement from v$sql and pass that to display_cursor:

select * from table(dbms_xplan.display_cursor ( '&sql_id','',''));

SQL_ID fqmkvmdyb043r, child number 0

------------------------------------- select t1.id, t1.vc10 , t2.id From t1,t2 where t1.id=t2.id and t2.id <1000

Plan hash value: 3286489634 --------------------------------------------------------------------------------------

| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 512 (100)| |

|* 1 | HASH JOIN | | 975 | 22425 | 512 (1)| 00:00:02 | |* 2 | INDEX RANGE SCAN | T2_N1 | 976 | 5856 | 5 (0)| 00:00:01 |

| 3 | TABLE ACCESS BY INDEX ROWID| T1 | 1000 | 17000 | 506 (1)| 00:00:02 | ...

Page 25: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Time spent?

Rows Row Source Operation ------- --------------------------------------------------- 49996 WINDOW SORT 644538 NESTED LOOPS 704891 NESTED LOOPS 704891 NESTED LOOPS 704891 NESTED LOOPS OUTER 704891 NESTED LOOPS 704891 NESTED LOOPS 1 TABLE ACCESS BY INDEX ROWID HR_ALL_ORGANIZATION_UNITS 1 INDEX UNIQUE SCAN HR_ORGANIZATION_UNITS_PK 704891 TABLE ACCESS BY INDEX ROWID WSH_DELIVERY_DETAILS 704891 INDEX RANGE SCAN WSH_DELIVERY_DETAILS_N8 704891 TABLE ACCESS BY INDEX ROWID MTL_SYSTEM_ITEMS_B 704891 INDEX RANGE SCAN MTL_SYSTEM_ITEMS_B_U1 644783 VIEW PUSHED PREDICATE 644783 NESTED LOOPS 704891 TABLE ACCESS BY INDEX ROWID FND_FLEX_VALUE_SETS 704891 INDEX UNIQUE SCAN FND_FLEX_VALUE_SETS_U2 644783 TABLE ACCESS BY INDEX ROWID FND_FLEX_VALUES 644783 INDEX RANGE SCAN FND_FLEX_VALUES_N1 704891 TABLE ACCESS BY INDEX ROWID OE_ORDER_LINES_ALL 704891 INDEX UNIQUE SCAN OE_ORDER_LINES_U1 704891 TABLE ACCESS BY INDEX ROWID WSH_DELIVERY_ASSIGNMENTS 704891 INDEX RANGE SCAN WSH_DELIVERY_ASSIGNMENTS_N3 644538 TABLE ACCESS BY INDEX ROWID WSH_NEW_DELIVERIES 704891 INDEX UNIQUE SCAN WSH_NEW_DELIVERIES_U1

Total of 330 seconds spent on this SQL. Can you identify the step that incurred most time?

Page 26: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Statistics_level   Statistics_level parameter provides valuable row source execution statistics.

  This parameter can be changed at session or instance level: alter session set statistics_level=all;

  Default value of this parameter is TYPICAL which do not collect row source execution statistics.

  Don’t set this initialization parameter to ALL in Production. This parameter incurs additional overhead. But, still, tracing at session level is acceptable.

Page 27: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Statistics_level=ALL

Rows Row Source Operation ------- --------------------------------------------------- 49996 WINDOW SORT (cr=11333940 r=66442 w=46044 time=327835520 us) 644538 NESTED LOOPS (cr=11333940 r=41631 w=0 time=302305095 us) 704891 NESTED LOOPS (cr=9219265 r=34793 w=0 time=263965257 us) 704891 NESTED LOOPS (cr=7093885 r=27898 w=0 time=217008667 us) 704891 NESTED LOOPS OUTER (cr=4979210 r=460 w=0 time=80832751 us) 704891 NESTED LOOPS (cr=2219750 r=460 w=0 time=46780270 us) 704891 NESTED LOOPS (cr=51405 r=0 w=0 time=15862881 us) 1 TABLE ACCESS BY INDEX ROWID HR_ALL_ORGANIZATION_UNITS (cr=3 r=0 w=0 time=79 us) 1 INDEX UNIQUE SCAN HR_ORGANIZATION_UNITS_PK (cr=2 r=0 w=0 time=42 us)(object id 43498) 704891 TABLE ACCESS BY INDEX ROWID WSH_DELIVERY_DETAILS (cr=51402 r=0 w=0 time=15456581 us) 704891 INDEX RANGE SCAN WSH_DELIVERY_DETAILS_N8 (cr=2692 r=0 w=0 time=1677304 us)(object id 5124003) 704891 TABLE ACCESS BY INDEX ROWID MTL_SYSTEM_ITEMS_B (cr=2168345 r=460 w=0 time=26259264 us) 704891 INDEX RANGE SCAN MTL_SYSTEM_ITEMS_B_U1 (cr=1409795 r=213 w=0 time=15069327 us)(object id 38017) 644783 VIEW PUSHED PREDICATE (cr=2759460 r=0 w=0 time=30859890 us) 644783 NESTED LOOPS (cr=2759460 r=0 w=0 time=29181767 us) 704891 TABLE ACCESS BY INDEX ROWID FND_FLEX_VALUE_SETS (cr=1409784 r=0 w=0 time=11031089 us) 704891 INDEX UNIQUE SCAN FND_FLEX_VALUE_SETS_U2 (cr=704893 r=0 w=0 time=6257393 us)(object id 33768) 644783 TABLE ACCESS BY INDEX ROWID FND_FLEX_VALUES (cr=1349676 r=0 w=0 time=13839752 us) 644783 INDEX RANGE SCAN CCW_FND_FLEX_VALUES_N1 (cr=704893 r=0 w=0 time=8683861 us)(object id 5153800) 704891 TABLE ACCESS BY INDEX ROWID OE_ORDER_LINES_ALL (cr=2114675 r=27438 w=0 time=133292520 us) 704891 INDEX UNIQUE SCAN OE_ORDER_LINES_U1 (cr=1409784 r=2025 w=0 time=14863664 us)(object id 42102) 704891 TABLE ACCESS BY INDEX ROWID WSH_DELIVERY_ASSIGNMENTS (cr=2125380 r=6895 w=0 time=42893004 us) 704891 INDEX RANGE SCAN WSH_DELIVERY_ASSIGNMENTS_N3 (cr=1413108 r=2580 w=0 time=24181814 us)(object id 46295) 644538 TABLE ACCESS BY INDEX ROWID WSH_NEW_DELIVERIES (cr=2114675 r=6838 w=0 time=35307346 us) 704891 INDEX UNIQUE SCAN WSH_NEW_DELIVERIES_U1 (cr=1409784 r=362 w=0 time=7381948 us)(object id 46306)

Setting this parameter to ALL provides valuable Row source statistics.

Page 28: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Statistics_level=ALL

Rows Row Source Operation ------- --------------------------------------------------- 49996 WINDOW SORT (cr=11333940 r=66442 w=46044 time=327835520 us) 644538 NESTED LOOPS (cr=11333940 r=41631 w=0 time=302305095 us) 704891 NESTED LOOPS (cr=9219265 r=34793 w=0 time=263965257 us) 704891 NESTED LOOPS (cr=7093885 r=27898 w=0 time=217008667 us) 704891 NESTED LOOPS OUTER (cr=4979210 r=460 w=0 time=80832751 us) 704891 NESTED LOOPS (cr=2219750 r=460 w=0 time=46780270 us) 704891 NESTED LOOPS (cr=51405 r=0 w=0 time=15862881 us) 1 TABLE ACCESS BY INDEX ROWID HR_ALL_ORGANIZATION_UNITS (cr=3 r=0 w=0 time=79 us) 1 INDEX UNIQUE SCAN HR_ORGANIZATION_UNITS_PK (cr=2 r=0 w=0 time=42 us)(object id 43498) 704891 TABLE ACCESS BY INDEX ROWID WSH_DELIVERY_DETAILS (cr=51402 r=0 w=0 time=15456581 us) 704891 INDEX RANGE SCAN WSH_DELIVERY_DETAILS_N8 (cr=2692 r=0 w=0 time=1677304 us)(object id 5124003) 704891 TABLE ACCESS BY INDEX ROWID MTL_SYSTEM_ITEMS_B (cr=2168345 r=460 w=0 time=26259264 us) 704891 INDEX RANGE SCAN MTL_SYSTEM_ITEMS_B_U1 (cr=1409795 r=213 w=0 time=15069327 us)(object id 38017) 644783 VIEW PUSHED PREDICATE (cr=2759460 r=0 w=0 time=30859890 us) 644783 NESTED LOOPS (cr=2759460 r=0 w=0 time=29181767 us) 704891 TABLE ACCESS BY INDEX ROWID FND_FLEX_VALUE_SETS (cr=1409784 r=0 w=0 time=11031089 us) 704891 INDEX UNIQUE SCAN FND_FLEX_VALUE_SETS_U2 (cr=704893 r=0 w=0 time=6257393 us)(object id 33768) 644783 TABLE ACCESS BY INDEX ROWID FND_FLEX_VALUES (cr=1349676 r=0 w=0 time=13839752 us) 644783 INDEX RANGE SCAN CCW_FND_FLEX_VALUES_N1 (cr=704893 r=0 w=0 time=8683861 us)(object id 5153800) 704891 TABLE ACCESS BY INDEX ROWID OE_ORDER_LINES_ALL (cr=2114675 r=27438 w=0 time=133292520 us) 704891 INDEX UNIQUE SCAN OE_ORDER_LINES_U1 (cr=1409784 r=2025 w=0 time=14863664 us)(object id 42102) 704891 TABLE ACCESS BY INDEX ROWID WSH_DELIVERY_ASSIGNMENTS (cr=2125380 r=6895 w=0 time=42893004 us) 704891 INDEX RANGE SCAN WSH_DELIVERY_ASSIGNMENTS_N3 (cr=1413108 r=2580 w=0 time=24181814 us)(object id 46295) 644538 TABLE ACCESS BY INDEX ROWID WSH_NEW_DELIVERIES (cr=2114675 r=6838 w=0 time=35307346 us) 704891 INDEX UNIQUE SCAN WSH_NEW_DELIVERIES_U1 (cr=1409784 r=362 w=0 time=7381948 us)(object id 46306)

Timeline

Page 29: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Statistics_level

  So, statistics_level parameter can be used to understand the step consuming much time.

  To improve performance we try to reduce the step consuming time.

  But, of course, functional knowledge of the SQL statement will come handy.

Page 30: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

All trace commands   If you are tracing from your session, you might want to set these parameters too:

  Sets maximum file size to unlimited.

Alter session set max_dump_file_size=unlimited;

  This sets trace files to have file names easily distinguishable!

alter session set tracefile_identifier='riyaj';

  Print row source timing information.

alter session set Statistics_level=all;

exec dbms_session.session_trace_enable(waits => true);

Page 31: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen 31

Agenda

  Tracing SQL execution

  Analyzing Trace files

  Understanding Explain plans

  Access, Joins, filters and unnest

  Writing optimal SQL

  Good hints and no-so good hints

  Effective indexing

  Partitioning for performance

Page 32: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Index Unique Scan

explain plan for select ename from demo1.emp where empno=:b1 --------------------------------------------------------------------------------------

| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------------

| 0 | SELECT STATEMENT | | 1 | 10 | 1 (0)| 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 10 | 1 (0)| 00:00:01 | |* 2 | INDEX UNIQUE SCAN | EMP_PK | 1 | | 0 (0)| 00:00:01 |

--------------------------------------------------------------------------------------

Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("EMPNO"=TO_NUMBER(:B1))

  One row returned from the index key.

  Usually, quite efficient since just one row returned from the index. Index must be unique index for this access path.

Page 33: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen 33

Index unique scan

Index returns one Rowid.

Table Root block

Branch block

Leaf blocks

empno:b1

Data file

120

121

122

123

124

125

126

Page 34: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Index Range Scan

explain plan for select ename from demo1.emp where empno between :b1 and :b2;

---------------------------------------------------------------------------------------

| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 10 | 2 (0)| 00:00:01 |

|* 1 | FILTER | | | | | | | 2 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 10 | 2 (0)| 00:00:01 |

|* 3 | INDEX RANGE SCAN | EMP_PK | 2 | | 1 (0)| 00:00:01 | --------------------------------------------------------------------------------------- Predicate Information (identified by operation id):

---------------------------------------------------

1 - filter(TO_NUMBER(:B1)<=TO_NUMBER(:B2)) 3 - access("EMPNO">=TO_NUMBER(:B1) AND "EMPNO"<=TO_NUMBER(:B2))

  Multiple rowids retrieved from the index. For every rowid returned, table block is accessed to retrieve other selected columns.

Page 35: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen 35

Index range scan

Index may return one or more rowids.

Table Root block

Branch block

Leaf blocks

Empno between :b1 and :b2

Data file

120

121

122

123

124

125

126

Page 36: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Index Skip Scan

Index Column Type ------ ----------- -------------

EMP_C2 LOCATION_ID 1 NUMBER(22) ENAME 2 VARCHAR2(10)

select * from emp a where ename='JACK';

-------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |

-------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 43 | 3 (0)| 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 43 | 3 (0)| 00:00:01 |

|* 2 | INDEX SKIP SCAN | EMP_C2 | 1 | | 2 (0)| 00:00:01 | --------------------------------------------------------------------------------------

Predicate Information (identified by operation id): ---------------------------------------------------

2 - access("ENAME"='JACK') filter("ENAME"='JACK')

  Index is searched with non-leading columns of the index. Generally, performs better if the number of distinct values for the leading columns are very few.

Page 37: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen 37

Index skip scan

Leading column Is location_id

Table Root block

Branch block

Leaf blocks

Empname=‘JACK’

Data file

120

121

122

123

124

125

126

Page 38: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Joins

  Type of join operators   Equi-join   Outer join   Merge Join   Anti-join

  Type of join techniques in Oracle   Nested Loops join   Hash Join   Merge Join

Page 39: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Nested loops join

644783 NESTED LOOPS 704891 TABLE ACCESS BY INDEX ROWID EMP 704891 INDEX UNIQUE SCAN EMP_C1 644783 TABLE ACCESS BY INDEX ROWID EMP_HISTORY 644783 INDEX RANGE SCAN EMP_HIST_N1

  For every row from outer row source, inner row source is probed for a matching row satisfying join key condition.

  Generally, performs better if number of rows from the inner row source is much lower.

  Typically, OLTP uses this type of join.

Page 40: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Nested loops join

EMP_C1 Index searched for the predicate Emp_name like ‘S%’

EMP

Rowids fetched from the index and table accessed.

Loop

EMP_HIST_N1

EMP_HISTORY

Rowids fetched from the index and table accessed using rowid.

Index searched for the join key Employee_id = employee_id

Row piece fetched from the table

Row piece fetched from the table

1

2

3

4

5

6

7

644783 NESTED LOOPS 704891 TABLE ACCESS BY INDEX ROWID EMP 704891 INDEX UNIQUE SCAN EMP_C1

644783 TABLE ACCESS BY INDEX ROWID EMP_HISTORY 644783 INDEX RANGE SCAN EMP_HIST_N1

Page 41: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Hash join

explain plan for select t1.color, t2.color, t1.shape from t1, t2 where t1.color_id = t2.color_id and t1.color=:b1 and t2.color=:b1 / Explained.

SQL> select * from table (dbms_xplan.display);

Plan hash value: 2339531555

------------------------------------------------------------------d----------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ----------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 333 | 9990 | 8 (13)| 00:00:01 | |* 1 | HASH JOIN | | 333 | 9990 | 8 (13)| 00:00:01 | | 2 | TABLE ACCESS BY INDEX ROWID| T1 | 333 | 6660 | 3 (0)| 00:00:01 | |* 3 | INDEX RANGE SCAN | T1_COLOR | 333 | | 1 (0)| 00:00:01 | | 4 | TABLE ACCESS BY INDEX ROWID| T2 | 500 | 5000 | 4 (0)| 00:00:01 | |* 5 | INDEX RANGE SCAN | T2_COLOR | 500 | | 2 (0)| 00:00:01 | ----------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - access("T1"."COLOR_ID"="T2"."COLOR_ID") 3 - access("T1"."COLOR"=:B1) 5 - access("T2"."COLOR"=:B1)

19 rows selected.

  Rows from two row sources fetched and then joined.

Page 42: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Hash join ------------------------------------------------- | Id | Operation | Name | ------------------------------------------------- | 0 | SELECT STATEMENT | | |* 1 | HASH JOIN | | | 2 | TABLE ACCESS BY INDEX ROWID| T1 | |* 3 | INDEX RANGE SCAN | T1_COLOR | | 4 | TABLE ACCESS BY INDEX ROWID| T2 | |* 5 | INDEX RANGE SCAN | T2_COLOR | -------------------------------------------------

T1_COLOR Index searched for the predicate T1.color=:b1

T1

Rowids fetched from the index and table accessed.

Row piece fetched from the table

1

2

3

T2_COLOR Index searched for the predicate T2.color =:b1

T2

Rowids fetched from the index and table accessed.

Row piece fetched from the table

4

5

6

Hash Join

Rows returned

7

Page 43: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Merge join

select /*+ use_merge (t1, t2) */ t1.color, t2.color, t1.shape from t1, t2 where t1.color_id = t2.color_id and t1.color='black' and t2.color='black' ------------------------------------------------------------------------------------------ | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | | | 10 (100)| | | 1 | MERGE JOIN | | 500 | 15000 | 10 (20)| 00:00:01 | | 2 | SORT JOIN | | 500 | 5000 | 5 (20)| 00:00:01 | | 3 | TABLE ACCESS BY INDEX ROWID| T2 | 500 | 5000 | 4 (0)| 00:00:01 | |* 4 | INDEX RANGE SCAN | T2_COLOR | 500 | | 2 (0)| 00:00:01 | |* 5 | SORT JOIN | | 900 | 18000 | 5 (20)| 00:00:01 | |* 6 | TABLE ACCESS FULL | T1 | 900 | 18000 | 4 (0)| 00:00:01 | ------------------------------------------------------------------------------------------

Predicate Information (identified by operation id): ---------------------------------------------------

4 - access("T2"."COLOR"='black') 5 - access("T1"."COLOR_ID"="T2"."COLOR_ID") filter("T1"."COLOR_ID"="T2"."COLOR_ID") 6 - filter("T1"."COLOR"='black')

  Rows from two row sources are sorted and joined using merge join technique.

Page 44: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Merge join ------------------------------------------------- | Id | Operation | Name | ------------------------------------------------- | 0 | SELECT STATEMENT | | |* 1 | HASH JOIN | | | 2 | TABLE ACCESS BY INDEX ROWID| T1 | |* 3 | INDEX RANGE SCAN | T1_COLOR | | 4 | TABLE ACCESS BY INDEX ROWID| T2 | |* 5 | INDEX RANGE SCAN | T2_COLOR | -------------------------------------------------

T2_COLOR Index searched for the predicate T1.color=:b1

T2

Rowids fetched from the index and table accessed.

Row piece fetched from the table

1

2

3

T1 Index searched for the predicate T2.color =:b1

SORT

Rows filtered

Rows are sorted

4

5

6

Merge Join

Rows returned

7

SORT

Rows filtered 5

6

Page 45: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

select * from emp e where e.deptno in (select deptno from dept where loc like 'A%') and e.ename in (select ename from bonus where sal >100000);

-------------------------------------------------------- | Id | Operation | Name | Rows | -------------------------------------------------------- | 0 | SELECT STATEMENT | | | |* 1 | FILTER | | | | 2 | TABLE ACCESS FULL | EMP | 14 | |* 3 | TABLE ACCESS BY INDEX ROWID| DEPT | 1 | |* 4 | INDEX RANGE SCAN | DEPT_PK | 1 | |* 5 | TABLE ACCESS FULL | BONUS | 1 | -------------------------------------------------------- Predicate Information (identified by operation id): ---------------------------------------------------

1 - filter(( IS NOT NULL AND IS NOT NULL)) 3 - filter("LOC" LIKE 'A%') 4 - access("DEPTNO"=:B1) 5 - filter(("ENAME"=:B1 AND "SAL">100000))

FILTER step EMP

DEPT

DEPT_PK

BONUS

For every row from EMP Filter checking with Dept_pk and

then DEPT table Filter checking with BONUS table

Page 46: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen 46

Subquery unnesting

Subquery unnesting is an optimization step in which sub queries are unnested to a join.

select * from emp e where

e.deptno in (select deptno from dept where loc like 'A%') and

e.ename in (select ename from bonus where sal >100000);

----------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |

----------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 7 (100)| | |* 1 | HASH JOIN SEMI | | 5 | 675 | 7 (15)| 00:00:01 |

|* 2 | HASH JOIN SEMI | | 5 | 575 | 5 (20)| 00:00:01 | | 3 | TABLE ACCESS FULL| EMP | 14 | 1316 | 2 (0)| 00:00:01 |

|* 4 | TABLE ACCESS FULL| DEPT | 1 | 21 | 2 (0)| 00:00:01 | |* 5 | TABLE ACCESS FULL | BONUS | 1 | 20 | 2 (0)| 00:00:01 | -----------------------------------------------------------------------------

Page 47: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Summary

  There are different types of join techniques.

  There are no one join techniques superior to other join technique. If that is the case, Oracle product would not choose inferior techniques.

  Choosing a join method suitable for the SQL statement is also a necessary step of tuning that statement.

Page 48: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen 48

Common problem areas in an execution plan.

Page 49: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Identify plan issues

  There are couple of clues in the execution plan that can give you a hint about the root cause.

  Following few slides details these clues. But, this is not a complete list.

  It is probably a better idea to test your theory   by rewriting the query   by adjusting the execution plan with hints   by testing with a different database or different set of data.

Page 50: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Cardinality feedback

  Cardinality is the measure of optimizer estimate for the number of rows returned in a step.

Select * from table (dbms_xplan.display); --------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| --------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 214 | 9 (23)| | 1 | SORT ORDER BY | | 1 | 214 | 9 (23)| |* 2 | TABLE ACCESS BY INDEX ROWID | RCV_STAGING_LINES | 1 | 155 | 4 (25)| | 3 | NESTED LOOPS | | 1 | 214 | 8 (13)| |* 4 | TABLE ACCESS BY INDEX ROWID| RCV_STAGING_HEADERS | 1 | 59 | 5 (20)| |* 5 | INDEX RANGE SCAN | RCV_STAGING_HEADERS_N5 | 20 | | 4 (25)| |* 6 | INDEX RANGE SCAN | RCV_STAGING_LINES_N3 | 1 | | 3 (34)| ----------------------------------------------------------------------------------------------

  In this case, optimizer estimates that step 6, accessing through index returned 20 rows.

  Step 4 reduced that just one row. So, you need to investigate to see why there is many rows estimated to be returned in that step.

Page 51: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Cardinality feedback (2)

  Match the run time statistics and the execution plan. Identify the difference in the estimate and actual statistics.

--------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| --------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 214 | 9 (23)| | 1 | SORT ORDER BY | | 1 | 214 | 9 (23)| |* 2 | TABLE ACCESS BY INDEX ROWID | RCV_STAGING_LINES | 1 | 155 | 4 (25)| | 3 | NESTED LOOPS | | 1 | 214 | 8 (13)| |* 4 | TABLE ACCESS BY INDEX ROWID| RCV_STAGING_HEADERS | 1 | 59 | 5 (20)| |* 5 | INDEX RANGE SCAN | RCV_STAGING_HEADERS_N5 | 20 | | 4 (25)| |* 6 | INDEX RANGE SCAN | RCV_STAGING_LINES_N3 | 1 | | 3 (34)| ----------------------------------------------------------------------------------------------

Rows Row Source Operation ------- --------------------------------------------------- 0 TABLE ACCESS BY INDEX ROWID RCV_STAGING_LINES 0 NESTED LOOPS 0 NESTED LOOPS 14 TABLE ACCESS BY INDEX ROWID RCV_STAGING_HEADERS 2608158 INDEX RANGE SCAN RCV_STAGING_HEADERS_N5 14 INDEX RANGE SCAN RCV_STAGING_LINES_N3

  In this example below, optimizer estimates are off, especially for step 5..

Page 52: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Leading table   In most cases, if the optimizer chooses correct starting table, then the performance of that SQL statement will be tolerable.

Rows     Row Source Operation -------  ---------------------------------------------------

   1000  SORT GROUP BY (cr=33914438 r=0 w=0 time=386540124 us)    1000   NESTED LOOPS  (cr=33914438 r=0 w=0 time=386162642 us)

16540500    TABLE ACCESS BY INDEX ROWID OBJ#(38049)(cr=831438 r=0 w=0 time=146900634us ) 16540500     INDEX RANGE SCAN OBJ#(149297) (cr=154584 r=0 w=0 time=25026467 us)(obj 149297)    1000    INDEX RANGE SCAN OBJ#(5743997) (cr=33083000 r=0 w=0 time=202120686 us)(obj 5743997)

  Try to understand why the optimizer did not chose correct starting table.

  In this example, leading table is chosen unwisely.

Page 53: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Leading table (2)

  With a leading hint, SQL was tuned to start from object id 5743997, which was a temporary table.

Rows     Row Source Operation -------  ---------------------------------------------------

      0  SORT GROUP BY (cr=3067 r=0 w=0 time=38663 us)       0   TABLE ACCESS BY INDEX ROWID OBJ#(38049) (cr=3067 r=0 w=0 time=26516 us)

   1000    NESTED LOOPS  (cr=3067 r=0 w=0 time=24509 us)       0     INDEX RANGE SCAN OBJ#(5743997) (cr=3067 r=0 w=0 time=23379 us)(object id 5743997)       0     INDEX RANGE SCAN OBJ#(1606420) (object id 1606420)

  Performance of that SQL is much better with temporary table chosen as a leading table.

Page 54: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Cartesian merge join   Cartesian merge join (CMJ) is a special case of merge join. It is a merge join with no join predicates between two row sources.

select * from emp e where e.deptno in (select deptno from dept where loc like 'A%') and e.ename in (select ename from bonus where sal >100000);

------------------------------------------------------- | Id | Operation | Name | E-Rows |

------------------------------------------------------- |* 1 | TABLE ACCESS BY INDEX ROWID| EMP | 5 |

| 2 | NESTED LOOPS | | 1 | | 3 | MERGE JOIN CARTESIAN | | 1 | | 4 | SORT UNIQUE | | 1 |

|* 5 | TABLE ACCESS FULL | BONUS | 1 | | 6 | BUFFER SORT | | 1 |

| 7 | SORT UNIQUE | | 1 | |* 8 | TABLE ACCESS FULL | DEPT | 1 | |* 9 | INDEX RANGE SCAN | EMP_FK | 5 |

-------------------------------------------------------

1 - filter("E"."ENAME"="ENAME") 5 - filter("SAL">100000) 8 - filter("LOC" LIKE 'A%')

9 - access("E"."DEPTNO"="DEPTNO")

Page 55: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

CMJ - Analysis

Dept

Bonus

Cartesian merge join

Nested Loops join

Emp

Dept

Bonus

Cartesian merge join

Nested Loops join

Emp

Estimate Actual

1

1

1

10

Emp estimated to be scanned once.

21

210

Emp scanned 210 times

Page 56: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

CMJ & cardinality   If the row source cardinality is incorrectly estimated as 1, then SQL statements with cartesian merge join can be a performance bottleneck.

  In some cases, it may be necessary to avoid this unnesting with an hint.

select * from emp e where e.deptno in (select /*+ no_unnest */ deptno from dept where loc like 'A%') and e.ename in (select ename from bonus where sal >100000); --------------------------------------------------------- | Id | Operation | Name | E-Rows | --------------------------------------------------------- |* 1 | FILTER | | | |* 2 | HASH JOIN SEMI | | 1 | | 3 | TABLE ACCESS FULL | EMP | 14 | |* 4 | TABLE ACCESS FULL | BONUS | 1 | |* 5 | TABLE ACCESS BY INDEX ROWID| DEPT | 1 | |* 6 | INDEX RANGE SCAN | DEPT_PK | 1 | ---------------------------------------------------------

Page 57: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

View merging

  View definitions are merged with calling query or DML statement and merged to create final execution plan.

create or replace view bonus_simple_vw as select job, sal from bonus;

select e.ename, e.job, e.sal, b.sal from emp e, bonus_simple_vw b where e.job=b.job;

--------------------------------------------- | Id | Operation | Name | E-Rows |

--------------------------------------------- | 1 | NESTED LOOPS | | 1 |

| 2 | TABLE ACCESS FULL| EMP | 14 | |* 3 | TABLE ACCESS FULL| BONUS | 1 | ---------------------------------------------

Predicate Information (identified by operation id):

---------------------------------------------------

3 - filter("E"."JOB"="JOB")

  Normal views do not store data, just the definition of the view.

Page 58: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

View merging

  For example, following complex view is not merged. Note the keyword VIEW in the execution plan.

create or replace view bonus_vw as select job, avg(sal) avg_sal from bonus group by job;

select e.ename, e.job,e.sal, b.avg_sal from emp e, bonus_vw b where e.job=b.job

----------------------------------------------------------------------------- | Id | Operation | Name | E-Rows | OMem | 1Mem | Used-Mem |

----------------------------------------------------------------------------- | 1 | NESTED LOOPS | | 3 | | | |

| 2 | TABLE ACCESS FULL | EMP | 14 | | | | |* 3 | VIEW | BONUS_VW | 1 | | | | | 4 | SORT GROUP BY | | 1 | 1024 | 1024 | |

| 5 | TABLE ACCESS FULL| BONUS | 1 | | | | -----------------------------------------------------------------------------

Predicate Information (identified by operation id): --------------------------------------------------- 3 - filter("E"."JOB"="B"."JOB")

  But, some views are not merge-able.

Page 59: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Non-mergeable

  In some cases, it is better to merge the views and in some cases, it is not.

  Usually, complex views with group by, union all, union, function calls are not merge-able.

  Use tkprof output file to understand how much time is spent in the step joining with non-merged view. Decide whether to merge or not based upon that.

  This depends upon how optimized each execution of the view is.

Page 60: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen 60

Writing optimal SQL

Page 61: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Avoid loop based processing         FOR c1 in cursor_c1 LOOP         --         v_commit_count := v_commit_count + 1;

        INSERT INTO RECS_TEMP         VALUES (c1.join_record      , --    JOIN_RECORD                 c1.location         , --    LOCATION                 c1.item_number      , --    ITEM_NUMBER                 c1.wh_report_group  , --    WH_REPORT_GROUP                 c1.po_number        , --    PO_NUMBER

...                 );         IF v_commit_count = 100000 THEN                 COMMIT;                 v_loop_counter := v_loop_counter + 1;                 v_commit_count := 0;         END IF;         --         END LOOP;

For each row from outer cursor..

Insert a row in to the table.

Commit after every 100K rows.

Page 62: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Loop based processing

Send SQL

Execute SQL

Fetch first row

Return row

Insert one row

Insert and send status

Fetch next row

Return row

Insert next row

PL/SQL SQL

Insert and send status

This is known as “chatty” application and encounters much performance issues…

Page 63: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Think in terms of set

  SQL is a set language. SQL is optimized to work as a set language.

  Avoid row-by-row processing if possible.

  In some cases, Loops are unavoidable.

Page 64: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Rewritten loop INSERT INTO RECS_TEMP (col1, col2, col3…coln) SELECT join_record      , --    JOIN_RECORD           location         , --    LOCATION           item_number      , --    ITEM_NUMBER           report_group  , --    WH_REPORT_GROUP           po_number        , --    PO_NUMBER ... FROM c1_cust_view t WHERE t.asset_id BETWEEN p_min_val AND p_max_val GROUP BY t.location||'.'||t.item_number, t.location, t.item_number, t.wh_report_group, t.po_number, t.loc_company, -- t.loc_company, t.location_type, t.asset_id, t.book_type_code, t.asset_major_category, t.asset_minor_category ;

One simple insert statement inserting all necessary rows.

Page 65: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Few guidelines…

  Use direct SQL statements.

  If PL/SQL loop can’t be avoided use array based processing.

  bulk bind, bulk fetch, bulk insert etc.

  If another host language such as ‘C’ or java, use arrays and host language facilities to reduce round trip network calls between client and the database.

Page 66: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Use joins (1)

  Optimizer will tries to convert almost all sub-queries to a join format.

  Join conditions provides more permutations for the optimizer to tune your SQL statement.

  Due to complex SQL statement, providing more options to optimizer to tune your SQL will improve the performance of that statement.

Page 67: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Use joins (1)

------------------------------------------------------- | Id | Operation | Name | E-Rows | ------------------------------------------------------- | 1 | TABLE ACCESS BY INDEX ROWID| EMP | 5 | | 2 | NESTED LOOPS | | 5 | | 3 | SORT UNIQUE | | 1 | |* 4 | TABLE ACCESS FULL | DEPT | 1 | |* 5 | INDEX RANGE SCAN | EMP_FK | 5 | ------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 4 - filter("DNAME" LIKE 'A%') 5 - access("E"."DEPTNO"="DEPTNO")

IN operator converted to a Nested Loops Join.

select ename from emp e where e.deptno in ( select deptno from dept d where dname like 'A%' );

Page 68: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Use joins (2)

select ename from emp e where exists ( select deptno from dept d where

dname like 'A%' and d.deptno=e.deptno);

------------------------------------------------------- | Id | Operation | Name | E-Rows | ------------------------------------------------------- | 1 | TABLE ACCESS BY INDEX ROWID| EMP | 5 | | 2 | NESTED LOOPS | | 5 | | 3 | SORT UNIQUE | | 1 | |* 4 | TABLE ACCESS FULL | DEPT | 1 | |* 5 | INDEX RANGE SCAN | EMP_FK | 5 | ------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 4 - filter("DNAME" LIKE 'A%') 5 - access("E"."DEPTNO"="DEPTNO")

EXISTS operator converted to a Nested Loops Join.

Page 69: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Use joins (3) select ename from emp e, (select distinct deptno, dname from dept where dname like 'A%') d Where d.deptno = e.deptno;

------------------------------------------------------- | Id | Operation | Name | E-Rows | ------------------------------------------------------- | 1 | TABLE ACCESS BY INDEX ROWID| EMP | 5 | | 2 | NESTED LOOPS | | 5 | | 3 | VIEW | | 1 | | 4 | HASH UNIQUE | | 1 | |* 5 | TABLE ACCESS FULL | DEPT | 1 | |* 6 | INDEX RANGE SCAN | EMP_FK | 5 | ------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 5 - filter("DNAME" LIKE 'A%') 6 - access("D"."DEPTNO"="E"."DEPTNO")

Instead, you might want to use join condition.

Of course, this SQL needed a distinct operator to Maintain equivalency.

Page 70: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Views over views

  Building views over views are not probably a good idea.

  It is unavoidable not to access few tables multiple times even when it is not necessary to do so.

  Instead of using views as base tables, bring in the view definition and then simplify the SQL statement.

Page 71: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Avoid column level subquery

Select n1, n 2, (select x1 from t5 where t5.n1 =t1.n1) amt, (select x2 from t6 where t6.n1 =t2.n1 ) amt1, get_approval_qty ( n1), .. From t1, t2 where t1.n1=t1.n2;

For every row, from the outer query, these two queries and function calls are executed once.

Page 72: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Transformed predicates

  Transformed predicates means that optimizer will not be able to choose index based execution plan.

-------------------------------------------- | Id | Operation | Name | E-Rows | --------------------------------------------

|* 1 | HASH JOIN | | 1 | |* 2 | TABLE ACCESS FULL| DEPT | 1 |

| 3 | TABLE ACCESS FULL| EMP | 14 | --------------------------------------------

Predicate Information (identified by operation id): ---------------------------------------------------

1 - access(SUBSTR(TO_CHAR("E"."DEPTNO"),1,10)=SUBSTR(TO_CHAR("D"."DEPTNO"),1,10)) 2 - filter("D"."DNAME" LIKE 'A%')

select ename from emp e, dept d where substr(to_char(e.deptno),1,10)= substr(to_char(d.deptno),1,10) and d.dname like 'A%'

Page 73: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Join predicates

  Avoid function calls and tranformed predicates in the join predicates.

-------------------------------------------- | Id | Operation | Name | E-Rows | -------------------------------------------- |* 1 | HASH JOIN | | 19 | | 2 | TABLE ACCESS FULL| DEPT | 4 | | 3 | TABLE ACCESS FULL| EMP | 14 | --------------------------------------------

Predicate Information (identified by operation id): --------------------------------------------------- 1 - access("E"."DEPTNO"=NVL("D"."DEPTNO",10))

select ename from emp e, dept d where e.deptno = nvl(d.deptno,10);

Page 74: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Implicit transformation

  Implicit transformation also can lead to poor execution plan. Column deptno2 is a character column and to_number function must be applied.

-------------------------------------------- | Id | Operation | Name | E-Rows |

-------------------------------------------- |* 1 | HASH JOIN | | 4 |

|* 2 | TABLE ACCESS FULL| DEPT | 1 | | 3 | TABLE ACCESS FULL| EMP | 14 | --------------------------------------------

Predicate Information (identified by operation id): ---------------------------------------------------

1 - access("D"."DEPTNO"=TO_NUMBER("E"."DEPTNO2")) 2 - filter("DNAME" LIKE 'A%')

select ename from emp e, dept d where e.deptno2 =d.deptno and dname like 'A%'

Page 75: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Conversion

  You can apply filter predicates in the correct side of equality join to use index access path.

------------------------------------------------------- | Id | Operation | Name | E-Rows |

------------------------------------------------------- | 1 | TABLE ACCESS BY INDEX ROWID| EMP | 5 |

| 2 | NESTED LOOPS | | 5 | |* 3 | TABLE ACCESS FULL | DEPT | 1 | |* 4 | INDEX RANGE SCAN | EMP_C1 | 5 |

------------------------------------------------------- Predicate Information (identified by operation id):

--------------------------------------------------- 3 - filter("DNAME" LIKE 'A%') 4 - access("E"."DEPTNO2"=TO_CHAR("D"."DEPTNO"))

select ename from emp e, dept d where e.deptno2 =to_char(d.deptno) and dname like 'A%‘;

Page 76: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

EXISTS vs IN Select * from emp where deptno in (select deptno from dept where name=‘ACCOUNTING’ ) ;

  If the inner subquery will return very few rows, then use IN operator.

  In this case, there is just one row from the dept table and so, IN operator is more appropriate.

  Simply put, driving table has very few rows and DEPT is the driving table for this SQL statement.

Page 77: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

EXISTS vs IN …2 Select * from emp e where exists (select deptno from dept where name=‘ACCOUNTING’

and e.deptno = d.deptno ) and e.empno = :b1 ;

  If the outer query is more selective, then use the EXISTS operator.

  In this case, there is just one row from emp table. So, accessing dept after filtering on emp table is more optimal.

Page 78: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Excessive revisits   Avoid revisiting data.

Select count(*) from employees where department=‘ACCOUNTING’;

Select count(*) from employees where department=‘FINANCE’ or ‘IT’;

Select count(*) from employees;

  Above SQL statement can be rewritten as: Employees accessed just once.

Select

count( case when department =‘ACCOUNTING’ then 1 else 0 end ) acct_cnt,

count( case when department =‘FINANCE’ or department=‘IT’ then 1 else 0 end) fin_it_cnt,

count(*)

From employees;

Page 79: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Full table scan

  Not all full table scans (FTS) are bad.

  In some cases, FTS is cheaper than performing index and then table block access.

  Especially, if the driving table has higher number of rows and the inner table has few rows, then it may be better to do FTS.

Rows Row Source Operation ------- --------------------------------------------------- 43935 TABLE ACCESS BY INDEX ROWID FA_METHODS (cr=743619 pr=272870 pw=0 time=826315272 us) 87870 NESTED LOOPS (cr=738078 pr=272865 pw=0 time=825760138 us) 43935 NESTED LOOPS (cr=644350 pr=272865 pw=0 time=809397304 us) ... 43935 INDEX RANGE SCAN FA_METHODS_U2 (cr=93728 pr=0 pw=0 time=16211647 us)(object id 32589)

FA_METHODS is a small table with 500 rows. But, outer table returned 43,000 times and FA_METHODS accessed 43,000 times.

Page 80: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

FTS and Hash join

  FTS combined with hash join will perform better in this case.

Rows Row Source Operation ------- --------------------------------------------------- 30076 HASH JOIN (cr=4250352 pr=1043030 pw=0 time=1887152322 us) 411487 NESTED LOOPS (cr=4250335 pr=1043019 pw=0 time=2396907676 us) 411487 NESTED LOOPS (cr=3015121 pr=861281 pw=0 time=2136848154 us) ... 580 TABLE ACCESS FULL FA_METHODS (cr=17 pr=11 pw=0 time=21346 us)

411K rows joined with 580 tables. Access to FA_METHODS took only 21 milli-seconds.

Page 81: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

NULL

  Certain constructs with ‘IS NULL’ predicates may not use index.

  NULL values are not stored in single column indices and so, index may not be usable.

  Even if you have index on deptno column, in the example below, that index can not be used. This query is very typical in manufacturing applications.

Select * from emp where deptno is null;

  One way to use index and improve performance is to use case statement and a function based index.

Create index emp_f1 on emp ( case when deptno is null then ‘X’ else null end);

Select * from emp where ( case when deptno is null then ‘X’ else null end) is not null;

Page 82: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Rule based?

  In olden days, one way to disallow RULE based optimizer (RBO) not to use index is to use arithmetic operations.

  Even if there is such a construct, Cost Based Optimizer will simply ignore it and start using the index.

  If the index is chosen while Full Table Scan is faster or if the execution plan is incorrect, then understand the reason as to why the optimizer is unable to choose the optimal plan.

Select * from emp where deptno+0 =100; Select * from emp where lastname ||’ ‘ = ‘ADAMS ‘;

Page 83: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Parallel queries

  If the SQL statement is running for longer time and if the performance is important, then Parallel queries (PQ) might be of help.

  PQ can be enabled by:

  Setting degree > 1 at the table or index level.

  Using parallel hints

explain plan for select /*+ parallel (a, 8) */ count(*) from ont.oe_order_lines_all a;

Explained.

select * from table(dbms_xplan.display);

------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Cost (%CPU)| Time | Pstart| Pstop | ------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 233K (4)| 00:11:42 | | | | 1 | SORT AGGREGATE | | 1 | | | | | | 2 | PARTITION HASH ALL| | 69M| 233K (4)| 00:11:42 | 1 | 32 | | 3 | INDEX FULL SCAN | OE_ORDER_LINES_U1 | 69M| 233K (4)| 00:11:42 | 1 | 32 | -------------------------------------------------------------------------------------------------

Page 84: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Parallel DML

  Parallel DML is also possible. Use this sparingly as bursty redo generation can cause instance wide performance issues.

Alter session enable parallel dml;

explain plan for update /*+ parallel (a 8) */ ont.oe_order_lines_all a set attribute2=attribute2||' ';

>select * from table(dbms_xplan.display);

------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Time | Pstart| Pstop | TQ |IN-OUT| ------------------------------------------------------------------------------------------------------- | 0 | UPDATE STATEMENT | | 69M| 00:18:44 | | | | | | | 1 | UPDATE | OE_ORDER_LINES_ALL | | | | | | | | | 2 | PX COORDINATOR | | | | | | | | | | 3 | PX SEND QC (RANDOM)| :TQ10000 | 69M| 00:18:44 | | | Q1,00 | P->S | | 4 | PX BLOCK ITERATOR | | 69M| 00:18:44 | 1 | 32 | Q1,00 | PCWC | | | 5 | TABLE ACCESS FULL| OE_ORDER_LINES_ALL | 69M| 00:18:44 | 1 | 32 | Q1,00 | PCWP | | -------------------------------------------------------------------------------------------------------

Page 85: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Parsing

  For every unique SQL statement, Oracle Database must parse the statement, authenticate statement and generate an execution plan.

  Use of literal variable makes SQL statements unsharable.

Select ename from emp where first_name=‘SCOTT’;

Select ename from emp where first_name=‘ADAM’;

Select ename from emp where first_name=‘JUNE’;

Select ename from emp where first_name=‘JAMES’;

  Following statement need to be hard parsed for execution. As the first name can change for every execution, it is not sharable.

Page 86: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen

Use Bind variables

  Use of bind variables leads to sharable SQL statement.

  Hard parsing a SQL statement is an expensive operation and lead to poor application scalability.

Select ename from emp where first_name=:B1;

  Handful of exceptions.

  SQL statements accessing columns with very low cardinality. Optimizer can determine the cardinality correctly with a literal value.

select * from transaction_interface where processed =‘N’;

  Data ware house queries which generally are not executed repeatedly.

Page 87: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen 87

Hints

Page 88: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen 88

HINTS

  Hints are directive to the optimizer and almost always honored by the optimizer.

  Hints are not honored only if the hints are inconsistent with itself or another hint.

  Avoid hints, if possible. Many software upgrade performance issues that I have seen is due to hints(bad).

  In some cases, hints are necessary evils

Page 89: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen 89

ORDERED

  ORDERED hint Dictates optimizer an exact sequence of tables to join [ top to bottom or L->R canonically speaking].

Select …

From t1,

t2,

t3,

t4

t1 t2 t3 t4

Page 90: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen 90

ORDERED

explain plan for select /*+ ORDERED */

t1.n1, t2.n2 from t_large2 t2,

t_large t1 where t1.n1 = t2.n1 and t2.n2=100 /

-------------------------------------------------------------------------------------

| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 13 | 1401 (5)| 00:00:08 |

| 1 | NESTED LOOPS | | 1 | 13 | 1401 (5)| 00:00:08 | |* 2 | INDEX FAST FULL SCAN| T_LARGE2_N2 | 1 | 8 | 1399 (6)| 00:00:07 |

|* 3 | INDEX RANGE SCAN | T_LARGE_N1 | 1 | 5 | 2 (0)| 00:00:01 | -------------------------------------------------------------------------------------

t2 t1

t1.n1=t2.n1

n2=100

Page 91: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen 91

ORDERED

  Later developer added another table to the join..

explain plan for select /*+ ORDERED */

t1.n1, t2.n2 from t_large3 t3,

t_large2 t2, t_large t1

where t1.n1 = t2.n1 and t2.n2=100

and t1.n1=t3.n1 /

t2 t1

t1.n1=t2.n1

n1=100

t3

----------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time | ----------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 18 | | 1397M (6)|999:59:59 | |* 1 | HASH JOIN | | 1 | 18 | 11M| 1397M (6)|999:59:59 | | 2 | MERGE JOIN CARTESIAN | | 499K| 6345K| | 1397M (6)|999:59:59 | | 3 | INDEX FAST FULL SCAN | T_LARGE3_N1 | 999K| 4880K| | 1161 (4)| 00:00:06 | | 4 | BUFFER SORT | | 1 | 8 | | 1397M (6)|999:59:59 | |* 5 | INDEX FAST FULL SCAN| T_LARGE2_N2 | 1 | 8 | | 1398 (6)| 00:00:07 | | 6 | INDEX FAST FULL SCAN | T_LARGE_N1 | 1100K| 5371K| | 1176 (5)| 00:00:06 | -----------------------------------------------------------------------------------------------

Optimizer did exactly what it is told to!

Page 92: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen 92

Ordered & leading

explain plan for select /*+ leading (t2) */

t1.n1, t2.n2 from t_large3 t3,

t_large2 t2, t_large t1

where t1.n1 = t2.n1 and t2.n2=100

and t1.n1=t3.n1 /

t2 t1

t1.n1=t2.n1

n1=100

t3

--------------------------------------------------------------------------------------

| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------------

| 0 | SELECT STATEMENT | | 1 | 18 | 1403 (5)| 00:00:08 | | 1 | NESTED LOOPS | | 1 | 18 | 1403 (5)| 00:00:08 | | 2 | NESTED LOOPS | | 1 | 13 | 1401 (5)| 00:00:08 |

|* 3 | INDEX FAST FULL SCAN| T_LARGE2_N2 | 1 | 8 | 1399 (6)| 00:00:07 | |* 4 | INDEX RANGE SCAN | T_LARGE_N1 | 1 | 5 | 2 (0)| 00:00:01 |

|* 5 | INDEX RANGE SCAN | T_LARGE3_N1 | 1 | 5 | 2 (0)| 00:00:01 |

If you must, use leading instead of ordered..

Page 93: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen 93

Index hint

explain plan for select /*+ index (t, t_large_n2) */ n2 from t_large t where n1=:b1;

------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |

------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 8 | 3 (0)| 00:00:01 | |* 1 | INDEX RANGE SCAN| T_LARGE_N2 | 1 | 8 | 3 (0)| 00:00:01 |

-------------------------------------------------------------------------------

drop index t_large_n2; create index t_large_n2 on t_large(n2, n1);

-------------------------------------------------------------------------------

| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 8 | 12 (0)| 00:00:01 |

|* 1 | INDEX SKIP SCAN | T_LARGE_N2 | 1 | 8 | 12 (0)| 00:00:01 | -------------------------------------------------------------------------------

Index hint specifies what index to use. If you must use index hint, specify columns instead of index name.

explain plan for select /*+ index ( t n1 n2) */ n2 from t_large t where n1=:b1;

------------------------------------------------------------------------------------------ | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |

------------------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 1 | 8 | 4 (0)| 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID| T_LARGE | 1 | 8 | 4 (0)| 00:00:01 |

|* 2 | INDEX RANGE SCAN | T_LARGE_N1 | 1 | | 3 (0)| 00:00:01 | ------------------------------------------------------------------------------------------

Page 94: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen 94

Cardinality hint

explain plan for select /*+ cardinality (t, 10) */ n2 from t_large t where n1=:b1;

------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 10 | 260 | 1 (0)| 00:00:01 | |* 1 | INDEX RANGE SCAN| T_LARGE_N2 | 10 | 260 | 2 (0)| 00:00:01 | -------------------------------------------------------------------------------

SQL> explain plan for select /*+ cardinality (t, 120) */ n2 from t_large t where n1=:b1;

Explained.

SQL> @e ------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 120 | 3120 | 1 (0)| 00:00:01 | |* 1 | INDEX RANGE SCAN| T_LARGE_N2 | 120 | 3120 | 2 (0)| 00:00:01 | -------------------------------------------------------------------------------

Cardinality hint specifies number of rows retrieved in a specific step.

  We can change cardinality estimates to a different value.

Page 95: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen 95

OUTLINE

explain plan for select /*+ cardinality (t, 10) */ n2 from t_large t where n1=:b1;

select * from table (dbms_xplan.display ('','','OUTLINE'))

------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 10 | 260 | 1 (0)| 00:00:01 | |* 1 | INDEX RANGE SCAN| T_LARGE_N2 | 10 | 260 | 2 (0)| 00:00:01 | -------------------------------------------------------------------------------

Outline Data -------------

/*+ BEGIN_OUTLINE_DATA INDEX(@"SEL$1" "T"@"SEL$1" ("T_LARGE"."N1" "T_LARGE"."N2")) OUTLINE_LEAF(@"SEL$1") ALL_ROWS OPT_PARAM('_optimizer_rownum_pred_based_fkr' 'false') OPT_PARAM('_fast_full_scan_enabled' 'false') OPT_PARAM('_b_tree_bitmap_plans' 'false') OPTIMIZER_FEATURES_ENABLE('10.2.0.4') IGNORE_OPTIM_EMBEDDED_HINTS END_OUTLINE_DATA */

  If you don’t know the hints then you could inspect the hints using OUTLINE option in the dbms_xplan package call.

SEL$1 is a system generated query Block name.

Page 96: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen 96

QB_NAME

explain plan for select /*+ qb_name (T_LARGE_SEL) */ n2 from t_large t where n1=:b1;

select * from table (dbms_xplan.display ('','','OUTLINE')); -------------------------------------------------------------------------------

| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 6 | 156 | 2 (0)| 00:00:01 |

|* 1 | INDEX RANGE SCAN| T_LARGE_N2 | 6 | 156 | 2 (0)| 00:00:01 | -------------------------------------------------------------------------------

Outline Data ------------- /*+

BEGIN_OUTLINE_DATA INDEX(@"T_LARGE_SEL" "T"@"T_LARGE_SEL" ("T_LARGE"."N1" "T_LARGE"."N2"))

... END_OUTLINE_DATA */

  QB_NAME hint can be used to name a query block.

Page 97: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen 97

QB_NAME usage

select /*+ qb_name (main) leading(e@main dept@sel_dept bonus@sel_bonus) */ * from emp e

where e.deptno in

(select /*+ qb_name (sel_dept) */ deptno from dept where loc like 'A%') and e.ename in (select /*+ qb_name (sel_bonus) */ ename from bonus where sal >100000);

/*+

BEGIN_OUTLINE_DATA IGNORE_OPTIM_EMBEDDED_HINTS OPTIMIZER_FEATURES_ENABLE('10.2.0.4')

… UNNEST(@"SEL_BONUS")

UNNEST(@"SEL_DEPT") OUTLINE(@"MAIN") OUTLINE(@"SEL_BONUS")

OUTLINE(@"SEL_DEPT") …

LEADING(@"SEL$05DAEEF6" "E"@"MAIN" "DEPT"@"SEL_DEPT“ "BONUS"@"SEL_BONUS") USE_HASH(@"SEL$05DAEEF6" "DEPT"@"SEL_DEPT") END_OUTLINE_DATA

*/

  QB_NAME hint is quite handy in specifying tables in a sub-query in an hint.

Hints specifying user named query blocks.

Page 98: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen 98

Indexing, partitioning and more

Page 99: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen 99

Choose index wisely

explain plan for select * from emp where ename ='JACOB' and hiredate > sysdate-365;

-------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |

-------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 40 | 2 (0)| 00:00:01 | |* 1 | TABLE ACCESS FULL| EMP | 1 | 40 | 2 (0)| 00:00:01 |

--------------------------------------------------------------------------

Predicate Information (identified by operation id): ---------------------------------------------------

1 - filter("ENAME"='JACOB' AND "HIREDATE">SYSDATE@!-365)

  Leading column of indices should contain most common predicates.

  In the example above, creating an index on ename would be helpful.

Page 100: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen 100

Join keys for indices

explain plan for select * from emp e where e.deptno in

(select /*+ qb_name (sel_dept) */ deptno from dept where loc like 'A%') and e.ename in

(select /*+ qb_name (sel_bonus) */ ename from bonus where sal >100000) / -----------------------------------------------------------------------------

| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -----------------------------------------------------------------------------

| 0 | SELECT STATEMENT | | 1 | 71 | 7 (15)| 00:00:01 | |* 1 | HASH JOIN SEMI | | 1 | 71 | 7 (15)| 00:00:01 | |* 2 | HASH JOIN SEMI | | 5 | 255 | 5 (20)| 00:00:01 |

| 3 | TABLE ACCESS FULL| EMP | 14 | 560 | 2 (0)| 00:00:01 | |* 4 | TABLE ACCESS FULL| DEPT | 1 | 11 | 2 (0)| 00:00:01 |

|* 5 | TABLE ACCESS FULL | BONUS | 1 | 20 | 2 (0)| 00:00:01 | -----------------------------------------------------------------------------

Predicate Information (identified by operation id): ---------------------------------------------------

1 - access("E"."ENAME"="ENAME") 2 - access("E"."DEPTNO"="DEPTNO")

4 - filter("LOC" LIKE 'A%') 5 - filter("SAL">100000)

  For frequently executed queries, try to see if indices can be created on join key columns.

Creating indices on these join key columns would be beneficial.

Page 101: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen 101

Column selectivity

Table Number Empty Average Name of Rows Blocks Blocks Space --------------- -------------- ------------ ------------ ------- MTL_MATERIAL_TR 170,392,833 13,551,182 0 0 ANSACTIONS

Column Column Distinct Name Details Values ------------------------- ------------------------ -------------- ... ORGANIZATION_ID NUMBER(22) NOT NULL 2,589 INVENTORY_ITEM_ID NUMBER(22) NOT NULL 7,703 ...

  Selectivity of the column indices how many rows will be returned from the table.

  For organization_id alone in the index, approximately, each key entry will return 65,814 rows: 170392833/2589 = 65814

  For organization_id , inventory_item_id; Each combination of key entry will return just 8 rows: 170392833/(2589*7703) = 8

Where organization_id = :b1 and inventory_item_id=:b2

Page 102: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen 102

Avoid updated columns

  Do not index columns that are updated heavily.

  Updates to indexed columns are equivalent of a delete and insert at index level.

 In this example, quantity is a good candidate column, but updated heavily. So, that column should not be indexed. explain plan for Select attribute1, quantity, quantity_cancelled, quantity_delivered from

PO_REQUISITION_LINES_ALL where item_id=:b1 and quantity >100 /

------------------------------------------------------------------------ | Id | Operation | Name | Rows | ------------------------------------------------------------------------

| 0 | SELECT STATEMENT | | 1545 | |* 1 | TABLE ACCESS BY INDEX ROWID| PO_REQUISITION_LINES_ALL | 1545 |

|* 2 | INDEX RANGE SCAN | PO_REQUISITION_LINES_N7 | 1545 | ------------------------------------------------------------------------

1 - filter("QUANTITY">100) 2 - access("ITEM_ID"=TO_NUMBER(:B1))

Page 103: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen 103

Other index types

  There are more index types then just b-tree indices

  bitmap indices

  Index organized tables

  Partitioned indices etc.

  Consider activity while choosing index. For example, bitmap indices are useful only for read only tables (like data warehouse tables).

  Index Organized tables are suitable for tables which are accessed just using just leading columns of the table.

Page 104: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen 104

Index efficiency?

DESC T1 ... N2 NUMBER N3 NUMBER ...

select count(distinct(n2)) n2_count, count(distinct(n3)) n3_count from t1;

N2_COUNT N3_COUNT ---------- ---------- 62 63

1 row selected.

create index t1_n2 on t1(n2); create index t1_n3 on t1(n3);

Begin dbms_Stats.gather_table_stats ( user, 't1' , estimate_percent =>100, cascade =>true); End; /

explain plan for select n1 from t1 where n3=:b1; ------------------------------------------------------------------ | Id | Operation | Name | Rows | Cost (%CPU)| ------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 61 | 3 (0)| | 1 | TABLE ACCESS BY INDEX ROWID| T1 | 61 | 3 (0)| |* 2 | INDEX RANGE SCAN | T1_N3 | 61 | 1 (0)| ------------------------------------------------------------------

Predicate Information (identified by operation id): ---------------------------------------------------

2 - access("N3"=TO_NUMBER(:B1))

explain plan for select n1 from t1 where n2=:b1;

------------------------------------------------------- | Id | Operation | Name | Rows | Cost (%CPU)| ------------------------------------------------------- | 0 | SELECT STATEMENT | | 62 | 19 (0)| |* 1 | TABLE ACCESS FULL| T1 | 62 | 19 (0)| ------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("N2"=TO_NUMBER(:B1))

Why would the optimizer not choose t1_n2 index?

Page 105: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen 105

Index properties..

drop table t1; create table t1

(n1 number, n2 number,

n3 number, v1 varchar2(100) );

insert into t1 select l1,

mod(l1, 62) n2, round (l1/62) n3, lpad(l1, 100,'x') v1

from (select level l1 from dual

connect by level <=3844);

commit;

Property T1_N2 T1_N3

Unique No No

Level 1 1

Leaf blocks 8 8

Distinct keys 62 63

# of rows 3844 3844

Avg. leaf block/key 1 1

Avg. datablock/key 61 1

Clustering factor 3843 78

Page 106: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen 106

Index efficiency

Index with Low clustering factor

Table Root block

Branch block

Leaf blocks

n3=:b1

Data file

120

121

122

123

124

125

126

Page 107: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen 107

Index efficiency

Index with high Clustering factor

Table Root block

Branch block

Leaf blocks

n2=:b1

Data file

120

121

122

123

124

125

126

Page 108: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen 108

Index efficiency

  Optimizer did not choose index on n2 since clustering factor of that index is very high.

  Accessing table through the index will be costlier due to visits to table blocks can increase I/O.

  Avoiding table blocks by adding more columns to that index might be of help.

Page 109: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen 109

Partitioning

  Consider table partitioning for many reasons:

  Easy archiving of data. Example: gl_balances on period_name

  Performance of table scan. For example, report SQLs accessing just last period on gl_balances table.

  Partition to improve scalability of the table. Hash partition the table inserted aggressively with primary key/unique keys.

Page 110: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen 110

Unscalable index

Root block

Branch block

Leaf blocks

Primary key index on line_id

Ses 1: inserting value of 10000

Ses 2: inserting value of 10001

Ses 3: inserting value of 10002

Ses 4: inserting value of 10003

All sessions inserting into a leaf block.

Page 111: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen 111

Hash partitioning

Root block

Branch block

Leaf blocks

Primary key index on line_id

All sessions inserting into a leaf block.

Root block

Branch block

Ses 1: inserting value of 10000 Ses 2: inserting value of 10001

Ses 3: inserting value of 10002 Ses 4: inserting value of 10003

Page 112: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen 112

Identifying performance issues

in Production

Page 113: Performance tuning   a quick intoduction

Page 113

Thinking about performance

Is the session waiting for a resource?

Try to understand the problem

Decompose the problem.

Where was the time spent? Which SQL consumed time? (Breakdown analysis)

If one SQL causing issues, is there any execution plan change? Review execution plan and SQL performance history.

Any instance or cluster wide database issues?

Is there any statistics issue? Review stats at table, index and column level.

Is there increase in data processed by this SQL statement?

Page 114: Performance tuning   a quick intoduction

ATT June 2010 Riyaj Shamsudeen 114

Identify session wait For example:

USER_CONCURRENT_PROGRAM_N EXECUTION_FILE_NAME SUBROUTINE_NAME PH

------------------------- ------------------------- --------------- -- Custom Inv extract Invoice_Extract_To ...

INST_ID SID SERIAL# SPID EVENT STATE ---------- ---------- ---------- ------------ ------------------------- ----------

4 10417 45212 29971 db file scattered read WAITING

State is very important. If the state is not WAITING, then the event is irrelevant.

Page 115: Performance tuning   a quick intoduction

115

Session details

  Review session details with ash_session_detail.sql   This script retrieves information about the session using Active

Session History view.

  For example: SQL> @ash_session_details.sql

SID SERIAL# -------- ----------

10604 20697

OraInternals June 2010 Riyaj Shamsudeen

Page 116: Performance tuning   a quick intoduction

116

Session details… (2) Waits in the past five minutes. .. This is a sample of waits, not currently waiting. .. Gives you an idea about where could be the problem .. Again, these are samples and actuals. So, care must be taken to understand this SAMPLE_TIME SESSION_ID ST WAIT_TIME TIME_WAITED SEQ# P1_P2_P3_TEXT ------------------------- ---------- ---------------------------------------- --------- ----------- ---------- ------------------

11-JUN-10 05.09.39.712 PM 10604 db file sequential read WAITING 0 3457 11047 file# 328-block# 293543-blocks 1

11-JUN-10 05.09.40.742 PM 10604 db file sequential read WAITING 0 14885 11160 file# 32-block# 443309-blocks 1

11-JUN-10 05.09.41.774 PM 10604 ON CPU 1054 0 11292 file# 410-block# 487451-blocks 1

11-JUN-10 05.09.42.801 PM 10604 ON CPU 2437 0 11416 129- 667948- 33619969

11-JUN-10 05.09.43.821 PM 10604 db file sequential read WAITING 0 0 11525 file# 992-block# 251572-blocks 1

Seq# is changing indicating that session is not waiting for One occurrence of event, but many occurrences of the events.

Session is working on the database and performing some work. Not stuck for anything recently.

OraInternals June 2010 Riyaj Shamsudeen

Page 117: Performance tuning   a quick intoduction

117

Session details… (3) Waits with available history of the session .. This is a summary of waits

START_TIME END_TIME EVENT CNT_ON_CPU CNT_WAITING ------------------------- ------------------------- ------------------------- ---------- ----------- 11-JUN-10 05.00.06.744 PM 11-JUN-10 05.09.43.821 PM ON CPU 232 0 db file scattered read 0 3 db file sequential read 0 210 gc buffer busy 0 1 gc cr grant 2-way 0 30 gc cr grant congested 0 1 gc cr multi block request 0 1 gc current block 2-way 0 2 gc current grant 2-way 0 49 gc current grant 0 2 congested

row cache lock 0 19

11 rows selected.

Session started at 05PM. It is observed that session using CPU in 232 samples and waiting for single block reads in 210 samples.

This output tells us that session is using CPU and waiting for single block reads in a nearly 50-50 split.

OraInternals June 2010 Riyaj Shamsudeen

Page 118: Performance tuning   a quick intoduction

118

Session details… (4) Waits with avaialble history of the session .. This is a summary of waits for sql_ids

START_TIME END_TIME SQL_ID CNT_ON_CPU CNT_WAITING TOT_CNT RNK ------------------------- ------------------------- ------------- ---------- ----------- ---------- ---------- 06-JUL-10 04.00.01.933 PM 06-JUL-10 04.37.05.647 PM 2w9n14h63ygy6 256 1836 2092 1 1sfcx2m4n4u67 25 4 29 2

This session was observed executing the SQL with sql_id 2w9n14h63ygy6 in 2092 samples.

So, if we need to reduce the run time of this program, we need to tune the top SQL statement 2w9n14h63ygy6.

OraInternals June 2010 Riyaj Shamsudeen

Page 119: Performance tuning   a quick intoduction

119

Session details… (5) Waits with avaialble history of the session .. This is a summary of waits for sql_ids

START_TIME END_TIME SQL_ID EVENT TOT_CNT RNK

------------------------- ------------------------- ------------- ------------------------------ ---------- ---------- 06-JUL-10 04.48.39.073 PM 06-JUL-10 05.14.59.218 PM 77shh7fxb5fau db file scattered read 202 1 acj84tu2kx7zz db file scattered read 188 2

acj84tu2kx7zz gc cr multi block request 153 3 77shh7fxb5fau gc cr multi block request 151 4 77shh7fxb5fau 124 5 acj84tu2kx7zz 113 6 77shh7fxb5fau db file parallel read 82 7 acj84tu2kx7zz db file parallel read 53 8 gg7c75z1w0zz6 19 9 gg7c75z1w0zz6 gc cr multi block request 18 10

10 rows selected.

This session was observed executing many SQL statements. Most statements are waiting for full table scan since the event is ‘db file scattered read’.

OraInternals June 2010 Riyaj Shamsudeen

Page 120: Performance tuning   a quick intoduction

120

SQL Details… SQL> @sql_details bspofin01a:PROD1>@"sql_details.sql" Enter value for sql_id: bt8wv3whmnpvg

SQL_ID CPU_TIME ELA_TIME EXECUTIONS CPU_TIME_PER_EXEC ELA_TIME_PER_EXEC BGETS_PER_EXEC DRDS_PER_EXEC ROWS_PER_EXEC ------------- ---------- ---------- ---------- ----------------- ----------------- -------------- ------------- ------------- bt8wv3whmnpvg 138.26 189.18 43600 .003171305 .004339 37.6788761 .079610092 .939220183

bspofin01a:PROD1> bspofin01a:PROD1>/

SQL_ID CPU_TIME ELA_TIME EXECUTIONS CPU_TIME_PER_EXEC ELA_TIME_PER_EXEC BGETS_PER_EXEC DRDS_PER_EXEC ROWS_PER_EXEC ------------- ---------- ---------- ---------- ----------------- ----------------- -------------- ------------- ------------- bt8wv3whmnpvg 138.37 189.3 43639 .003170984 .004337927 37.6730448 .079584775 .939228672

Executing this SQL multiple times shows that if the SQL is executed just once or frequently.

Also, shows the elapsed time, cpu time, buffer gets , rows processed per execution in a readable format.

OraInternals June 2010 Riyaj Shamsudeen

Page 121: Performance tuning   a quick intoduction

121

SQL Plan… @xpcur.sql

SQL_ID bt8wv3whmnpvg, child number 0 ------------------------------------- SELECT WIAS.ACTIVITY_STATUS, WIAS.ACTIVITY_RESULT_CODE, WIAS.PROCESS_ACTIVITY FROM WF_ITEM_ACTIVITY_STATUSES WIAS, WF_PROCESS_ACTIVITIES WPA WHERE WIAS.ITEM_KEY = :B3 AND WIAS.ITEM_TYPE = :B2 AND WPA.ACTIVITY_NAME = :B1 AND WIAS.PROCESS_ACTIVITY = WPA.INSTANCE_ID

Plan hash value: 3698887934

---------------------------------------------------------------------------------------- | Id | Operation | Name | E-Rows | ---------------------------------------------------------------------------------------- | 1 | NESTED LOOPS | | 3 | | 2 | PARTITION RANGE SINGLE | | 4 | | 3 | PARTITION HASH SINGLE | | 4 | | 4 | TABLE ACCESS BY LOCAL INDEX ROWID| WF_ITEM_ACTIVITY_STATUSES | 4 | |* 5 | INDEX RANGE SCAN | CCW_WF_ITEM_ACTIVITY_STATUS_N5 | 4 | |* 6 | TABLE ACCESS BY INDEX ROWID | WF_PROCESS_ACTIVITIES | 1 | |* 7 | INDEX UNIQUE SCAN | WF_PROCESS_ACTIVITIES_PK | 1 | ----------------------------------------------------------------------------------------

Predicate Information (identified by operation id): ---------------------------------------------------

5 - access("WIAS"."ITEM_KEY"=:B3 AND "WIAS"."ITEM_TYPE"=:B2) 6 - filter("WPA"."ACTIVITY_NAME"=:B1) 7 - access("WIAS"."PROCESS_ACTIVITY"="WPA"."INSTANCE_ID")

This script is to fetch the current execution plan for the SQL statement.

There can be many children and plan can be different across instances.

OraInternals June 2010 Riyaj Shamsudeen

Page 122: Performance tuning   a quick intoduction

122

SQL History… SQL> @awr_sql_history Enter the SQL_ID: 53ph4hjg7dmtb Enter number of days (backwards from this hour) to report (default: ALL): 5 +--------------------------------------------------------------------------------------------------+ |Plan HV Min Snap Max Snap Execs LIO PIO CPU Elapsed | +--------------------------------------------------------------------------------------------------+ |3239720338 11464 11614 341 432,102,720 1,455,517 13,922.17 28,535.22 | +--------------------------------------------------------------------------------------------------+ . ========== PHV = 3239720338========== First seen from "06/07/10 17:00:13" (snap #11464) Last seen from "06/10/10 20:00:00" (snap #11614) . Execs LIO PIO CPU Elapsed ===== === === === ======= 341 1,455,517 432,102,720 13,922.17 28,535.22 . Plan hash value: 3239720338

-------------------------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop | -------------------------------------------------------------------------------------------------------------------------- | 0 | DELETE STATEMENT | | | | 3 (100)| | | | | 1 | DELETE | WF_ITEM_ACTIVITY_STATUSES | | | | | | | | 2 | PARTITION RANGE SINGLE| | 3 | 108 | 3 (0)| 00:00:01 | KEY | KEY | | 3 | PARTITION HASH SINGLE| | 3 | 108 | 3 (0)| 00:00:01 | KEY | KEY | | 4 | INDEX RANGE SCAN | CCW_WF_ITEM_ACTIVITY_STATUS_N5 | 3 | 108 | 3 (0)| 00:00:01 | KEY | KEY | --------------------------------------------------------------------------------------------------------------------------

Summary Execution Statistics Over Time Avg Avg Snapshot Avg LIO Avg PIO CPU (secs) Elapsed (secs) Time Execs Per Exec Per Exec Per Exec Per Exec ------------ -------- ------------------- ------------------- ------------------- ------------------- 07-JUN 17:00 19 589,986.84 3,043.89 21.50 55.15 07-JUN 17:30 12 1,368,471.83 7,357.75 48.21 126.40 07-JUN 18:00 4 4,063,364.00 6,779.25 118.69 180.04 07-JUN 20:00 27 932,021.63 1,941.33 29.21 50.70 07-JUN 22:00 1 10,350,626.00 35,867.00 334.29 683.23 08-JUN 00:00 1 9,345,543.00 31,275.00 287.37 582.39

OraInternals June 2010 Riyaj Shamsudeen

Page 123: Performance tuning   a quick intoduction

123

SQL History… (2) SQL> @awr_sql_history +--------------------------------------------------------------------------------------------------+ |Plan HV Min Snap Max Snap Execs LIO PIO CPU Elapsed | +--------------------------------------------------------------------------------------------------+ |4152136498 11844 12007 1,737 30,521,209,942 514,449 682,669.22 769,689.90 | |3744705170 12010 12147 1,588 17,624,970,347 658,412 411,930.84 468,395.55 | |4280475418 12148 12178 547 3,040,635,549 55,481 81,962.93 95,473.93 | +--------------------------------------------------------------------------------------------------+ . ========== PHV = 4152136498========== First seen from "06/15/10 15:00:07" (snap #11844) Last seen from "06/19/10 00:30:11" (snap #12007) … ========== PHV = 3744705170========== First seen from "06/19/10 02:00:19" (snap #12010) Last seen from "06/21/10 22:30:05" (snap #12147) ... Execs LIO PIO CPU Elapsed ===== === === === ======= 1,588 658,412 17,624,970,347 411,930.84 468,395.55 ...

========== PHV = 4280475418========== First seen from "06/21/10 23:00:02" (snap #12148) Last seen from "06/22/10 14:00:19" (snap #12178)

Execs LIO PIO CPU Elapsed ===== === === === ======= 547 55,481 3,040,635,549 81,962.93 95,473.93 ...

This SQL had three execution plans.

Above section shows when these plans were in use.

OraInternals June 2010 Riyaj Shamsudeen

Page 124: Performance tuning   a quick intoduction

124

SQL History… (3) Snapshot Avg LIO Avg PIO CPU (secs) Elapsed (secs) Time Execs Per Exec Per Exec Per Exec Per Exec ------------ -------- ------------------- ------------------- ------------------- ------------------- 20-JUN 05:30 20 10,802,003.30 3.55 244.27 244.49 20-JUN 06:00 20 10,469,251.05 6.40 240.89 241.10 20-JUN 08:30 10 10,731,641.80 12,233.40 285.55 601.76 20-JUN 09:00 10 10,333,361.60 16.10 229.33 230.20 20-JUN 09:30 20 11,215,536.45 10.25 248.07 248.11 20-JUN 10:00 20 11,049,868.80 10.50 239.99 240.09 ... 22-JUN 10:30 21 5,227,556.10 144.19 142.67 165.10 22-JUN 11:00 20 5,355,930.35 156.20 146.93 176.22 22-JUN 11:30 15 6,730,461.87 192.20 186.53 221.81 22-JUN 12:00 20 5,342,742.10 88.25 145.67 173.39 22-JUN 12:30 20 5,344,663.40 90.65 146.75 173.12 22-JUN 13:00 20 5,335,895.55 92.80 145.05 167.10 22-JUN 13:30 20 5,336,389.95 63.45 144.69 166.85 22-JUN 14:00 20 5,336,126.60 63.80 144.24 165.58 -------- ------------------- ------------------- ------------------- ------------------- avg 14,636,548.90 1,048.47 338.41 428.53

This section shows how the execution statistics of the plan changed. It was using almost 10 million LIO per execution around 20th Jun. After 22-Jun, it was performing 6 million LIOs

OraInternals June 2010 Riyaj Shamsudeen

Page 125: Performance tuning   a quick intoduction

125

SQL History… (3) Avg Avg

Plan Snapshot Avg LIO Avg PIO CPU (secs) Elapsed (secs) Hash Value Time Execs Per Exec Per Exec Per Exec Per Exec ---------- ------------ -------- ------------------- ------------------- ------------------- ------------------- 3744705170 21-JUN 12:30 15 8,524,628.27 271.80 213.67 267.43 21-JUN 13:00 15 5,717,546.87 254.80 146.71 238.70 ... 4152136498 15-JUN 15:00 20 8,865,314.60 133.55 199.20 214.41 15-JUN 15:30 20 7,642,152.90 169.70 171.14 190.17 15-JUN 16:00 15 12,298,334.13 241.87 276.54 309.40 ... 4280475418 21-JUN 23:00 5 6,901,149.80 78.60 171.43 184.42 21-JUN 23:30 15 6,592,838.73 92.80 173.07 192.28 22-JUN 00:00 15 6,540,028.13 104.47 176.42 198.56

This section shows how various execution plans were in play. Plan with plan_hash_value 4280475418 is the winner.

`

OraInternals June 2010 Riyaj Shamsudeen

Page 126: Performance tuning   a quick intoduction

126

CBO stats (1) @get_cbostats Please enter Name of Table Owner (Null = APPS): inv Please enter Table Name to show Statistics for: mtl_material_transactions ********** Table Level ***********

Table Number Empty Average Chain Average Global User Sample Date Name of Rows Blocks Blocks Space Count Row Len Stats Stats Size MM-DD-YYYY --------------- -------------- ------------ ------------ ------- ----------- ------- ------ ------ -------------- ---------- MTL_MATERIAL_TR 147,336,280 14,602,892 0 0 0 310 YES NO 44,200,884 06-16-2010 ANSACTIONS

Column Column Distinct Number Number Global User Sample Date Name Details Values Density Buckets Nulls Stats Stats Size MM-DD-YYYY ------------------------- ------------ --------- ------- ------- ----------- ------ ------ -------------- ---------- OVERCOMPLETION_TRANSACTIO NUMBER(22) 0 0 0 ########### YES NO 06-16-2010 N_QTY REASON_ID NUMBER(22) 73 0 1 ########### YES NO 4,536,756 06-16-2010 DISTRIBUTION_ACCOUNT_ID NUMBER(22) 26,945 0 1 61,312,033 YES NO 25,807,274 06-16-2010 ...

get_cbostats.sql is handy in showing the statistics of table in one step.

OraInternals June 2010 Riyaj Shamsudeen

Page 127: Performance tuning   a quick intoduction

127

CBO stats (2) .. Index Column Col Column Name Name Pos Details --------------- ------------------------- ---- ------------------------ CCW_MTL_MATERIA SOURCE_LINE_ID 1 NUMBER(22) L_TRANS_N1 TRX_SOURCE_LINE_ID 2 NUMBER(22) INVENTORY_ITEM_ID 3 NUMBER(22) NOT NULL CCW_MTL_MATERIA TRX_SOURCE_LINE_ID 1 NUMBER(22) L_TRANS_N2 INVENTORY_ITEM_ID 2 NUMBER(22) NOT NULL CCW_MTL_MATERIA CREATION_DATE 1 DATE NOT NULL L_TRANS_N3 CCW_MTL_MATERIA INVENTORY_ITEM_ID 1 NUMBER(22) NOT NULL L_TRANS_N5 ORGANIZATION_ID 2 NUMBER(22) NOT NULL TRANSACTION_TYPE_ID 3 NUMBER(22) NOT NULL TRANSACTION_SOURCE_TYPE_I 4 NUMBER(22) NOT NULL D

Also prints index details, statistics of those indices etc.

OraInternals June 2010 Riyaj Shamsudeen

Page 128: Performance tuning   a quick intoduction

128

SQL tracing … (1)

To trace a running session in another window, use dbms_monitor package from 10g onwards: Scripts: enable_sqltrace.sql and disable_sqltrace.sql

exec dbms_monitor.SESSION_TRACE_ENABLE ( &sid, &serial, true, false);

exec dbms_monitor.SESSION_TRACE_ENABLE ( &sid, &serial, true, false);

OraInternals June 2010 Riyaj Shamsudeen

Page 129: Performance tuning   a quick intoduction

129

AWR reports (1)

Use my scripts to generate AWR report in a single script.

-- This generates AWR from all available instances for the last snap interval. @awrrpt_all_gen.sql

OraInternals June 2010 Riyaj Shamsudeen

Page 130: Performance tuning   a quick intoduction

130

AWR reports range (2)

Use my scripts to generate AWR report for a given range

-- This generates AWR from all available instances for an interval. -- prompts for snaps to choose @awrrpt_all_range_gen.sql

OraInternals June 2010 Riyaj Shamsudeen

Page 131: Performance tuning   a quick intoduction

131

Wait details (1)

These scripts give nice overview of session wait details. wait_details.sql for current instance. Uses v$ views wait_details_rac.sql for global view, uses gv$views

@wait_details.sql

SID PID EVENT USERNAME OSUSER STATE WAIT_TIME WIS P1_P2_P3_TEXT ------ ---------- ------------------------------ ---------- ---------- ------------------- --------- ----- ---------------------------------------- 10037 3417 gc cr request APPS applprod WAITING 0 0 file# 780-block# 707453-class# 1 10046 24221 db file sequential read APPS applprod WAITING 0 0 file# 949-block# 381820-blocks 1 9567 19391 gc cr request APPS applprod WAITING 0 0 file# 506-block# 1977-class# 4377 9878 24926 db file sequential read APPS applprod WAITED SHORT TIME -1 0 file# 229-block# 949561-blocks 1 9675 20716 gc cr request APPS applprod WAITED SHORT TIME -1 0 file# 984-block# 464587-class# 1

OraInternals June 2010 Riyaj Shamsudeen

Page 132: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen 132

Resolutions: SQL Profiles

Page 133: Performance tuning   a quick intoduction

133

Introduction

  SQL Profiles can be used to stabilize the execution plan.

  SQL Profiles are similar to outlines, but much versatile and powerful.

  SQL Execution plan can be modified without even touching the code or statistics.

  Very useful to stabilize critical SQL statements.

OraInternals June 2010 Riyaj Shamsudeen

Page 134: Performance tuning   a quick intoduction

134

Use case

  Always try to identify why the optimizer is not choosing an optimal plan.

  In a practical world, it is a necessary evil.

  You can even create profiles in development, and copy them to production.

OraInternals June 2010 Riyaj Shamsudeen

Page 135: Performance tuning   a quick intoduction

135

An example..

OraInternals June 2010 Riyaj Shamsudeen

select count(*) from bigtable bg, bmtest bt where bg.n1 = bt.n1 and bg.n1 <100 ; COUNT(*) ---------- 891

1 row selected.

select * from table (dbms_xplan.display_cursor ('','','ALLSTATS LAST'));

PLAN_TABLE_OUTPUT --------------------------------- SQL_ID 5jqxmjq15x5d8, child number 0 ------------------------------------- select count(*) from bigtable bg, bmtest bt where bg.n1 = bt.n1 and bg.n1 <100

Plan hash value: 3781779160

Contd..

We will use this SQL to show how execution plans can be tuned without any code change..

Page 136: Performance tuning   a quick intoduction

136

Current plan..

OraInternals June 2010 Riyaj Shamsudeen

Contd... -------------------------------------------------------------------------------

| Id | Operation | Name | E-Rows | OMem | 1Mem | Used-Mem | -------------------------------------------------------------------------------

| 0 | SELECT STATEMENT | | | | | | | 1 | SORT AGGREGATE | | 1 | | | | |* 2 | HASH JOIN | | 5 | 1517K| 1517K| 1505K (0)|

|* 3 | TABLE ACCESS FULL| BMTEST | 5 | | | | |* 4 | INDEX RANGE SCAN | BIGTABLE_N1 | 891 | | | |

-------------------------------------------------------------------------------

Predicate Information (identified by operation id):

---------------------------------------------------

2 - access("BG"."N1"="BT"."N1") 3 - filter("BT"."N1"<100) 4 - access("BG"."N1"<100)

Note

----- - dynamic sampling used for this statement (level=2) - Warning: basic plan statistics not available. These are only collected when:

Current execution plan uses Hash join.

Page 137: Performance tuning   a quick intoduction

137

Tuning with a hint..

OraInternals June 2010 Riyaj Shamsudeen

select /*+ leading (bt) use_nl (bg) */ count(*) from bigtable bg, bmtest bt where bg.n1 = bt.n1 and bg.n1 <100

/

COUNT(*) ---------- 891

To create a profile, it is easier to swap the profiles, if the execution plan is in memory.

select * from table (dbms_xplan.display_cursor ('','','ALLSTATS LAST'));

SQL_ID guxnam2t4px2k, child number 0 -------------------------------------

select /*+ leading (bt) use_nl (bg) */ count(*) from bigtable bg, bmtest bt where bg.n1 = bt.n1 and bg.n1 <100

Plan hash value: 497639897

We will use hints to modify the execution plan for this statement.

Tuned SQL_ID

Page 138: Performance tuning   a quick intoduction

138

Join : NL

OraInternals June 2010 Riyaj Shamsudeen

----------------------------------------------------

| Id | Operation | Name | E-Rows | ----------------------------------------------------

| 0 | SELECT STATEMENT | | | | 1 | SORT AGGREGATE | | 1 | | 2 | NESTED LOOPS | | 5 |

|* 3 | TABLE ACCESS FULL| BMTEST | 5 | |* 4 | INDEX RANGE SCAN | BIGTABLE_N1 | 1 |

----------------------------------------------------

Predicate Information (identified by operation id):

---------------------------------------------------

3 - filter("BT"."N1"<100) 4 - access("BG"."N1"="BT"."N1") filter("BG"."N1"<100)

Note

----- - dynamic sampling used for this statement (level=2)

Modified execution plan uses Nested loops join, as we tuned..

Page 139: Performance tuning   a quick intoduction

139

Script

OraInternals June 2010 Riyaj Shamsudeen

@profile_from_cache

Enter value for tuned_sql_id: guxnam2t4px2k

Enter value for tuned_child: 0 Enter value for orig_sql_id: 5jqxmjq15x5d8 Enter value for orig_child: 0

Enter value for profile_name: PROF_TEST_01

PL/SQL procedure successfully completed.

We will use a script to swap the hints to create a new profile PROF_TEST_01

Page 140: Performance tuning   a quick intoduction

140

Plan tuned

OraInternals June 2010 Riyaj Shamsudeen

select count(*) from bigtable bg, bmtest bt where bg.n1 = bt.n1 and bg.n1 <100;

select * from table (dbms_xplan.display_cursor ('','','ALLSTATS LAST'));

SQL_ID 4fcma6crxj305, child number 0 -------------------------------------

select count(*) from bigtable bg, bmtest bt where bg.n1 = bt.n1 and bg.n1 <100

---------------------------------------------------- | Id | Operation | Name | E-Rows | ----------------------------------------------------

| 0 | SELECT STATEMENT | | | | 1 | SORT AGGREGATE | | 1 |

| 2 | NESTED LOOPS | | 6818 | |* 3 | TABLE ACCESS FULL| BMTEST | 2026 | |* 4 | INDEX RANGE SCAN | BIGTABLE_N1 | 3 |

----------------------------------------------------

Note ----- - SQL profile PROF_TEST_01 used for this statement

After swapping the profile, execution plan is different. Notice the profile name printed.

Page 141: Performance tuning   a quick intoduction

141

Few key points

  Script profile_from_cache uses dbms_sqltune.import_sql_profile procedure to swap execution plans.

  This method works even with bind variables, since the script uses force_match=>true while calling import_sql_profile procedure.

  Only next hard parse of the statement will use the profile, in some cases, you might have to purge the cursor.

Use: purge_cursor.sql script to purge a cursor.

OraInternals June 2010 Riyaj Shamsudeen

Page 142: Performance tuning   a quick intoduction

142

Dba_sql_profiles

  Sql profiles can be seen in dba_sql_profiles view: select name, status, force_matching from dba_sql_profiles;

NAME STATUS FOR

------------------------------ -------- ---

PROF_TEST_01 ENABLED YES

  V$sql and v$sqlarea has a column sql_profile that shows whether the cursor using profile or not.

  If that column value is null, then that cursor is not using a profile.

OraInternals June 2010 Riyaj Shamsudeen

Page 143: Performance tuning   a quick intoduction

143

outline

  Hints from the profile can be fetched passing outline as a parameter.

select count(*) from bigtable bg, bmtest bt where bg.n1 = bt.n1 and bg.n1 < :v_n1;

select * from table (dbms_xplan.display_cursor ('','','outline' ));

Outline Data

------------- /*+ BEGIN_OUTLINE_DATA

IGNORE_OPTIM_EMBEDDED_HINTS OPTIMIZER_FEATURES_ENABLE('11.2.0.1')

DB_VERSION('11.2.0.1') ALL_ROWS OUTLINE_LEAF(@"SEL$1")

FULL(@"SEL$1" "BT"@"SEL$1") INDEX(@"SEL$1" "BG"@"SEL$1" ("BIGTABLE"."N1"))

LEADING(@"SEL$1" "BT"@"SEL$1" "BG"@"SEL$1") USE_HASH(@"SEL$1" "BG"@"SEL$1") END_OUTLINE_DATA

*/

OraInternals June 2010 Riyaj Shamsudeen

Page 144: Performance tuning   a quick intoduction

144

Bind capture

  It is also important to use exact SQL statement if possible to avoid surprises.

  For example, use bind variables as in this statement. Variable v_n1 number

select count(*) from bigtable bg, bmtest bt where bg.n1 = bt.n1 and bg.n1 < :v_n1;

  Bind values can be fetched from v$sql_bind_capture.

OraInternals June 2010 Riyaj Shamsudeen

select child_number, position, datatype, value_string, last_captured from v$sql_bind_capture where sql_id='9gkpy76q1gp89’

CHILD_NUMBER POSITION DATATYPE VALUE_STRING LAST_CAPT ------------ ---------- ---------- ---------------------------------------- --------- 1 1 2 100 18-NOV-10

0 1 2 100 18-NOV-10

Page 145: Performance tuning   a quick intoduction

©OraInternals Riyaj Shamsudeen 145

When conventional tools fail..

Page 146: Performance tuning   a quick intoduction

146

Database centric..

  When database centric tools fails, Operating system need to be reviewed in-depth.

  Following few slides introduces few tools.

  Every platform has its own tools and so, it is not possible to cover all Operating systems in this presentation.

OraInternals June 2010 Riyaj Shamsudeen

Page 147: Performance tuning   a quick intoduction

vmstat

  vmstat provides a concise view of server health. vmstat 1 5

kthr memory page disk faults cpu

r b w swap free re mf pi po fr de sr m0 m1 m4 m5 in sy cs us sy id

3 3 0 265724888 296150632 279 2870 279 8 6 0 0 23 4 21 12 9004 66456 38958 9 8 83

2 4 0 260794488 291328496 18 925 0 0 0 0 0 0 0 6 0 9739 74972 49516 11 29 59

0 3 0 260793680 291328248 94 520 0 0 0 0 0 0 0 0 0 9328 83034 47627 10 34 56

323 6 0 260793264 291328176 0 10 0 0 0 0 0 0 0 0 0 10829 61895 50706 11 32 57

251 7 0 260794440 291330168 71 1175 0 0 0 0 0 0 0 0 0 11345 71646 55438 10 23 67

Runnable processes Excceeds 300

Not much swapping Or paging

30% cpu usage in kernel or Sys mode. That’s 9 CPUs in Kernel mode usage.

Page 148: Performance tuning   a quick intoduction

Mpstat 1 5   mpstat provides per processor level statistics

CPU minf mjf xcal intr ithr csw icsw migr smtx srw syscl usr sys wt idl

33 9 0 11 80 2 933 38 135 501 0 1614 12 35 17 37 34 12 0 6 66 2 1246 24 123 549 0 1212 7 33 16 44 35 153 0 16 77 2 927 31 117 421 0 1529 11 29 12 48

36 62 0 7 70 2 544 26 110 280 0 1143 13 27 9 51 37 419 0 127 83 2 664 35 103 339 0 1348 24 29 12 36

38 13 0 645 2170 2107 588 39 93 2456 0 440 6 47 4 43 39 267 0 389 58 2 735 21 98 388 0 1038 17 33 18 32 64 133 0 6 66 2 421 32 74 207 0 10888 45 31 0 24

65 185 0 342 65 2 726 29 103 346 0 9341 11 31 6 53 66 5 0 6 46 2 673 12 88 313 0 494 5 31 5 60

67 23 0 344 1087 1016 437 39 90 425 0 1131 9 38 2 51 68 2 0 9190 53 2 421 15 116 282 0 517 7 32 2 60 69 431 0 6 55 2 569 20 107 275 0 1227 29 31 1 39

.. 134 0 0 5 81 1 534 38 96 312 0 3165 14 25 0 61

135 0 0 280 1087 1030 428 25 85 424 0 918 14 39 2 44 160 385 0 291 97 1 1244 34 193 571 0 9384 12 30 0 58 161 0 0 862 55 1 1131 7 138 590 0 683 4 31 1 64

162 6 0 8 61 1 1125 12 117 557 0 1977 6 28 0 66 163 2 0 4 66 1 719 20 112 495 0 1709 6 32 1 62

164 119 0 12 78 1 647 32 106 348 0 732 9 27 2 62 165 5 0 969 74 1 869 29 136 400 0 684 4 32 1 63

Many CPUs are in Kernel mode.

This is not an I/O Issue, as wt for I/O is not high.

Page 149: Performance tuning   a quick intoduction

sar queue size

sar -q 1 5

15:23:54 runq-sz %runocc swpq-sz %swpocc 15:23:55 267.0 81 0.0 0 15:23:56 154.0 93 0.0 0 15:23:57 0.0 0 0.0 0 15:23:58 1.0 78 0.0 0 15:23:59 0.0 0 0.0 0

Average 140.7 52 0.0 0

Sar confirms observations from Vmstat output.

Page 150: Performance tuning   a quick intoduction

Observations

  CPU run queue jumps to 100s intermittently.

  CPUs are used in kernel mode- over 30% kernel mode cpu usage.

  No observable abnormal swapping or paging activity

  No observable I/O issues.

Is that bad? Not necessarily, but needs further review.

Page 151: Performance tuning   a quick intoduction

prstat -mL PID USERNAME USR SYS TRP TFL DFL LCK SLP LAT VCX ICX SCL SIG PROCESS/LWPID 13893 oracle 84 0.5 0.1 0.0 0.0 0.0 2.1 14 39 180 3K 0 oracle/1 15309 oracle 54 18 0.0 0.0 0.0 0.0 29 0.0 416 271 23K 0 oracle/1 19096 oracle 69 3.5 0.1 0.0 0.0 0.0 14 13 742 178 2K 0 oracle/1

28759 oracle 69 0.4 0.0 0.0 0.0 0.0 18 12 104 119 831 0 oracle/1 15328 oracle 39 27 0.0 0.0 0.0 0.0 34 0.0 615 233 20K 0 oracle/1

15296 oracle 47 19 0.0 0.0 0.0 0.0 34 0.0 617 129 38K 0 oracle/1 15331 oracle 31 34 0.0 0.0 0.0 0.0 38 0.0 344 172 21K 0 oracle/1 27950 oracle 59 3.4 0.0 0.0 0.0 0.0 25 13 636 148 2K 0 oracle/1

15332 oracle 17 42 0.0 0.0 0.0 0.0 42 0.0 13K 376 75K 0 ps/1 20285 oracle 53 4.1 0.1 0.0 0.0 0.0 30 13 957 178 8K 0 oracle/1

11155 oracle 42 11 0.0 0.0 0.0 0.0 34 13 1K 106 16K 0 oracle/1 15299 oracle 28 15 0.0 0.0 0.0 0.0 58 0.0 259 42 13K 0 oracle/1 28250 oracle 21 21 0.0 0.0 0.0 0.0 43 15 5K 178 43K 0 oracle/1

24136 oracle 33 5.1 0.2 0.0 0.0 0.0 48 13 1K 133 4K 0 oracle/1 15292 oracle 26 11 0.0 0.0 0.0 0.0 64 0.0 402 67 14K 0 oracle/1

13306 oracle 30 6.5 0.0 0.0 0.0 0.0 53 11 1K 126 16K 0 oracle/1 27150 oracle 22 7.9 0.0 0.0 0.0 0.0 57 13 2K 172 11K 0 oracle/1 ...

27614 oracle 6.1 4.5 0.0 0.0 0.0 0.0 79 11 891 60 2K 0 oracle/1 28432 oracle 4.2 5.6 0.0 0.0 0.0 0.0 87 3.3 26 12 108 0 oracle/1

28310 oracle 4.5 4.9 0.0 0.0 0.0 0.0 80 11 1K 57 5K 0 oracle/1 28328 oracle 4.3 4.9 0.0 0.0 0.0 0.0 82 9.0 867 74 4K 0 oracle/1 28264 oracle 4.1 4.2 0.0 0.0 0.0 0.0 83 8.9 851 93 4K 0 oracle/1

28248 oracle 4.1 4.1 0.1 0.0 0.0 0.0 86 5.4 63 45 6K 0 oracle/1 28343 oracle 4.0 4.0 0.0 0.0 0.0 0.0 87 4.6 27 46 120 0 oracle/1

28277 oracle 3.9 4.1 0.0 0.0 0.0 0.0 86 6.4 971 40 4K 0 oracle/1 28324 oracle 3.9 3.9 0.0 0.0 0.0 0.0 82 10 860 24 4K 0 oracle/1 28436 oracle 3.4 4.4 0.0 0.0 0.0 0.0 91 1.0 26 25 92 0 oracle/1

prstat provides process level statistics. Flag m is for microstate accounting

LAT column suggests that pid 13893 was waiting for CPU 14% of its time, during that observation Interval.

Page 152: Performance tuning   a quick intoduction

More prstat -mL PID USERNAME USR SYS TRP TFL DFL LCK SLP LAT VCX ICX SCL SIG PROCESS/LWPID 23131 oraprod 56 4.8 0.7 0.0 0.0 0.0 0.3 39 5 7K .1M 0 oracle/1

25566 oraprod 57 0.0 0.8 0.0 0.0 0.0 4.3 38 16 7K 13 0 oracle/1

24588 root 19 11 - - - - 0.0 - 1K 5K 9K 0 sendmail/1

18093 oraprod 17 9.8 0.3 0.0 0.0 0.0 43 30 2K 3K 7K 0 oracle/1

23495 oraprod 19 7.8 0.4 0.0 0.0 0.0 40 33 2K 4K 6K 0 oracle/1

23499 oraprod 18 7.8 0.3 0.0 0.0 0.0 45 29 2K 2K 7K 0 oracle/1

23483 oraprod 18 7.0 0.3 0.0 0.0 0.0 39 36 2K 2K 7K 0 oracle/1

23559 oraprod 18 7.5 0.3 0.0 0.0 0.0 39 35 2K 3K 7K 0 oracle/1

23557 oraprod 18 7.2 0.3 0.0 0.0 0.0 35 40 1K 3K 5K 0 oracle/1

23485 oraprod 18 6.9 0.4 0.0 0.0 0.0 40 35 1K 4K 6K 0 oracle/1

23561 oraprod 16 7.3 0.2 0.0 0.0 0.0 50 26 2K 2K 7K 0 oracle/1

23493 oraprod 16 6.9 0.3 0.0 0.0 0.0 38 39 2K 2K 5K 0 oracle/1

21665 oraprod 15 8.3 0.3 0.0 0.0 0.0 39 38 2K 3K 6K 0 oracle/1

796 oraprod 14 8.5 0.2 0.0 0.0 0.0 49 28 2K 2K 7K 0 oracle/1

1345 oraprod 14 8.3 0.2 0.0 0.0 0.0 46 31 2K 2K 7K 0 oracle/1

21667 oraprod 14 8.3 0.2 0.0 0.0 0.0 44 33 2K 2K 7K 0 oracle/1

23491 oraprod 16 6.9 0.4 0.0 0.0 0.0 39 38 1K 3K 5K 0 oracle/1

23481 oraprod 16 6.0 0.4 0.0 0.0 0.0 36 41 1K 4K 5K 0 oracle/1

23553 oraprod 15 6.7 0.3 0.0 0.0 0.0 41 37 2K 2K 6K 0 oracle/1

23563 oraprod 15 5.9 0.3 0.0 0.0 0.0 34 44 1K 2K 5K 0 oracle/1

23489 oraprod 15 5.7 0.4 0.0 0.0 0.0 33 46 1K 3K 5K 0 oracle/1

24824 oraprod 15 5.4 0.2 0.0 0.0 0.0 71 8.0 950 3K 5K 0 oracle/1

807 oraprod 13 7.3 0.3 0.0 0.0 0.0 33 46 1K 3K 5K 0 oracle/1

18097 oraprod 13 7.6 0.3 0.0 0.0 0.0 38 42 2K 3K 5K 0 oracle/1

Page 153: Performance tuning   a quick intoduction

Truss

Description: The truss utility traces the system calls and the signal process receives.

Options:

truss [-fcaeildD] [ - [tTvx] [!] syscall ,...] [ - [sS] [!] signal ,...] [ - [mM] [!] fault ,...] [ - [rw] [!] fd ,...] [ - [uU] [!] lib ,... : [:] [!] func ,...] [-o outfile] com- mand | -p pid...

Solaris – truss Hpux- tusc (download) Linux – strace

Page 154: Performance tuning   a quick intoduction

Truss – Word of caution

At every system call, truss inspects the process. This *potentially* could slow down the process.

So, Truss critical processes, only when it is necessary to do so.

Page 155: Performance tuning   a quick intoduction

Truss - Example truss –p 28393

llseek(18, 0, SEEK_CUR) = 0x012EF9A4 fstat(18, 0xFFBFA058) = 0 write(18, " 9 8 7 1 0 o b j\n <".., 21) = 21 fstat(18, 0xFFBFA058) = 0 write(18, " 9 8 7 2 0 R > >\n s".., 18) = 18 fstat(18, 0xFFBFA0C0) = 0 write(18, " q\n 0 . 0 0 1 4 3 . 7".., 5700) = 5700 fstat(18, 0xFFBFA100) = 0 write(18, " e n d s t r e a m\n e n".., 17) = 17 fstat(18, 0xFFBFA100) = 0 write(18, " 9 8 7 2 0 o b j\n 5".., 23) = 23 fstat(18, 0xFFBFA100) = 0 lseek(17, 0x0216B000, SEEK_SET) = 0x0216B000 write(17, "C8021686 )\0\0 )D0\0\0\0".., 4096) = 4096 lseek(17, 0x0219D000, SEEK_SET) = 0x0219D000 read(17, "\0\0\0\00101\001FF \0\0".., 4096) = 4096 lseek(17, 0x0219E000, SEEK_SET) = 0x0219E000 read(17, "D3\007\0\015CC\0\0\0 qB0".., 4096) = 4096 lseek(17, 0x0216D000, SEEK_SET) = 0x0216D000 write(17, "\0 \b\0\0\0 qAA18 L O S".., 4096) = 4096 lseek(17, 0x0219F000, SEEK_SET) = 0x0219F000 read(17, "\0\0\0\0\0\0\0\00101\001".., 4096) = 4096 write(18, " 9 8 7 0 0 o b j\n <".., 189) = 189 fstat(18, 0xFFBFA058) = 0 llseek(18, 0, SEEK_CUR) = 0x012F10F4 fstat(18, 0xFFBFA058) = 0 write(18, " 9 8 7 4 0 o b j\n <".., 21) = 21 fstat(18, 0xFFBFA058) = 0 write(18, " 9 8 7 5 0 R > >\n s".., 18) = 18 fstat(18, 0xFFBFA0C0) = 0 write(18, " q\n 0 . 0 0 1 4 3 . 7".., 5736) = 5736 fstat(18, 0xFFBFA100) = 0 write(18, " e n d s t r e a m\n e n".., 17) = 17 fstat(18, 0xFFBFA100) = 0

Process seemingly calling Many seek, write and fstat calls

For, seek, fstat, write, read calls etc, first argument is the file descriptor.

For read, write call second argument is the buffer itself.

Page 156: Performance tuning   a quick intoduction

Truss

To trace a process and print minimal information truss –p <pid> Example: truss –p 23898

To trace a process, follow its children and print minimal information truss –f –p <pid> Example: truss –f –p 23898

To trace a process, print timestamp and print minimal information truss –d –p <pid> Example: truss –d –p 23898

To trace a process, send output to a file and print minimal information. truss –o /tmp/truss.out –p <pid> Example: truss –o /tmp/truss.out –d –p 23898

Page 157: Performance tuning   a quick intoduction

Truss – Few outputs truss -d -o /tmp/truss.out -p 484 cat /tmp/truss.out: Baase time stamp: 1188872874.8745 [ Mon Sep 3 22:27:54 EDT 2007 ] 0.5942 semtimedop(3735584, 0xFFFFFFFF7FFFDFCC, 1, 0xFFFFFFFF7FFFDFB8) Err#11 EAGAIN 0.5949 ioctl(8, (('7'<<8)|72), 0xFFFFFFFF7FFF87F8) = 192

0.5950 ioctl(8, (('7'<<8)|63), 0x1038AA738) = 0 0.5958 semtimedop(3735584, 0xFFFFFFFF7FFFC26C, 1, 0xFFFFFFFF7FFFC258) = 0 0.5998 ioctl(10, (('V'<<24)|('X'<<16)|('O'<<8)|28), 0xFFFFFFFF7FFFD838) = 0 0.6025 ioctl(10, (('V'<<24)|('X'<<16)|('O'<<8)|28), 0xFFFFFFFF7FFFD838) = 0 0.6047 ioctl(10, (('V'<<24)|('X'<<16)|('O'<<8)|28), 0xFFFFFFFF7FFFD838) = 0

0.6054 ioctl(10, (('V'<<24)|('X'<<16)|('O'<<8)|28), 0xFFFFFFFF7FFFDA48) = 0 0.6059 ioctl(10, (('V'<<24)|('X'<<16)|('O'<<8)|28), 0xFFFFFFFF7FFFD9C8) = 0 0.6064 ioctl(10, (('V'<<24)|('X'<<16)|('O'<<8)|28), 0xFFFFFFFF7FFFD858) = 0 0.6076 ioctl(10, (('V'<<24)|('X'<<16)|('O'<<8)|28), 0xFFFFFFFF7FFFD808) = 0 0.6089 ioctl(10, (('V'<<24)|('X'<<16)|('O'<<8)|28), 0xFFFFFFFF7FFFD8B8) = 0

1.2775 semtimedop(3735584, 0xFFFFFFFF7FFFDFCC, 1, 0xFFFFFFFF7FFFDFB8) = 0 1.2780 ioctl(10, (('V'<<24)|('X'<<16)|('O'<<8)|28), 0xFFFFFFFF7FFF7BF8) = 0 1.2782 ioctl(8, (('7'<<8)|72), 0xFFFFFFFF7FFFA4B8) = 160 1.2783 ioctl(8, (('7'<<8)|63), 0x1038AA738) = 0 1.2785 semtimedop(3735584, 0xFFFFFFFF7FFFD3FC, 1, 0xFFFFFFFF7FFFD3E8) = 0

1.2794 semtimedop(3735584, 0xFFFFFFFF7FFFD3FC, 1, 0xFFFFFFFF7FFFD3E8) = 0 1.2795 ioctl(10, (('V'<<24)|('X'<<16)|('O'<<8)|28), 0xFFFFFFFF7FFFBE08) = 0 1.2797 ioctl(10, (('V'<<24)|('X'<<16)|('O'<<8)|28), 0xFFFFFFFF7FFFBE08) = 0

Time stamp displacement From base timestamp. Seconds.fraction of sec

Page 158: Performance tuning   a quick intoduction

Truss again

  Truss of that process, with –c flag truss -c -p 13893

syscall seconds calls errors write .001 20 times .326 9360 semctl .000 2 ioctl .015 48 -------- ------ ---- sys totals: .344 9430 0 usr time: 11.182 elapsed: 15.670

For this process, during this observation period, 11.182 second spent in user mode, 0.344 seconds in kernel mode. What happened to other 4.146 seconds ?

Page 159: Performance tuning   a quick intoduction

PROC tools

Most Operating systems provide PROC tools to review process’ run time attributes.

Page 160: Performance tuning   a quick intoduction

pstack   pstack shows current stack of the process. Let’s look at pstack for this java process: pstack 16858 |more

16858: java TestCase

----------------- lwp# 1 / thread# 1 --------------------

ff31dfdc poll (ffbfeb38, 0, fa0)

ff37e6a8 select (0, 0, 0, 0, ffbfec08, ffbfec08) + 6c

fecf0a20 __1cIos_sleep6Fxi_i_ (0, fa0, 1, 0, 4, 0) + 1f8

fecf07ec __1cCosFsleep6FpnGThread_xi_i_ (36b70, 0, fa0, 1, 10, 0) + 21c

fed6da4c JVM_Sleep (36c04, ffbfed60, 0, fa0, 10, 0) + 27c

f9c0be48 ???????? (0, b8, 5, 8, 0, ffbfed78)

f9c05c64 ???????? (ffbfee78, 0, 0, f9c155e0, 3349f0, ffbfee18)

f9c00118 ???????? (ffbfef00, ffbff0e0, a, f58fa768, f9c0aae0, ffbfefec)

fecc84a8 __1cJJavaCallsLcall_helper6FpnJJavaValue_pnMmethodHandle_pnRJavaCallArguments_pnGTh

read__v_ (ffbff0d8, ffbfefb4, ffbfefe4, 36b70, 36b 70, 0) + 274

fecdc674 _1cRjni_invoke_static6FpnHJNIEnv__pnJJavaValue_pnI_jobject_nLJNICallType_pnK_jmetho

dID_pnSJNI_ArgumentPusher_pnGThread__v_ (36c04, f

fbff0d8, 0, 0, fbd90, ffbff0bc) + 218

fed660ec jni_CallStaticVoidMethod (36c04, 36dc8, fbd90, 36dd8, 36c04, ff0000) + 13c

00012ea4 main (36dac, 260, 0, fbd90, 488, 268) + 158c

000118f0 _start (0, 0, 0, 0, 0, 0) + 108

Thread #1 is sleeping.

Page 161: Performance tuning   a quick intoduction

pmap <pid> Address Kbytes RSS Anon Locked Mode Mapped File 00010000 72 72 - - r-x-- java 00030000 16 16 16 - rwx-- java 00034000 8744 8680 8680 - rwx-- [ heap ] 77980000 1224 1048 - - r--s- dev:273,2000 ino:104403 77CFA000 24 24 24 - rw--R [ anon ] 77F7A000 24 24 24 - rw--R [ anon ] 78000000 72 72 72 - rwx-- [ anon ] 7814C000 144 144 144 - rwx-- [ anon ] 783E8000 32 32 32 - rwx-- [ anon ] 78408000 8 8 8 - rwx-- [ anon ] 78480000 752 464 - - r--s- dev:85,0 ino:13789 7877E000 8 8 8 - rw--R [ anon ] 78800000 36864 8192 8192 - rwx-- [ anon ] …… FF25C000 16 8 8 - rwx-- libCrun.so.1 FF276000 8 8 - - rwxs- [ anon ] FF280000 688 688 - - r-x-- libc.so.1 FF33C000 32 32 32 - rwx-- libc.so.1 FF350000 16 16 16 - rw--- [ anon ] FF360000 8 8 8 - rwx-- [ anon ] FF370000 96 96 - - r-x-- libthread.so.1 FF398000 8 8 8 - rwx-- libthread.so.1 FF39A000 8 8 8 - rwx-- libthread.so.1 FF3A0000 8 8 - - r-x-- libc_psr.so.1 FF3B0000 184 184 - - r-x-- ld.so.1 FF3EE000 8 8 8 - rwx-- ld.so.1 FF3F0000 8 8 8 - rwx-- ld.so.1 FF3FA000 8 8 8 - rwx-- libdl.so.1 FFB80000 24 - - - ----- [ anon ] FFBF0000 64 64 64 - rw--- [ stack ] -------- ------- ------- ------- ------- total Kb 182352 65568 26360 -

Pmap prints a Nice memory map of the Process. Verious heaps and Stacks are printed here

Total memory foot print Also printed.

Page 162: Performance tuning   a quick intoduction

pfiles pfiles 28393 28393: ar60runb P_CONC_REQUEST_ID=2452107 STARTDATE='012006'

ENDDATE='122006' Current rlimit: 4096 file descriptors 0: S_IFIFO mode:0000 dev:272,0 ino:7325504 uid:11175 gid:100 size:0 O_RDWR 1: S_IFREG mode:0644 dev:233,63004 ino:895220 uid:11175 gid:100 size:0 O_WRONLY|O_APPEND|O_CREAT 2: S_IFREG mode:0644 dev:233,63004 ino:895220 uid:11175 gid:100 size:0 O_WRONLY|O_APPEND|O_CREAT ... 17: S_IFREG mode:0644 dev:233,63004 ino:895242 uid:11175 gid:100 size:

102522880 O_RDWR|O_CREAT|O_TRUNC 18: S_IFREG mode:0644 dev:233,63004 ino:895305 uid:11175 gid:100 size:

25491841 O_RDWR|O_CREAT|O_TRUNC

This is the file_id In the truss output

This is the device id Of the form minor,major

Inode number

Using these device numbers and Inode numbers, file names can be mapped.