Friday, November 23, 2012

SQL Profiling/Optimization in Oracle


To tune your SQL you need to profile/measure your sql performance (time, how many reads) and inspect the
execution plan generated by optimizer (e.g. to detect full table scan that can be avoided by creating index). Start with measuring the baseline and then try to modify your table/sql (e.g. creating index, give optimizer hints) and measure again.

There are several ways to measure your sql performace in Oracle:

1. The simplest way: click "auto trace" or "explain plan" in the SQL worksheet in  JDeveloper/SQLDeveloper:


To use the autotrace you need to have permission to the dictionary view V$mystat


2. In SQLplus you can use explain plan:

EXPLAIN PLAN FOR select e.employee_id,e.last_name,j.job_title FROM employees e, jobs j WHERE salary < 4000 and j.job_id = e.job_id;

Explained.

SQL>  SELECT * FROM TABLE (DBMS_XPLAN.display);

PLAN_TABLE_OUTPUT

| Id  | Operation            | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------
|   0 | SELECT STATEMENT          |            |    98 |  4508 |     6  (17)| 00:00:01 |
|   1 |  MERGE JOIN               |            |    98 |  4508 |     6  (17)| 00:00:01 |
|   2 |   TABLE ACCESS BY INDEX ROWID| JOBS    |    19 |   475 |     2   (0)| 00:00:01 |
|   3 |    INDEX FULL SCAN        | JOB_ID_PK |    19 |   |     1   (0)| 00:00:01 
|*  4 |   SORT JOIN               |            |    98 |  2058 |     4  (25)| 00:00:01 |
|*  5 |    TABLE ACCESS FULL      | EMPLOYEES |    98 |  2058 |     3   (0)| 00:00:01 |


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

   4 - access("J"."JOB_ID"="E"."JOB_ID")
       filter("J"."JOB_ID"="E"."JOB_ID")
   5 - filter("SALARY"<4000>

19 rows selected.

3. In SQLplus you can use autotrace so Oracle will display the explain plan or statistics everytime you perform a query.


To display the plan: SET AUTOTRACE TRACEONLY EXPLAIN or SET AUTOTRACE ON EXPLAIN;
To display the statistics: SET AUTOTRACE TRACEONLY STATISTICS or SET AUTOTRACE ON  STATISTICS;
Or display both: SET AUTOTRACE TRACEONLY STATISTICS EXPLAIN;


Interesting statistics information are included:

  • recursive calls: how much works to construct query plan
  • consistent gets / logical reads: number of logical read operation when Oracle constructing query plan and execute it)
  • physical reads: number of disk read operations when Oracle can't find the data in the cache.
e.g.

Statistics
----------------------------------------------------------
       1097  recursive calls
        0  db block gets
      282  consistent gets
       24  physical reads
        0  redo size
       2434  bytes sent via SQL*Net to client
      464  bytes received via SQL*Net from client
        6  SQL*Net roundtrips to/from client
       13  sorts (memory)
        0  sorts (disk)


If you run the same query again, the performance is better due to caching:
Statistics
----------------------------------------------------------
       10  recursive calls
        0  db block gets
       17  consistent gets
        0  physical reads
        0  redo size
       2434  bytes sent via SQL*Net to client
      464  bytes received via SQL*Net from client
        6  SQL*Net roundtrips to/from client
        1  sorts (memory)
        0  sorts (disk)
       64  rows processed

To get accurate measure over your query you might need to flush the cache (execution plan & data) before you perform the sql again:
ALTER SYSTEM FLUSH SHARED_POOL;
ALTER SYSTEM FLUSH BUFFER_CACHE;


As an experiment we can see how big the infuence of indexing to the query performance. For example try to hide an index from the optimizer:
ALTER INDEX hr.job_id_pk INVISIBLE
or using hint:
SELECT /*+ NO_INDEX (j job_id_pk) */  e.employee_id, e.last_name, j.job_title FROM employees e, jobs j WHERE salary < 4000 and j.job_id = e.job_id;

The performance will be worse due to table full scan:

Execution Plan
----------------------------------------------------------
Plan hash value: 1300016118

--------------------------------------------------------------------------------
| Id  | Operation    | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |        |    98 |  4508 |     7     (15)| 00:00:01 |
|*  1 |  HASH JOIN         |         |    98 |  4508 |     7     (15)| 00:00:01 |
|   2 |   TABLE ACCESS FULL| JOBS      |    19 |   475 |     3   (0)| 00:00:01 |
|*  3 |   TABLE ACCESS FULL| EMPLOYEES |    98 |  2058 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------------

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

   4 - access("J"."JOB_ID"="E"."JOB_ID")
       filter("J"."JOB_ID"="E"."JOB_ID")
   5 - filter("SALARY"<4000>

19 rows selected.

Statistics
----------------------------------------------------------
       1240  recursive calls
        2  db block gets
      317  consistent gets
       38  physical reads
        0  redo size
       2434  bytes sent via SQL*Net to client
      464  bytes received via SQL*Net from client
        6  SQL*Net roundtrips to/from client
       12  sorts (memory)
        0  sorts (disk)
       64  rows processed


4. You can use DBMS_SYSTEM.set_sql_trace_in_session to tell Oracle to generate *.trc trace files and then use tkprof utility to summary the result. Personally I find this method unhandy except when I need the trace files.

Tips:

1. When using autotrace, Oracle might complain that you don't have PLUSTRACE role privelege.
Solution:
connect sys as sysdba, run @$ORACLE_HOME/sqlplus/admin/plustrce.sql to perform several grants. Then Grant PLUSTRACE to the user you used. Connect again as a normal user (e.g. hr) create PLAN_TABLE in the schema you use by using  @$ORACLE_HOME/rdbms/admin/utlxplan

2. When your query produces big result and you're interested only to the profiling statistics, use TRACEONLY so that Oracle will not display the query result.
e.g.
SET AUTOTRACE TRACEONLY STATISTICS
SET AUTOTRACE TRACEONLY EXPLAIN

3. You can set the statistics level e.g. ALTER SESSION SET statistics_level=ALL;

Source: Steve's blogs http://soa-java.blogspot.com/

Any comments are welcome :)




References:
Beginning Oracle Database 11g Administration by Iggy Fernandez



Oracle Database 11g Release 2 Performance Tuning Tips & Techniques by Niemiec

No comments: