Oracle DBA – Tips and Techniques

Knowledge is Power – so share it ….

Archive for the ‘Performance Tuning’ Category

11g Optimizer Plan Stabilty using SQL Plan Baselines

Posted by Gavin Soorma on July 30, 2009

In Oracle 11g, we can ensure that the same proven execution plans are used by the Optimizer regardless of any change which can effect the optimizer like a version change, index drop or recreate, init.ora parameter change etc.
Once plans are marked as enabled and accepted, the optimizer will not use any new plans unless there is proven performance benefits of adopting a new plan. Plans can also be manually adopted or “evolved”.

One of the dangers of upgrading from 10g to 11g (or between any release) is that execution plans may change in the new version which could affect application performance.

But with this new 11g feature, if we have tried and trusted 10g execution plans in place, we can ensure that the same plans will be used by the optimizer even after the migration unless new 11g plans are found to offer better performance. This can be achieved by capturing 10g plans in SQL tuning sets (STS) and exporting those to the 11g database.

Let us demonstrate the same with a small example using the SALES table in the SH schema.

We need to enable the use of SQL Plan management by setting the parameter optimizer_capture_sql_plan_baselines to TRUE. The default value is FALSE.

Initially the OPTIMIZER_MODE is set to FIRST_ROWS which should optimize the statement for best response time. So in this case the index on the SALES table is used.

SQL> ALTER SESSION SET optimizer_mode='FIRST_ROWS';

Session altered.

SQL> EXPLAIN PLAN FOR
  2  SELECT * FROM SALES
  3  WHERE QUANTITY_SOLD > 40 ORDER BY PROD_ID;

Explained.

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

| Id  | Operation                           | Name            | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
---------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                    |                 |    29 |  5063   (2)| 00:01:01 |       |       |
|   1 |  SORT ORDER BY                      |                 |    29 |  5063   (2)| 00:01:01 |       |       |
|   2 |   PARTITION RANGE ALL               |                 |    29 |  5062   (2)| 00:01:01 |     1 |    28 |
|*  3 |    TABLE ACCESS BY LOCAL INDEX ROWID| SALES           |    29 |  5062   (2)| 00:01:01 |     1 |    28 |
|   4 |     BITMAP CONVERSION TO ROWIDS     |                 |            |          |       |       |
|   5 |      BITMAP INDEX FULL SCAN         | SALES_PROMO_BIX |       |            |          |     1 |    28 |
---------------------------------------------------------------------------------------------------------------

if we change the OPTIMIZER_MODE to ALL_ROWS, we now see from the explain plan that a FULL TABLE SCAN is being used as the statement is optimized for best throughput.

SQL> ALTER SESSION SET optimizer_mode='ALL_ROWS';

Session altered.

SQL> EXPLAIN PLAN FOR
  2  SELECT * FROM SALES
  3  WHERE QUANTITY_SOLD > 40 ORDER BY PROD_ID;

Explained.

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

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------
Plan hash value: 3147563666

----------------------------------------------------------------------------------------------
| Id  | Operation            | Name  | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |       |     1 |    29 |  1196   (3)| 00:00:15 |       |       |
|   1 |  SORT ORDER BY       |       |     1 |    29 |  1196   (3)| 00:00:15 |       |       |
|   2 |   PARTITION RANGE ALL|       |     1 |    29 |  1195   (3)| 00:00:15 |     1 |    28 |
|*  3 |    TABLE ACCESS FULL | SALES |     1 |    29 |  1195   (3)| 00:00:15 |     1 |    28 |
----------------------------------------------------------------------------------------------

We will run the query – initially with OPTIMIZER_MODE set to FIRST_ROWS and then the same query with OPTIMIZER_MODE set to ALL_ROWS. We will use a label ‘GAVIN’ in the SQL statement to help us easily identify it from the other SQL statements in the shared pool.

We explicitly flush the shared pool to force a hard parse the next time the same query is run but with the ALL_ROWS optimizer session setting.

SQL> ALTER SESSION SET optimizer_mode='FIRST_ROWS';

Session altered.

SQL>  SELECT /* GAVIN */
  2   *
  3   FROM SALES WHERE QUANTITY_SOLD > 40
  4  ORDER BY PROD_ID;

no rows selected

SQL> ALTER SYSTEM FLUSH SHARED_POOL;

System altered.

SQL> ALTER SESSION SET optimizer_mode='ALL_ROWS';

Session altered.

SQL>  SELECT /* GAVIN */
  2   *
  3   FROM SALES WHERE QUANTITY_SOLD > 40
  4  ORDER BY PROD_ID;

no rows selected

We will notice that the second execution of the query has encountered a change in the execution plan. But it has not been ‘accepted as yet even though it has been ‘enabled’.

SQL> SELECT sql_handle, plan_name, enabled, accepted FROM dba_sql_plan_baselines
WHERE sql_text like '%GAVIN%';  2

                                                              Ena-
SQL_HANDLE                     PLAN_NAME                      bled Acpt
------------------------------ ------------------------------ ---- ----
SYS_SQL_6f3dcd88c7488035       SYS_SQL_PLAN_c748803511df68d0  YES  YES
SYS_SQL_6f3dcd88c7488035       SYS_SQL_PLAN_c748803554bc8843  YES  NO

We can use the EVOLVE_SQL_PLAN_BASELINE function to compare the performance between the two plans. We note that the second plan has not been automatically evolved or accepted because it does not pass the performance improvement criteria which has been laid down

SQL> SET LONG 10000
SQL> SELECT DBMS_SPM.evolve_sql_plan_baseline(sql_handle => 'SYS_SQL_6f3dcd88c7488035') FROM dual;

DBMS_SPM.EVOLVE_SQL_PLAN_BASELINE(SQL_HANDLE=>'SYS_SQL_6F3DCD88C7488035')
--------------------------------------------------------------------------------

-------------------------------------------------------------------------------
                        Evolve SQL Plan Baseline Report
-------------------------------------------------------------------------------

Inputs:
-------
  SQL_HANDLE = SYS_SQL_6f3dcd88c7488035
  PLAN_NAME  =
  TIME_LIMIT = DBMS_SPM.AUTO_LIMIT
  VERIFY     = YES
  COMMIT     = YES

Plan: SYS_SQL_PLAN_c748803554bc8843
-----------------------------------
  Plan was verified: Time used 1.457 seconds.
  Failed performance criterion: Compound improvement ratio <= 1.16.

                      Baseline Plan      Test Plan     Improv. Ratio
                      -------------      ---------     -------------
  Execution Status:        COMPLETE       COMPLETE
  Rows Processed:                 0              0
  Elapsed Time(ms):            1036            304              3.41
  CPU Time(ms):                1020            300               3.4
  Buffer Gets:                 1929           1727              1.12
  Disk Reads:                     0              0
  Direct Writes:                  0              0
  Fetches:                        0              0
  Executions:                     1              1

-------------------------------------------------------------------------------
                                 Report Summary
-------------------------------------------------------------------------------
Number of SQL plan baselines verified: 1.
Number of SQL plan baselines evolved: 0.

We can also manually evolve the plan if we ourselves feel that we know that the plan is a better one

SQL> var spm number;

SQL> exec :spm := dbms_spm.alter_sql_plan_baseline(sql_handle =>'SYS_SQL_6f3dcd88c7488035',
- plan_name =>'SYS_SQL_PLAN_c748803554bc8843', attribute_name => 'ACCEPTED',attribute_value => 'YES');

PL/SQL procedure successfully completed.

SQL> SELECT sql_handle, plan_name, enabled, accepted FROM dba_sql_plan_baselines
  2  WHERE sql_text like '%GAVIN%';

                                                              Ena-
SQL_HANDLE                     PLAN_NAME                      bled Acpt
------------------------------ ------------------------------ ---- ----
SYS_SQL_6f3dcd88c7488035       SYS_SQL_PLAN_c748803511df68d0  YES  YES
SYS_SQL_6f3dcd88c7488035       SYS_SQL_PLAN_c748803554bc8843  YES  YES

Posted in Oracle 11g, Performance Tuning | Tagged: , , , , | Leave a Comment »

Exporting and Importing AWR snapshot data

Posted by Gavin Soorma on July 25, 2009

The AWR tables contains a wealth of important performance data which can be very useful in performance tuning trend analysis and also when comparing performance between two seperate periods of time.

AWR data is stored in the WRH$ and DBA_HIST tables in the SYSAUX tablespace. There could be performance implications if these tables were to grow too large in size or if the retention was increased beyond the default of 7 days.

A good solution is to have a central repository and move statistical AWR data periodically to this central repository database using the Oracle supplied awrextr.sql and awrload.sql scripts which can be found in the $ORACLE_HOME/rdbms/admin directory.

The AWR History is by default maintained for 7 days and the data is gathered in the AWR repository tables every hour by default.

The current snapshot retention settings and data gathering frequency can be determined by the query shown below. Note in this case the default settings of 7 days and 1 hour is displayed.

SQL> select to_char(snap_interval,'DD'),to_char(retention,'DD') FROM dba_hist_wr_control;

TO_CHAR(SNAP_INTER TO_CHAR(RETENTION,
------------------ ------------------
+00000 01:00:00.0  +00007 00:00:00.0;

The AWR default settings can be modified using the DBMS_WORKLOAD_REPOSITORY package as shown below. In this case the retention is being increased to 30 days and the interval to every 30 minutes.

BEGIN
DBMS_WORKLOAD_REPOSITORY.modify_snapshot_settings(
retention => 43200,
interval => 30);
END;
/
Extracting AWR data

Create a directory

SQL> CREATE DIRECTORY AWR_DATA AS
2 ‘/u01/oracle/’;

Directory created.

SQL> @?/rdbms/admin/awrextr.sql

The script will list the information we need to provide to it

AWR EXTRACT
~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~  This script will extract the AWR data for a range of snapshots  ~
~  into a dump file.  The script will prompt users for the         ~
~  following information:                                          ~
~     (1) database id                                              ~
~     (2) snapshot range to extract                                ~
~     (3) name of directory object                                 ~
~     (4) name of dump file                                        ~

After entering the range of snapshot ids, we will need to provide the directory location where the data pump export file will be located. We need to also enter the dumpfile name as well.

Note: the script will autiomatically append a ‘.dmp’ to the data punp export file name. So we need to just enter the dumpfile name without any extension.

Specify the Directory Name
~~~~~~~~~~~~~~~~~~~~~~~~~~

Directory Name                 Directory Path
------------------------------ -------------------------------------------------
ADMIN_DIR                      /u01/oracle/product/10.2.0/rmand/md/admin
AWR_DATA                       /u01/oracle/
DATA_PUMP_DIR                  /u01/oracle/product/10.2.0/rmand/admin/rmand/dpdu
                               mp/

ORACLE_OCM_CONFIG_DIR          /u01/oracle/product/10.2.0.4/rmand/ccr/state
WORK_DIR                       /u01/oracle/product/10.2.0/rmand/work

Choose a Directory Name from the above list (case-sensitive).

Enter value for directory_name: AWR_DATA

Using the dump directory: AWR_DATA

Specify the Name of the Extract Dump File
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The prefix for the default dump file name is awrdat_11369_11383.
To use this name, press  to continue, otherwise enter
an alternative.

Enter value for file_name: awrexp

After the export is complete, we will need to ftp the data pump dump file awrexp.dmp to the target server where our central repository database is located.

Loading AWR Data

On the repository database, we will create a directiory AWR_DATA as well and the ensure that the DIRECTORY_PATH corresponds to the directory where the awrexport.dmp file is located.

Also, a temporary staging schema AWR_STAGE is created. Objects are first imported into this staging schema and then inserted into the WR$ and DBA_HIST AWR historical tables.

We will need to provide information about the directory location, dump file and the staging schema name

@?/rdbms/admin/awrload.sql

AWR LOAD
~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~  This script will load the AWR data from a dump file. The   ~
~  script will prompt users for the following information:    ~
~     (1) name of directory object                            ~
~     (2) name of dump file                                   ~
~     (3) staging schema name to load AWR data into           ~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

While specifying the dumpfile name we need to only provide the file name without the .dmp extension

Specify the Name of the Dump File to Load
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Please specify the prefix of the dump file (.dmp) to load:

We need to specify the default tablespace and temporary tablespace for this staging user. Note – this user will be dropped once the load is completed.

Choose the AWR_STAGE users's default tablespace.  This is the
tablespace in which the AWR data will be staged.

TABLESPACE_NAME                CONTENTS  DEFAULT TABLESPACE
------------------------------ --------- ------------------
MGMT_ECM_DEPOT_TS              PERMANENT
MGMT_TABLESPACE                PERMANENT
PATROL                         PERMANENT
SYSAUX                         PERMANENT *
USERS                          PERMANENT

Pressing  will result in the recommended default
tablespace (identified by *) being used.

Enter value for default_tablespace:

Using tablespace SYSAUX as the default tablespace for the AWR_STAGE

Choose the Temporary tablespace for the AWR_STAGE user
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Choose the AWR_STAGE user's temporary tablespace.

TABLESPACE_NAME                CONTENTS  DEFAULT TEMP TABLESPACE
------------------------------ --------- -----------------------
TEMP                           TEMPORARY *

Pressing  will result in the database's default temporary
tablespace (identified by *) being used.

Enter value for temporary_tablespace:

Using tablespace TEMP as the temporary tablespace for AWR_STAGE

... Creating AWR_STAGE user

if we look at the import log file, we will see that data is first imported into the AWR_STAGE schema and then from here it is inserted into the WRH$ and other DBA_HIST tables.

Processing object type TABLE_EXPORT/TABLE/TABLE_DATA
. . imported "AWR_STAGE"."WRH$_SQL_PLAN"                 3.845 MB   11494 rows
. . imported "AWR_STAGE"."WRH$_SQLTEXT"                  1.012 MB     569 rows
. . imported "AWR_STAGE"."WRH$_SYSMETRIC_SUMMARY"        156.8 KB    2025 rows
.......
........
	Append Data for "AWR_STAGE".WRH$_SGASTAT.
INSERT /*+ APPEND */ INTO SYS.WRH$_SGASTAT (SNAP_ID, DBID, INSTANCE_NUMBER, NAME, POOL, BYTES) SELECT SNAP_ID,
3228342000, INSTANCE_NUMBER, NAME, POOL, BYTES FROM "AWR_STAGE".WRH$_SGASTAT WHERE DBID = :c_dbid
... appended 388 rows

If we now query the DBA_HIST_SNAPSHOT table, we see that it contains the data for two DBID’s – this shows that AWR history data is available in the repository database for two databases.

SQL> select distinct dbid from dba_hist_snapshot;

      DBID
----------
3228342000
3892233981

We can query the repository AWR tables based on DBID values to obtain data for a specific database.

Posted in Administration, Performance Tuning | Tagged: , , , , | Leave a Comment »

Performance Tuning Tips and Techniques

Posted by Gavin Soorma on July 17, 2009

Checks to be performed at the machine level (note the example is Red Hat Linux specific)

run queue should be ideally not more than the number of CPU’s on the machine

At the maximum it should never be more than twice the number of CPU’s.

This is denoted by the column ‘r’ in the vmstat output shown below

vmstat – 5

face:/opt/oracle>vmstat 5
procs                      memory      swap          io     system         cpu
 r  b   swpd   free   buff  cache   si   so    bi    bo   in    cs us sy id wa
 4  1 488700 245704 178276 12513572    0    1    10    17   48  1365 40 12 43  5
 2  0 488700 302568 178312 12514904    0    0   157   686 3354  4342 55 22 22  1
 3  1 488700 257500 178508 12517896    0    0   616   221 1352  2132 21  7 64  9
 2  2 488700 232348 178820 12525392    0    0  1550   274 3632  6091 29  9 42 20
 3  0 488700 225040 178880 12527336    0    0   346   452 2494  3300 45 13 38  4
 5  0 488700 225680 178916 12527884    0    0    79   269 2005  3847 29 11 58  2

CPU idle% < 10 ( id column) could indicate a machine that is having CPU resource issues

Note: How to find number of CPU’s on a LINUX machine?

cat /proc/cpuinfo |grep -w “processor” |wc –l

Swap columns si and so should ideally be 0 to indicate no swapping activity

face:/opt/oracle>free -m
             total       used       free     shared    buffers     cached
Mem:         15635      15476        159          0        173      12196
-/+ buffers/cache:       3106      12528
Swap:         3999        477       3522

We should be looking at the free and used values in the row denoted by
“-/+ buffers/cache”

The ‘top’ command will help us identify the load average on the machine as well as any process that is consuming excessive CPU

face:/opt/oracle>top
09:04:01  up 243 days, 13:01, 10 users,  load average: 2.80, 3.14, 3.13
813 processes: 797 sleeping, 8 running, 8 zombie, 0 stopped
CPU states:  cpu    user    nice  system    irq  softirq  iowait    idle
           total   40.7%    0.0%   12.7%   0.0%     0.3%    2.3%   43.7%
           cpu00   42.8%    0.0%   13.4%   0.3%     1.1%    1.9%   40.0%
           cpu01   37.3%    0.0%   12.6%   0.0%     0.0%    1.7%   48.2%
           cpu02   47.5%    0.0%   14.1%   0.0%     0.1%    1.1%   36.9%
           cpu03   35.1%    0.0%   10.5%   0.0%     0.0%    4.5%   49.7%

Mem:  16010560k av, 15808688k used,  201872k free,       0k shrd,  181324k buff
                   11368660k actv, 2257548k in_d,  265628k in_c
Swap: 4095984k av,  488700k used, 3607284k free                 12551192k cache

  PID USER     PRI  NI  SIZE  RSS SHARE STAT %CPU %MEM   TIME CPU COMMAND
24062 oracle    18   0 1342M 1.3G 1328M S    14.0  8.5 370:13   2 oracle
 6129 oracle    18   0  958M 953M  923M R    12.3  6.0 172:14   0 oracle
10077 oracle    25   0  149M 149M  145M R     4.5  0.9   0:00   2 oracle
 9924 oracle    16   0  156M 155M  150M S     2.6  0.9   0:00   2 oracle
10038 oracle    17   0  140M 140M  136M S     2.5  0.8   0:00   1 oracle
10055 oracle    24   0  136M 135M  132M S     2.4  0.8   0:00   0 oracle
25529 oracle    17   0  782M 782M  775M S     1.7  5.0   3:58   0 oracle
 8245 oracle    16   0  274M 274M  268M S     1.6  1.7   0:03   1 oracle
 9751 oracle    16   0 70484  67M 62952 S     1.3  0.4   0:01   2 oracle
31879 oracle    16   0  830M 828M  815M R     1.0  5.2  46:06   3 oracle
 9210 oracle    15   0  229M 229M  225M S     0.7  1.4   0:01   3 oracle

A load average greater than 5 or 10 could indicate a heavily utilized machine

CPU information for each CPU is also provided via the top command as well as information on the physical as well as virtual Memory that is available on the machine.

Information on the top CPU as well as Memory consuming processes is also provided along with the Process ID (PID). In a later section we will use this PID as a parameter for a SQL query to identify the SQL being executed by the same CPU consuming process.

Identify if any single PID is constantly appearing in the top output

The iowait column can also help us identify if there s any resource contention from the IO perspective.

A value above 40-50% would indicate I/O resource issues and require further investigation into the process that is causing this high I/O – or it could indicate a case of inefficient disk sub system or file layout in the database.

We can also view the state of the machine at a particular time of the day by running the sar command which will provide the system utilisation report since 12:00 AM on that particular day.

We can use the sar command to identify the machine state even for a particular day of the month

For example sar -f /var/log/sa/sa03 will report for the 3rd of the month

Checks to be performed at the database level

Identify with the user if the problem is a slow response or a hanging situation.

Establish a connection via SQL*NET using a non SYSDBA account to confirm that the listener is accepting client connections and the hanging is not due to the archive area getting 100% full.

Examine the alert log file for ‘Checkpoint Not Completed’ errors recorded at the time the performance problem is reported – this could indicate an I/O contention issue or inadequately sized redo log files which can also cause an application hang while the checkpoint completes.

Ensure that the mount point on which the Oracle software is not 100% full or the disks holding the controlfiles are also not 100% full.

Check the listener.log file if it exists and ensure that it is not > 2GB – on some Operating Systems like LINUX, there is a file size upper limit for the listener.log file after which client connections will not be accepted by the listener.

Check for locked sessions (see script check_lock.sql).

If a PID has been identified as a top CPU consuming process, check the SQL being executed by that particular PID (see script check_pid_sql.sql)

If the user provides a particular SID where a possible performance issue exists, check the SQL being executed by that SID ( see script check_sid_sql.sql)

If the user provides a particular Oracle username where a possible performance issue exists, check the SQL being executed by that Oracle user ( see script check_username_sql.sql)

Very Important – check the major wait events in the database (see script wait_events.sql)

Check the SID along with the events that each SID is waiting on (see script wait_events_sid.sql) – based on the SID, the SQL being executed by the waiting sessions can be obtained as well ( see script check_sid_sql.sql)

Refer document Resolving common Oracle Wait Events using the Wait Interface for details on important wait events along with their possible causes and resolution.

Check for any sessions continuously waiting for on a particular latch (see script check_latch.sql) and also refer document “Resolving common Oracle Wait Events using the Wait Interface.doc”.

What has changed?

Is there a measurable baseline regarding the “problem” query – when did it last perform well?

Has the database been upgraded recently?

Has any modifications been done to the database in terms of init.ora parameters?

Have any new indexes been added to the table or has the table structure changed?

Has the platform or database version changed?

Is this a period of unusual business activity? – like a monthly data load or one-off batch job

Appendix

check_pid_sql.sql

SET PAGESIZE 500
set long 500000
set head off
select
       s.username su,
       substr(sa.sql_text,1,540) txt
from v$process p,
     v$session s,
     v$sqlarea sa
where    p.addr=s.paddr
and      s.username is not null
and      s.sql_address=sa.address(+)
and      s.sql_hash_value=sa.hash_value(+)
and spid=;

check_sid_sql.sql

SET PAGESIZE 500
PROMPT=============================================================
PROMPT Current SQL statement this session executes
PROMPT=============================================================
col sql_text for a70 hea "Current SQL"
select q.sql_text
from v$session s
,    v$sql     q
WHERE s.sql_address = q.address
and   s.sql_hash_value + DECODE
                 (SIGN(s.sql_hash_value), -1, POWER( 2, 32), 0) = q.hash_value
AND   s.sid= ;

check_username_sql.sql

set long 500000
SET PAGESIZE 500
select
       s.username su,
       substr(sa.sql_text,1,540) txt
from v$process p,
     v$session s,
     v$sqlarea sa
where    p.addr=s.paddr
and      s.username is not null
and      s.sql_address=sa.address(+)
and      s.sql_hash_value=sa.hash_value(+)
and s.username=upper('&username');

check_lock.sql

set linesize 500
SET PAGESIZE 500
col waiting_session format 99999 heading 'Waiting|Session'
col holding_session format 99999 heading 'Holding|Session'
col mode_held format a20 heading 'Mode|Held'
col mode_requested format a20 heading 'Mode|Requested'
col lock_type format a20 heading 'Lock|Type'
prompt blocked objects from V$LOCK and SYS.OBJ$

set lines 132
col BLOCKED_OBJ format a35 trunc

select /*+ ORDERED */
    l.sid
,   l.lmode
,   TRUNC(l.ctime/60) min_blocked
,   u.name||'.'||o.NAME blocked_obj
from (select *
      from v$lock
      where type='TM'
      and sid in (select sid
                  from v$lock
                  where block!=0)) l
,     sys.obj$ o
,     sys.user$ u
where o.obj# = l.ID1
and   o.OWNER# = u.user#
;

prompt blocked sessions from V$LOCK

select /*+ ORDERED */
   blocker.sid blocker_sid
,  blocked.sid blocked_sid
,  TRUNC(blocked.ctime/60) min_blocked
,  blocked.request
from (select *
      from v$lock
      where block != 0
      and type = 'TX') blocker
,    v$lock        blocked
where blocked.type='TX'
and blocked.block = 0
and blocked.id1 = blocker.id1
;

prompt blockers session details from V$SESSION

set lines 132
col username format a10 trunc
col osuser format a12 trunc
col machine format a15 trunc
col process format a15 trunc
col action format a50 trunc

SELECT sid
,      serial#
,      username
,      osuser
,      machine
FROM v$session
WHERE sid IN (select sid
      from v$lock
      where block != 0
      and type = 'TX')
;

wait_events.sql

SELECT count(*), event FROM v$session_wait
WHERE wait_time = 0
AND event NOT IN
('smon timer','pmon timer','rdbms ipc message',
'SQL*Net message from client')
GROUP BY event ORDER BY 1 DESC
;

wait_events_sid.sql

col username format a12
col sid format 9999
col state format a15
col event format a45
col wait_time format 99999999
set pagesize 800
set linesize 800
select s.sid, s.username, se.event
from v$session s, v$session_wait se
where s.sid=se.sid
and se.event not like 'SQL*Net%'
and se.event not like '%rdbms%'
and s.username is not null
order by 3;

check_latch.sql

select count(*), name latchname from v$session_wait, v$latchname
where event='latch free' and state='WAITING' and p2=latch#
group by name order by 1 desc;

Posted in Performance Tuning, Unix | Tagged: , , , , , , , , , , | Leave a Comment »

Oracle Wait Events causes and resolutions

Posted by Gavin Soorma on July 3, 2009

This note will discuss the various common Wait Events like db file sequential read, db file scattered read, log file sync etc , their possible causes and the likely actions which can be taken to resolve the problem.

Download …

Posted in Performance Tuning | Tagged: , , | Leave a Comment »

Export and Import schema statistics

Posted by Arjun Raja on June 25, 2009

Easy way to export and import schema statistics – in case the optimizer is behaving strangely and you want to re-import old statistics when the optimizer behaved properly.

SCHEMA NAME – TEST

Create the table to store the statistics-
EXEC DBMS_STATS.CREATE_STAT_TABLE(‘TEST’, ‘STATS_TABLE’);

Export schema stats – will be stored in the ‘STATS_TABLE’
EXEC DBMS_STATS.export_schema_stats(‘TEST’,'STATS_TABLE’,NULL,’TEST’);

If required import these statistics back to TEST schema.
EXEC DBMS_STATS.import_schema_stats(‘TEST’,'STATS_TABLE’,NULL,’TEST’);

Finally drop the table created to backup the schema stats
EXEC DBMS_STATS.drop_stat_table(‘TEST’,'STATS_TABLE’);

Posted in Performance Tuning | Tagged: , | Leave a Comment »

LOB’s – some Performance Tuning considerations

Posted by Gavin Soorma on June 23, 2009

  • If LOB data is less than 4000 bytes, then it can be stored in line or on the same database page as the rest of the row data. LOB data can be stored in line only when the block size is large enough to accommodate it.
  • LOB data that is stored out of line, on database pages that are separate from the row data, is accessed (read and written) by Oracle in CHUNK size pieces where CHUNK is specified in the LOB storage clause (see Section 11.2 for more information about the CHUNK option). CHUNK must be an integer multiple of DB_BLOCK_SIZE and defaults to DB_BLOCK_SIZE if not specified. Generally, it is more efficient for Oracle to access LOB data in large chunks, up to 32 KB. However, when LOB data is updated, it may be versioned (for read consistency) and logged both to the rollback segments and the redo log in CHUNK size pieces. If updates to LOB data are frequent then it may be more efficient space wise to manipulate smaller chunks of LOB data, especially when the granularity of the update is much less than 32 KB.

 

  • If the same BLOB is to be accessed frequently, set the table LOB CACHE parameter to TRUE

 

  • Use the CACHE option on interMedia column objects containing BLOBs if the same BLOB data is to be accessed frequently. The CACHE option puts the data into the database buffer and makes it accessible for subsequent read operations. If you specify CACHE, then LOGGING is used; you cannot have CACHE and NOLOGGING.

 

  • Use the NOCACHE option (the default) if BLOB data is to be read only once or infrequently, or if you have too much BLOB data to cache, or if you are reading lots of images but none more frequently than others.

 

  • Set the CHUNK option to the number of blocks of interMedia column objects containing BLOB data that are to be accessed at one time. That is, the number of blocks that are to be read or written using the object.readFromSource or object.writeToSource interMedia audio and video object methods or call, OCILobRead( ), OCILobWrite( ), DBMS_LOB.READ( ), or DBMS_LOB.WRITE( ) during one access of the BLOB value. Note that the default value for the CHUNK option is 1 Oracle block and does not vary across systems. If only 1 block of BLOB data is accessed at a time, set the CHUNK option to the size of 1 block. For example, if the database block size is 2K, then set the CHUNK option to 2K.

 

  • Set the CHUNK option to the next largest integer multiple of database block size that is slightly larger than the audio, image, or video data size being inserted. Specifying a slightly larger CHUNK option allows for some variation in the actual sizes of the multimedia data and ensures that the benefit is realized. For large-sized media data, a general rule is to set the CHUNK option as large as possible. The maximum is 32K in Oracle9i. For example, if the database block size is 2K or 4K or 8K and the image data is mostly 21K in size, set the CHUNK option to 24K.

 

  • If you explicitly specify the storage characteristics for the interMedia column object containing a BLOB, make sure that the INITIAL and NEXT parameters for the BLOB data segment storage are set to a size that is larger than the CHUNK size. For example, if the database block size is 2K and you specify a CHUNK value of 8K, make sure that the INITIAL and NEXT parameters are at least 8K, preferably higher (for example, at least 16K).

 

  • If the interMedia column object containing a BLOB is small (that is, less than 4000 bytes), then storing the BLOB data out of line will decrease performance. However, storing the BLOB in the row increases the size of the row. This has a detrimental impact on performance if you are doing a lot of base table processing, such as full table scans, multiple row accesses (range scans), or doing many UPDATE or SELECT statements to columns other than the interMedia column objects containing BLOBs. If you do not expect the BLOB data to be less than 4000 bytes, that is, if all BLOBs are big, then the default is the best choice because:

               The LOB data is automatically moved out of line once it gets bigger than 4000 bytes.
               Performance can be better if the BLOB data is small (less than 4000 bytes including control information) and     is stored inline because the LOB locator and the BLOB data can be retrieved in the same buffer, thus reducing I/O     operations.

Posted in Performance Tuning | Leave a Comment »

10g Wait Event Classes and Histograms

Posted by Gavin Soorma on June 23, 2009

Oracle 10g has simplified one important aspect of tuning which is the wait event
interface by broadly classifying wait events into 8-10 major classes
 
This will give us a good overview of which area is contributing the most to a performance
problem and enable us to concentrate our efforts in those areas.
 
While the same information is very well displayed in Oracle 10g EM, it is a good idea to
know the SQL being executed in the background.
 
 
SQL> col wait_class format a30
SQL> select  wait_class,
  2     sum(total_waits), sum(time_waited)
  3  from v$session_wait_class
  4  where wait_class !='Idle'
  5  group by wait_class
  6  order by 3;
 
WAIT_CLASS                     SUM(TOTAL_WAITS) SUM(TIME_WAITED)
------------------------------ ---------------- ----------------
Network                                     199                0
Application                                  32                6
Commit                                      239               70
Configuration                               209              271
Other                                      1120             1035
User I/O                                   8185             5201
Concurrency                                 646             5865
System I/O                               422189           416403
 
8 rows selected.
 
 
To find out which individual events are contributing to the wait class "System I/O" we
can drill down using the next query
 
SQL> col event format a30
SQL> select event, total_waits, time_waited
  2  from v$system_event e, v$event_name n
  3  where n.event_id = e.event_id
  4  and n.wait_class = (select wait_class from v$session_wait_class
  5   where wait_class !='Idle'
  6   group by wait_class having
  7  sum(time_waited) = (select max(sum(time_waited)) from v$session_wait_class
  8  where wait_class !='Idle'
  9  group by (wait_class)))
 10  order by 3;
 
EVENT                          TOTAL_WAITS TIME_WAITED
------------------------------ ----------- -----------
log file single write                   24          95
Log archive I/O                        458         109
control file sequential read        239678        2503
log file sequential read               424        6677
db file parallel write               79129       86110
log file parallel write              44111       93207
control file parallel write         142355      228264
 
7 rows selected.
 
 
So now we see that the wait event accounting for most I/O waits is the event
related to writing to the control file which is 'control file parallel write'
 
We can go one step further and see the breakdown of the time that has been spent
by sessions waiting on this event by using Histograms.
 
These histograms will organise the wait duration into buckets and will tell us
things like if the wait duration happened often, but for a very short duration,
or happened occasionally, but when it happened the wait time was of a longer
duration.
 
The indicator of a problem is when high wait_count numbers are falling into buckets
located at the far end of the histogram - in this case we see the highest wait_counts
are concentrated at the top end of the histogram.
 
 
SQL> select wait_time_milli bucket, wait_count
  2   from v$event_histogram
  3   where event =
  4   'control file parallel write';
 
    BUCKET WAIT_COUNT
---------- ----------
         1          0
         2      10097
         4      37621
         8      34497
        16      28743
        32      17543
        64       8000
       128       4065
       256       1528
       512        548
      1024         35
      2048          3
      4096          2
      8192          1
 
 

Posted in Performance Tuning | Leave a Comment »

How to manually submit a 10g SQL Tuning Advisor task

Posted by Gavin Soorma on June 22, 2009

Purpose:

Use the following procedure to generate the 10g SQL Tuning Advisor reports and recommendations from the command line when  Database Control or Grid Control GUI  is not available.
CREATE A SQL TUNING TASK

 DECLARE
  my_task_name   VARCHAR2 (30);
  my_sqltext     CLOB;
 BEGIN
  my_sqltext :=  ‘INSERT INTO BWINT.PAYSLIP_CURRENT_RUN (ASSIGNMENT_ID, FULL_NAME,
            ASSIGNMENT_NUMBER, LOCATION_CODE, PAYROLL_ACTION_ID,PAY_DATE) (SELECT
            PAA.ASSIGNMENT_ID, PAA.FULL_NAME, PAA.ASSIGNMENT_NUMBER,
            PAA.LOCATION_CODE, PAA.PAYROLL_ACTION_ID,PAA.PERIOD_END_DATE FROM
             PAY_AU_ASG_PAYMENT_RUNS_V PAA, PER_PERIODS_OF_SERVICE PPS WHERE
            PAA.PAYROLL_ACTION_ID = :B1 AND PAA.PERSON_ID = PPS.PERSON_ID AND
            PPS.ACTUAL_TERMINATION_DATE IS NULL)’;  >>>>> SQL TO BE TUNED GOES HERE
   my_task_name := dbms_sqltune.create_tuning_task (sql_text=> my_sqltext,
      bind_list     => sql_binds (anydata.convertnumber (9)),
          user_name     => ‘BWINT’,
         scope         => ‘COMPREHENSIVE’,
         time_limit    => 60,
          task_name     => ‘CHECK_PAYROLL_RUN’,
         description   => ‘Testing Slow Running Online Payslip Run’
      );
   END;
  /

PL/SQL procedure successfully completed.

EXECUTE THE TUNING TASK

 BEGIN
dbms_sqltune.execute_tuning_task (task_name =>   2  ‘CHECK_PAYROLL_RUN’);
 end;
 /

PL/SQL procedure successfully completed.

VERIFY THE STATUS OF THE TASK

SQL> SELECT status FROM USER_ADVISOR_TASKS WHERE task_name =’CHECK_PAYROLL_RUN’;

STATUS
———–
COMPLETED

RUN THE SQL TUNING ADVISOR REPORT

SQL> set pagesize 120
SQL> set pagesize 5000
SQL> set linesize 120
SQL> set long 50000
SQL> set longchunksize 500000

SQL> SELECT DBMS_SQLTUNE.REPORT_TUNING_TASK(‘CHECK_PAYROLL_RUN’) FROM DUAL;
DBMS_SQLTUNE.REPORT_TUNING_TASK(‘CHECK_PAYROLL_RUN’)                           
—————————————————————————————————-
GENERAL INFORMATION SECTION                                                    
——————————————————————————-
Tuning Task Name                  : CHECK_PAYROLL_RUN                          
Tuning Task Owner                 : SYS                                        
Scope                             : COMPREHENSIVE                              
Time Limit(seconds)               : 60                                         
Completion Status                 : COMPLETED                                  
Started at                        : 05/09/2008 10:34:44                        
Completed at                      : 05/09/2008 10:35:54                        
Number of Index Findings          : 1                                          
Number of SQL Restructure Findings: 1                                          
Number of Errors                  : 1                                          
                                                                               
——————————————————————————-
Schema Name: BWINT                                                             
SQL ID     : 35mw20gycm0q5                                                     
SQL Text   : INSERT INTO BWINT.PAYSLIP_CURRENT_RUN (ASSIGNMENT_ID, FULL_NAME,  
                      ASSIGNMENT_NUMBER, LOCATION_CODE,                        
             PAYROLL_ACTION_ID,PAY_DATE) (SELECT                               
                      PAA.ASSIGNMENT_ID, PAA.FULL_NAME,                        
             PAA.ASSIGNMENT_NUMBER,                                            
                      PAA.LOCATION_CODE, PAA.PAYROLL_ACTION_ID,PAA.PERIOD_END_ 
             DATE FROM                                                         
                      PAY_AU_ASG_PAYMENT_RUNS_V PAA, PER_PERIODS_OF_SERVICE    
             PPS WHERE                                                         
                      PAA.PAYROLL_ACTION_ID = :B1 AND PAA.PERSON_ID =          
             PPS.PERSON_ID AND                                                 
                      PPS.ACTUAL_TERMINATION_DATE IS NULL)                     
                                                                               
——————————————————————————-
FINDINGS SECTION (2 findings)                                                  
——————————————————————————-
                                                                               
1- Index Finding (see explain plans section below)                             
————————————————–                             
  The execution plan of this statement can be improved by creating one or more 
  indices.                                                                     
                                                                               
  Recommendation (estimated benefit: 100%)                                     
  —————————————-                                     
  – Consider running the Access Advisor to improve the physical schema design  
    or creating the recommended index.                                         
    create index HR.IDX$$_04DE0001 on HR.PER_ALL_ASSIGNMENTS_F(‘ASSIGNMENT_ID’)
    ;                                                                          
                                                                               
  – Consider running the Access Advisor to improve the physical schema design  
    or creating the recommended index.                                         
    create index HR.IDX$$_04DE0002 on HR.PER_ALL_PEOPLE_F(‘PERSON_ID’);        
                                                                               
  – Consider running the Access Advisor to improve the physical schema design  
    or creating the recommended index.                                         
    create index HR.IDX$$_04DE0003 on HR.PAY_PAYROLL_ACTIONS(‘DATE_EARNED’,'PAY
    ROLL_ID’);                                                                 
                                                                               
  Rationale                                                                    
  ———                                                                    
    Creating the recommended indices significantly improves the execution plan 
    of this statement. However, it might be preferable to run “Access Advisor” 
    using a representative SQL workload as opposed to a single statement. This 
    will allow to get comprehensive index recommendations which takes into     
    account index maintenance overhead and additional space consumption.       
                                                                               
2- Restructure SQL finding (see plan 1 in explain plans section)               
—————————————————————-               
  An expensive cartesian product operation was found at line ID 17 of the      
  execution plan.                                                              
                                                                               
  Recommendation                                                               
  ————–                                                               
  – Consider removing the disconnected table or view from this statement or    
    add a join condition which refers to it.                                   
                                                                               
  Rationale                                                                    
  ———                                                                    
    A cartesian product should be avoided whenever possible because it is an   
    expensive operation and might produce a large amount of data.

Posted in Performance Tuning | Leave a Comment »

DBMS_ADVANCED_REWRITE – tuning without touching the code

Posted by Gavin Soorma on June 22, 2009

Very often, with packaged vendor applications we do not have access to the source code, but would like to customise the application to some specific requirements or in many cases would like an optimised piece of SQL code to be executed which is actually sub-optimal when run directly via the vendor application. We can use DBMS_ADVANCED_REWRITE for this.

This feature is available in Oracle 10g Release 2 (maybe Release 1 as well)

Scenario:

Application calculates tax based on product price – earlier used to be 3.5% of the price – now has changed to 5% of the product price because of government regulations.

Instead of making code changes to application we can direct Oracle to execute a totally different query when a particular query is executed

 

SQL> grant query rewrite to scott;

Grant succeeded.

SQL> grant execute on DBMS_ADVANCED_REWRITE to scott;

Grant succeeded.

 

 

SQL> create table sales_table (

item_id number primary key,

price number (10,2)

);

2 3 4

Table created.

SQL> insert into sales_table values (1, 14.95);

insert into sales_table values (2, 17.50);

insert into sales_table values (3, 21.35);

commit;

SQL> select price + (price * 0.035) as tax from sales_table;

TAX

———-

15.47325

18.1125

22.09725

SQL> begin

sys.dbms_advanced_rewrite.declare_rewrite_equivalence

‘new_tax’,

’select price + (price * 0.035) as tax from sales_table’, >>>> Old query run internally by application

’select price + (price * 0.05) as tax from sales_table’, >>>> New query automatically run by application

false);

end;

PL/SQL procedure successfully completed.

SQL> select price + (price * 0.035) as tax from sales_table; >>> now calculates using new value of 5% instead of 3.5%

TAX

———-

15.6975

18.375

22.4175

 

 

(

Posted in Performance Tuning | Leave a Comment »