rem
rem Oracle Database Healthcheck script
rem
rem Note: In order to analyze the alert.log the script is creating a directory for bdump
rem
rem The report should be run after the system has been up for at least 10 hours
rem and should be run on several occasions over a period of time to get a feel for
rem what the real condition of the database is. A one-time sample run on an
rem intactive system will not give an accurate picture of what is really occuring
rem within the database.
rem
rem
undefine all
ttitle off
whenever sqlerror continue
set echo off
set feedback off
set term off
column timecol new_value timestamp
column spool_extension new_value suffix
select to_char(sysdate,'Mondd_hh24mi') timecol,
'.out' spool_extension from sys.dual;
column outp new_value dbname
select value || '_' outp
from v$parameter where name = 'db_name';
spool healthcheck_&&dbname×tamp&&suffix
SET LINESIZE 80
SET ECHO OFF
SET NEWPAGE 1
SET HEADING OFF
SET TERMOUT ON
SET NUMWIDTH 9
SET VERIFY OFF
SET FEEDBACK OFF
SET PAGESIZE 58
SET RECSEP OFF
COLUMN nl NEWLINE
prompt
prompt *** Database Health Check ver 2.0
prompt
BEGIN
SYS.DBMS_APPLICATION_INFO.set_module ('HealthCheck', NULL);
END;
/
select 'Timestamp: '||to_char(sysdate,'Mon dd yyyy hh24:mi:ss') from dual;
prompt
select
' RAC database? : '||DECODE(vp1.value,'TRUE','Yes ('||
decode(vp1.value,'TRUE',' instances: '||vp2.value)||')','No') ||chr(10)||
' Current instance: '||instance_name
from
v$instance,
(select value
from v$parameter
where name like 'cluster_database'
) vp1,
(select value
from v$parameter
where name like 'cluster_database_instances'
) vp2;
prompt
prompt *** INSTANCE DETAILS:
prompt
select
' instance id : '||inst_id ||' ( '||instance_name||
' on ' ||host_name ||' )' ||chr(10)||
' inst status : '||status ||chr(10)||
' db status : '||database_status ||chr(10)||
' active state : '||active_state ||chr(10)||
' shutd pending?: '||shutdown_pending ||chr(10)||
' startup time : '||to_char(startup_time,'dd/mm/yyyy hh24:mi:ss')||chr(10)||
' uptime (days) : '||trunc(sysdate-startup_time+1) ||chr(10)||
' version : '||version ||chr(10)||
' logins : '||logins ||chr(10)||
' instance role : '||instance_role ||chr(10)||
' archiver? : '||archiver
from gv$instance
order by inst_id;
prompt
SET PAGESIZE 0
col msg for a80
select
(case when trunc(sysdate-startup_time+1) < 2
then '+ Database was recently restarted.'||chr(10)||
'+ The inormation about wait events and hit ratios might be not useful'
end) msg
from v$instance;
prompt
prompt *** DATABASE DETAILS:
prompt
COLUMN sort1 NOPRINT
SELECT
1 sort1,' Database name : '||name ||chr(10)||
' Database id : '||dbid ||chr(10)||
' Created : '||to_char(created,
'dd/mm/yyyy hh24:mi:ss') ||chr(10)||
' Log mode : '||log_mode ||
decode (log_mode,'ARCHIVELOG','',' !!!') ||chr(10)||
' Force logging : '||force_logging ||chr(10)||
' Open mode : '||open_mode ||chr(10)||
' Remote archiving : '||remote_archive ||chr(10)||
' Database role : '||database_role
FROM v$database
UNION
SELECT
2 sort1,' Datafiles : '||trim(TO_CHAR(COUNT(*),'9,990'))||chr(10)||
' Data f. size (Gb): '||trim(TO_CHAR(SUM(bytes)/
1073741824, '9,990'))
FROM v$datafile
UNION
SELECT
3 sort1,' Tempfiles : '||trim(TO_CHAR(COUNT(*),'9,990'))||chr(10)||
' Temp f. size (Gb): '||trim(TO_CHAR(SUM(bytes)/
1073741824, '9,990'))
FROM v$tempfile
UNION
SELECT
4 sort1,' Segm size (Gb) : '||trim(TO_CHAR(SUM(bytes)/
1073741824, '9,990'))
FROM dba_segments
UNION
SELECT
5 sort1,' Tables / Indexes : '||
trim(TO_CHAR(SUM(DECODE(type#, 2, 1, 0)), '999,990'))||'/'||
trim(TO_CHAR(SUM(DECODE(type#, 1, 1, 0)), '999,990'))
FROM sys.obj$
WHERE owner# <> 0
UNION
SELECT
6 sort1,' Total DB Users : ' ||trim( TO_CHAR(COUNT(*), '9,990'))
FROM sys.user$
WHERE type# = 1
UNION
SELECT
7 sort1,' Online Sessions : ' ||trim( TO_CHAR(COUNT(*), '9,990'))
FROM gv$session
WHERE type='USER'
UNION
SELECT
8 sort1,' Active Sessions : ' ||trim( TO_CHAR(COUNT(*), '9,990'))
FROM gv$session
WHERE type='USER' and status = 'ACTIVE'
UNION
SELECT
9 sort1,' Session highwater: ' ||
trim(TO_CHAR(sessions_highwater, '9,990'))
FROM
v$license
UNION
SELECT
10 sort1,' SGA (Mb) : '||trim(TO_CHAR(SUM(value)/
1048576, '99,990.99'))
FROM
v$sga;
SET PAGESIZE 50
set head on
prompt
prompt *** Database components
prompt
col comp_name for a40 heading "Component Name"
col version for a12 heading "Version"
col status for a15 heading "Status"
select
COMP_NAME,
VERSION,
STATUS
from
dba_registry;
prompt
prompt
prompt *** Current SESSION workload:
prompt
col inst_id format 9999 heading "Inst|ID"
col status format a20 heading "Session status"
col sno format 9999999999 heading "# sessions"
BREAK ON inst_id
select inst_id,status, count(*) sno
from gv$session
where type='USER'
group by inst_id,status
order by 1,2;
prompt
prompt *** DATABASE RESOURCE USAGE
prompt
col inst_id format 9999 heading "Inst|ID"
col resource_name format a22 heading "Resource|Name"
col current_utilization format 9999999999 heading "Curret|Utilization"
col max_utilization format 9999999999 heading "Max|Utilization"
col initial_allocation format a18 heading "Initial|Allocation"
col mpr format a4 heading "Max %|reached"
BREAK ON inst_id
select
inst_id,
resource_name,
current_utilization,
max_utilization,
initial_allocation
from
gv$resource_limit
where
max_utilization > 0
order by
inst_id,
resource_name;
prompt
prompt *** CONTROLFILES location and mirroring
prompt
COLUMN num FORMAT 99 HEADING '#'
COLUMN name FORMAT a60 HEADING 'Location' WORD_WRAPPED
COLUMN status FORMAT a7 HEADING 'Status'
SELECT
rownum num,
name,
status
FROM
v$controlfile;
prompt
set pagesize 0
select (case
when count(1) <2 then '+ At least 2 controlfiles are recommended'
when count(1) >=2 and count(1) <=3 then '+ '||count(1)||' mirrors for controlfile detected. - OK'
else '+ More than 3 controlfiles might have additional overhead. Check the wait events.'
end)
from v$controlfile;
col msg format a79
select
(case when value <45 then '+ ! "control_file_record_keep_time='||value||'" to low. Set to at least 45'
else '+ "control_file_record_keep_time='||value||'" - OK.'
end) msg
from v$parameter where name = 'control_file_record_keep_time';
set pagesize 50
set heading on
prompt
prompt *** REDO LOGs location, size and mirroring
prompt
COLUMN thread# FORMAT 9 HEADING "Th"
COLUMN group# FORMAT 90 HEADING "Group"
COLUMN sequence# FORMAT 999999999990 HEADING "Sequence|Number"
COLUMN mbytes FORMAT 9999990 HEADING "Size|(Mb)"
COLUMN members FORMAT 90 HEADING "Members"
COLUMN first_time FORMAT A15 HEADING "First|Time"
COLUMN status FORMAT A12 HEADING "Status" TRUNC
COLUMN archived FORMAT A3 HEADING "Arc"
SELECT
thread#,
group#,
sequence#,
bytes / 1024 / 1024 mbytes,
TO_CHAR(first_time, 'DD-MON-YY HH24:MI') first_time,
status,
archived
FROM
v$log
ORDER BY
thread#,
group#;
-- prompt
-- prompt + STATUS Description
-- prompt + ********** ***********************************************************
-- prompt + UNUSED Indicates the online redo log has never been written to.
-- prompt + This is the state of a redo log that was just added, or just
-- prompt + after a RESET-LOGS, when it is not the current redo log.
-- prompt + CURRENT Indicates this is the current redo log. This implies that
-- prompt + the redo log is active. The redo log could be open or closed.
-- prompt + ACTIVE Indicates the log is active but is not the current log. It is
-- prompt + needed for instance recovery. It might or might not be
-- prompt + archived.
-- prompt + CLEARING Indicates the log is being recreated as an empty log after
-- prompt + an ALTER DATABASE CLEAR LOGFILE command. After the log is
-- prompt + cleared, the status changes to UNUSED.
-- prompt + CLEARING Indicates that the current log is being cleared of a closed
-- prompt + _CURRENT Thread. The log can stay in this status if there is some
-- prompt + failure in the switch such as an I/O error writing the new
-- prompt + log header.
-- prompt + INACTIVE Indicates the log is no longer needed for instance recovery.
-- prompt + It may be in use for media recovery. It might or might not be
-- prompt + archived.
-- prompt + INVALID Thread. File is inaccessible.
-- prompt + STALE File contents are incomplete (such as when an instance is shut
-- prompt + down with SHUTDOWN ABORT or due to a system crash).
-- prompt + DELETED File is no longer used.
prompt
set pagesize 0
select 'Group# '||group#||': '||
(case
when members<2 then '+ Redo log mirroring is recommended'
else ' '||members||' members detected. - OK'
end)
from v$log
where members < 2;
prompt
select (case
when count(*) > 1 then '+ Groups with different size detected. - Not recommended.'
else '+ All groups have the same size. - OK'
end)
from (select distinct bytes from v$log);
select
(case
when count(*)>3 then '+ '||count(*)||' times detected when log switches occured more than 1 log per 5 minutes.'||chr(10)||
'+ You may consider to increase the redo log size.'
else '+ Redo log size: OK'
end)
from (
select trunc(FIRST_TIME,'HH') Week, count(*) arch_no, trunc(10*count(*)/60) archpermin
from v$log_history
group by trunc(FIRST_TIME,'HH')
having trunc(5*count(*)/60)>1
);
prompt
prompt *** REDO CONTENTION STATISTICS:
prompt
prompt The following shows how often user processes had to wait for space in
prompt the redo log buffer:
set head off
select name||' = '||value
from v$sysstat
where name = 'redo log space requests';
set head on
prompt
prompt This value should be near 0. If this value increments consistently,
prompt processes have had to wait for space in the redo buffer. If this
prompt condition exists over time, increase the size of LOG_BUFFER in the
prompt init.ora file in increments of 5% until the value nears 0.
prompt ** NOTE: increasing the LOG_BUFFER value will increase total SGA size.
prompt
prompt
prompt Another way to check the contention on redo log buffer:
prompt
prompt The following query output shows the system waits on
prompt 'log file sync' and 'db file parallel write'
prompt
select (case when sync_waits = 0 then '+ Waits for log file sync not detected: log_buffer is OK'
else '+ Waits for log file sync detected ('||sync_waits||' times): Consider to increase the log_buffer'
end)
from (
select
decode(
sum(w.total_waits),
0, 0,
nvl(100 * sum(l.total_waits) / sum(w.total_waits), 0)
) sync_waits
from
sys.v_$bgprocess b,
sys.v_$session s,
sys.v_$session_event l,
sys.v_$session_event w
where
b.name like 'DBW_' and
s.paddr = b.paddr and
l.sid = s.sid and
l.event = 'log file sync' and
w.sid = s.sid and
w.event = 'db file parallel write'
);
prompt
prompt
prompt *** REDO LOG SWITCHING ACTIVITY (last 30 days):
prompt
set pagesize 0
select '+ Daily (max) : '||max(no)||' switches. Size: '||round((max(no) * max(logsize ))/1024,2)||'Gb'||chr(10)||
'+ Daily (avg) : '||trunc(avg(no))||' switches. Size: '||round((avg(no) * max(logsize ))/1024,2)||'Gb'||chr(10)||
'+ Daily (min) : '||min(no)||' switches. Size: '||round((min(no) * max(logsize ))/1024,2)||'Gb'
from (
select trunc(FIRST_TIME,'DD'), count(*) no
from v$log_history
where FIRST_TIME > sysdate - 31
group by trunc(FIRST_TIME,'DD')),
(select max(bytes/1024/1024) logsize from v$log);
prompt
select '+ Weekly (max): '||max(no)||' switches. Size: '||round((max(no) * max(logsize ))/1024,2)||'Gb'||chr(10)||
'+ Weekly (avg): '||trunc(avg(no))||' switches. Size: '||round((avg(no) * max(logsize ))/1024,2)||'Gb'||chr(10)||
'+ Weekly (min): '||min(no)||' switches. Size: '||round((min(no) * max(logsize ))/1024,2)||'Gb'
from (
select trunc(FIRST_TIME,'WW'), count(*) no
from v$log_history
where FIRST_TIME > sysdate - 31
group by trunc(FIRST_TIME,'WW')),
(select max(bytes/1024/1024) logsize from v$log);
set pagesize 50
prompt
prompt List redo log switches for the last 24 hours per hour:
prompt
col Hour format a20 heading "Hour"
col switches format 999999 heading "# of redo|switches"
select to_char(trunc(FIRST_TIME,'HH'),'dd-MON-yy hh24')||'h' Hour, count(*) switches
from v$log_history
where FIRST_TIME > (sysdate-1) - 1/24
group by to_char(trunc(FIRST_TIME,'HH'),'dd-MON-yy hh24')||'h'
order by 1;
prompt
prompt
prompt *** SGA DETAILS:
prompt
set pagesize 50
COLUMN inst_id format 9999 HEADING "Inst|id"
COLUMN name FORMAT A25 HEADING "Part"
COLUMN value FORMAT 99999999999999990 HEADING "Bytes"
COLUMN k FORMAT 999999999990.0 HEADING "Kbytes"
COLUMN m FORMAT 9999990.0 HEADING "Mbytes"
BREAK ON inst_id
COMPUTE SUM LABEL Total OF m ON inst_id
SET HEADING ON
SELECT
inst_id,
name,
value,
value / 1024 k,
value / 1024 /1024 m
FROM
GV$SGA
order by 1;
prompt
prompt + !!! BUG alert:
prompt + !!! ORA-600 error might occurs with instance crash when the parameter
prompt + !!! SGA_TARGET is set to an exact multiple of 4Gb due to Bug 4433838.
prompt
prompt
prompt *** SGA HIT RATIOS:
set pagesize 0
COLUMN val2 NEW_VALUE lib NOPRINT
SELECT (1 - (SUM(reloads) / SUM(pins)))*100 val2
FROM v$librarycache;
COLUMN val2 NEW_VALUE dict NOPRINT
SELECT (1 - (SUM(getmisses) / SUM(gets)))*100 val2
FROM v$rowcache;
COLUMN val2 NEW_VALUE phys_reads NOPRINT
SELECT value val2
FROM v$sysstat
WHERE name = 'physical reads';
COLUMN val2 NEW_VALUE log1_reads NOPRINT
SELECT value val2
FROM v$sysstat
WHERE name = 'db block gets';
COLUMN val2 NEW_VALUE log2_reads NOPRINT
SELECT value val2
FROM v$sysstat
WHERE name = 'consistent gets';
COLUMN val2 NEW_VALUE chr NOPRINT
SELECT (1 - (&phys_reads / (&log1_reads + &log2_reads)))*100 val2
FROM DUAL;
SET HEADING OFF
SELECT
' Data Block Buffer Hit Ratio (%): ' || &chr || chr(10) ||
' Shared SQL Pool' || chr(10) ||
' Dictionary Hit Ratio (%): ' || &dict || chr(10) ||
' Shared SQL Buffers (Library Cache)' || chr(10) ||
' Cache Hit Ratio (%): ' || &lib
FROM DUAL;
set pagesize 45
SET HEADING OFF
prompt
PROMPT *** LIBRARY CACHE STATISTICS:
prompt
select 'Instance id: '||inst_id,
'PINS - # of times an item in the library cache was executed - '||
sum(pins),
'RELOADS - # of library cache misses on execution steps - '||
sum (reloads),
'RELOADS / PINS * 100 = '||round((sum(reloads) / sum(pins) * 100),2)||'%'
from gv$librarycache
group by 'Instance id: '||inst_id
order by 1;
prompt
prompt + Increase memory until RELOADS is near 0 but watch out for
prompt + Paging/swapping
prompt + To increase library cache, increase SHARED_POOL_SIZE
prompt +
prompt + ** NOTE: Increasing SHARED_POOL_SIZE will increase the SGA size.
prompt +
prompt + Library Cache Misses indicate that the Shared Pool is not big
prompt + enough to hold the shared SQL area for all concurrently open cursors.
prompt + If you have no Library Cache misses (PINS = 0), you may get a small
prompt + increase in performance by setting CURSOR_SPACE_FOR_TIME = TRUE which
prompt + prevents ORACLE from deallocating a shared SQL area while an
prompt + application cursor associated with it is open.
prompt
SET HEADING ON
prompt
prompt *** CACHE HIT RATIO per Buffer Pool:
prompt
set pagesize 0
COLUMN val1 NEW_VALUE defblsize NOPRINT
select
to_number(value)/1024 val1
from
v$parameter
where
name like 'db_block_size';
set heading on
set pagesize 25
col pool FORMAT A20 HEADING "Buffer pool"
col ratio FORMAT 999.90 HEADING "Hit Ratio (%)"
col comment FORMAT A40 HEADING "Comment"
select name||'.'||block_size/1024||'k' pool,
100-round((physical_reads/(db_block_gets+consistent_gets+0.001))*100,2) as ratio,
(case when (100-round((physical_reads/(db_block_gets+consistent_gets+0.001))*100,2)) >90
then 'OK' else 'Consider to increase db_'||DECODE(block_size/1024,&defblsize,'',block_size/1024||'k_')||'cache_size' end) "comment"
from v$buffer_pool_statistics;
prompt
prompt *** FREE MEMORY AVAILABLE IN THE SGA:
col pool format A20
col name format A20
col sgas heading "Allocated|SGA (Mb)"
col frees heading "Free|(Kb)"
col freep heading "%|Free"
select pool,
name,
round(sgasize/1024/1024,2) sgas,
round(bytes/1024,2) frees,
round(bytes/sgasize*100, 2) freep
from (select sum(bytes) sgasize from sys.v_$sgastat) s, sys.v_$sgastat f
where f.name = 'free memory';
prompt
prompt *** MEMORY INIT PARAMETERS:
prompt
column inst_id heading "Inst|id" format 9999
column param heading "Parameter" format a50
BREAK ON inst_id
select
inst_id,
rpad(name,25,' ')||'= '||nvl(value ,0) param
from
gv$parameter
where
name like 'db%cache_size'
or name like 'log_buffer'
or name like '%pool_size'
or name like 'db_block_buffers'
or name like 'db_block_size'
or name like 'sga_max_size'
or name like 'sga_target'
or name like 'pga_aggregate_target'
order by inst_id, param;
set heading off
prompt
select 'Default block size : db_block_size = '||to_number(value)/1024||'k' from v$parameter where name like 'db_block_size'
union
select 'Default buffer pool: db_cache_size = '||value from v$parameter where name like 'db_cache_size'
union
select 'Other buffer pools : '||name||' = '||value from v$parameter where name <> 'db_cache_size' and name like 'db%k_cache_size' and value <>'0';
prompt
select
'+ Buffer Cache Advisory is: '||value||chr(10)||
DECODE (value,'ON','+ Review the buff cache advisory below and adjust the memory settings if needed',
'OFF','+ Enable the Buffer Cache Advisory by setting init parameter ''db_cache_advice=ON''',
'READY','+ The Buffer Cache Advisor is READY. Please switch it ON using'||chr(10)||
' ALTER SYSTEM SET DB_CACHE_ADVICE = ON')
from v$parameter where name like 'db_cache_advice';
prompt
prompt *** Buffer Pool Advisory:
prompt
select 'Instance ID: '||inst_id||' Buffer pool: '||pool||' status='||status from (
select inst_id,name||' '||block_size/1024||'k' pool, advice_status status, count(*)
from gV$DB_CACHE_ADVICE
group by inst_id,name||' '||block_size/1024||'k', advice_status)
order by 1;
column inst_id heading "Inst|id" format 9999
column block_size heading "Block|Size" format 999999
column size_factor heading 'Size Factor' format 99.90
column c1 heading 'Cache Size (m)' format 999,999,999,999
--column c2 heading 'Buffers' format 999,999,999
column c3 heading 'Estd Phys|Read Factor' format 999.90
column c4 heading 'Estd Phys| Reads' format 999999999999999
BREAK ON inst_id
set heading on
select name||'.'||block_size/1024||'k' pool,
100-round((physical_reads/(db_block_gets+consistent_gets+0.001))*100,2) as ratio,
(case when (100-round((physical_reads/(db_block_gets+consistent_gets+0.001))*100,2)) >90
then 'OK' else 'Consider to increase db_'||DECODE(block_size/1024,&defblsize,'',block_size/1024||'k_')||'cache_size' end) "comment"
from v$buffer_pool_statistics;
select
inst_id,
block_size,
size_factor,
size_for_estimate c1,
-- buffers_for_estimate c2,
estd_physical_read_factor c3,
estd_physical_reads c4
from
gv$db_cache_advice
where
name = 'DEFAULT'
and
advice_status = 'ON'
order by 1,2,3;
prompt
set pagesize 0
select
'+ Instance# '||s1.inst_id||
' "db_'||DECODE(s1.block_size/1024,&defblsize,'',s1.block_size/1024||'k_')||
'cache_size"='||s1.size_for_estimate||'MB'||chr(10)||
'+ Increasing "db_'||DECODE(s1.block_size/1024,&defblsize,'',s1.block_size/1024||'k_')||
'cache_size" to '||se.size_for_estimate||
' MB may reduce disk read factor to '||se.estd_physical_read_factor
from
(
select inst_id,block_size,size_factor,size_for_estimate,estd_physical_read_factor
from gv$db_cache_advice
where name = 'DEFAULT'
and advice_status = 'ON'
and size_factor = 1
) s1,
(
select sa.inst_id,sa.block_size,sa.size_factor,sa.size_for_estimate,sa.estd_physical_read_factor
from
(
select inst_id,block_size,size_factor,size_for_estimate,estd_physical_read_factor
from gv$db_cache_advice
where name = 'DEFAULT'
and advice_status = 'ON'
) sa
,
(select inst_id,block_size,max(size_factor) sfm
from gv$db_cache_advice
where name = 'DEFAULT' and advice_status = 'ON'
group by inst_id,block_size
) sm
where sa.inst_id = sm.inst_id
and sa.block_size = sm.block_size
and sa.size_factor = sm.sfm
) se
where se.inst_id = s1.inst_id
and se.block_size = s1.block_size
and se.estd_physical_read_factor < 0.90
order by s1.block_size, s1.inst_id;
set pagesize 50
prompt
prompt *** SORT Statistics:
prompt
COLUMN r FORMAT A10 HEADING "Sorted|Rows"
COLUMN m FORMAT A10 HEADING "Memory|Sorts"
COLUMN d FORMAT A7 HEADING "Disk|Sorts"
COLUMN pct FORMAT 990.00 HEADING "Disk|%"
COLUMN comm FORMAT A26 HEADING "Comment"
COLUMN p FORMAT A14 HEADING "SORT_AREA_SIZE"
SELECT
TO_CHAR(r.value) r,
TO_CHAR(m.value) m,
TO_CHAR(d.value) d,
DECODE(m.value, 0, 1, d.value / m.value * 100) pct,
DECODE(SIGN(DECODE(m.value, 0, 1, d.value / m.value) - 0.05),
1, 'Increase SORT_AREA_SIZE / PGA',
'OK') comm
--, p.value p
FROM
v$sysstat r,
v$sysstat m,
v$sysstat d,
v$parameter p
WHERE
r.name = 'sorts (rows)'
AND m.name = 'sorts (memory)'
AND d.name = 'sorts (disk)'
AND p.name = 'sort_area_size';
prompt
prompt
prompt *** PGA statistics:
prompt
set pagesize 0
select '+ Automatic SQL Execution Memory Management is '||decode (UPPER(value),'AUTO','ENABLED','DISABLED')||chr(10)||
'+ workarea_size_policy='||UPPER(value) from v$parameter where name = 'workarea_size_policy'
union all
select '+ pga_aggregate_target='||value from v$parameter where name = 'pga_aggregate_target';
select decode(UPPER(value),'MANUAL', '+ Enable Automatic SQL Execution Memory Management as described below:','')
from v$parameter where name = 'workarea_size_policy';
-- prompt
-- prompt + Optimization of the PGA memory structures could be very time consuming
-- prompt + depending on the type of operations the system was performing.
-- prompt + Oracle9i allows to leave configuration of the PGA up to Oracle by setting two
-- prompt + initialization parameters:
-- prompt + WORKAREA_SIZE_POLICY = AUTO
-- prompt + PGA_AGGREGATE_TARGET = ?????K
-- prompt
-- prompt + Actually the Automatic SQL Memory Management feature can be enabled by setting
-- prompt + only the parameter PGA_AGGREGATE_TARGET to a non-zero value.
-- prompt + The WORKAREA_SIZE_POLICY parameter tell the server that it should take over
-- prompt + PGA memory management. The PGA_AGGREGATE_TARGET parameter specifies the total
-- prompt + amount of memory the server can allocate to the PGA.
prompt + Oracle quote the following equations as a base for calculating the value
prompt + of this parameter:
prompt + PGA_AGGREGATE_TARGET = (TOTAL_MEM * 80%) * 20% for an OLTP system
prompt + PGA_AGGREGATE_TARGET = (TOTAL_MEM * 80%) * 50% for a DSS system
prompt + Where TOTAL_MEMORY is the total available memory for the system.
prompt + When the Automatic SQL Execution Memory Management feature is enabled, any
prompt + *_AREA_SIZE settings are ignored.
prompt
prompt *** DISPLAY PGA TARGET ADVICE:
prompt
-- prompt + Besides SGA, the Oracle database also assigns each server process a private
-- prompt + memory region called the .Program Global Area. (PGA). A PGA is created for
-- prompt + each server process when it is started (i.e. when a new session is initiated
-- prompt + while using the dedicate server configuration or when a new shared server
-- prompt + process is created). It contains data and control information for a server
-- prompt + process and, unlike the SGA, each server process has exclusive access to its
-- prompt + PGA.
-- prompt + Although the actual content of the PGA varies depending on whether a process
-- prompt + is running in dedicated or shared mode, it can generally be categorized as
-- prompt + follows:
-- prompt + o Session Memory: The memory allocated to hold session.s logon information
-- prompt + and other such details. For shared server processes, this information is
-- prompt + stored in the SGA.
-- prompt + o SQL Execution Memory: The memory allocated on behalf of SQL statements
-- prompt + being executed. The SQL Execution Memory has a persistent and a run time
-- prompt + area
-- prompt + o The persistent area is allocated when a cursor is opened for SQL
-- prompt + execution. It contains the information that persists across multiple
-- prompt + execution of the same statement (i.e. cursor) such as bind details,
-- prompt + data type conversion, etc. The persistent area is de-allocated when the
-- prompt + cursor is closed. For shared server processes, the persistent area is a
-- prompt + part of the SGA.
-- prompt + o The runtime area contains information used while a SQL statement is
-- prompt + being executed. Its size depends on the type and complexity of SQL
-- prompt + statement as well as the number and size of rows being processed. It is
-- prompt + de-allocated once the execution completes. For shared sever processes,
-- prompt + the run time area is resides in the PGA for DML/DDL operation and in
-- prompt + the SGA for queries. For complex queries, such as those used for
-- prompt + reporting purposes or as ad hoc queries in a data-warehousing
-- prompt + environment, a large portion of the run time SQL execution memory is
-- prompt + dedicated as the working area for operations such as sort, hash-join
-- prompt + and bitmap-merge.
-- prompt + Larger working areas can significantly improve the performance of these
-- prompt + operations and thereby, reduce the response time of queries. Ideally, the size
-- prompt + of work area should be large enough such that it can accommodate all input
-- prompt + data and auxiliary structure needed by the operation.
column p1 heading 'Target(M)'
column p2 heading 'Estimated|Cache Hit %'
column p3 heading 'Estimated|Over-Alloc.'
set heading on
set pagesize 50
BREAK ON inst_id
SELECT
inst_id,
ROUND(pga_target_for_estimate /(1024*1024)) p1,
PGA_TARGET_FACTOR "Size Factor",
estd_pga_cache_hit_percentage p2,
estd_overalloc_count p3
FROM
gv$pga_target_advice
order by 1 asc, 2 asc;
prompt
prompt + The following output shows us how many operations, or executions, were
prompt + performed in the work areas. These executions fall into three categories.
prompt + The optimal executions are those operations that were performed entirely in
prompt + memory. As the name suggests, this is the most favorable type of execution.
prompt + If the operation was too big to be performed in memory, then part of the
prompt + operation spills onto disk. If only one pass was needed on disk, then this
prompt + execution is noted in the onepass statistic. If more than one pass was needed
prompt + on disk, then this execution is noted in the multipass statistic. Ideally,
prompt + all executions should be in the optimal statistic and the statistics for
prompt + onepass and multipass should be zero. If the # of multipass executions is
prompt + high consider increasing the value of pga_aggregate_target parameter.
prompt
col name for a35
select
inst_id,
name,
value
from
gv$sysstat
where name like 'workarea executions%'
order by 1 asc, 2 desc;
set heading off
col msg for a50
select (case when value>1000 then
'+ You may need to increase pga_aggregate_target for inst #'||inst_id||chr(10)||
'+ Check the PGA Advisor for suggested size'
else '+ pga_agregate_target is OPTIMAL for inst #'||inst_id
end) msg
from gv$sysstat
where name like 'workarea executions - multipass';
prompt
prompt
prompt *** DISPLAY SHARED POOL ADVICE:
prompt
set pages 999
column s1 heading 'Pool |Size(M)'
column s2 heading 'Size|Factor'
column s3 heading 'Est|LC(M) '
column s4 heading 'Est LC|Mem. Obj.'
column s5 heading 'Est|Time|Saved|(sec)'
column s6 heading 'Est|Parse|Saved|Factor'
column s7 heading 'Est|Object Hits' format 999,999,999,999
prompt + This advisory gives advice on library cache memory and predicts the effect of
prompt + altering the shared pool size on the total amount parsing activities in the
prompt + system.
prompt + The following view displays information about estimated parse timesavings in
prompt + different sizes of shared pool. The sizes range from 50% to 200% of current
prompt + shared pool size, in equal intervals. The value of the interval depends on
prompt + current shared pool size. Parse Time Saved refers to the amount of time saved
prompt + by keeping library cache memory objects in the shared pool, as opposed to
prompt + having to reload these objects.
set heading on
BREAK ON inst_id
SELECT
inst_id,
shared_pool_size_for_estimate s1,
shared_pool_size_factor s2,
estd_lc_size s3,
estd_lc_memory_objects s4,
-- estd_lc_time_saved s5,
estd_lc_time_saved_factor s6,
estd_lc_memory_object_hits s7
FROM
gv$shared_pool_advice
order by 1,3;
PROMPT
PROMPT *** LIST ORACLE JOBS (Check for broken jobs here)
PROMPT
COLUMN jid FORMAT 9999 HEADING 'Id'
COLUMN subu FORMAT A10 HEADING 'Submitter' TRUNC
COLUMN secd FORMAT A10 HEADING 'Security' TRUNC
COLUMN proc FORMAT A19 HEADING 'Job' WORD_WRAPPED
COLUMN lsd FORMAT A5 HEADING 'Last|Ok|Date'
COLUMN lst FORMAT A5 HEADING 'Last|Ok|Time'
COLUMN nrd FORMAT A5 HEADING 'Next|Run|Date'
COLUMN nrt FORMAT A5 HEADING 'Next|Run|Time'
COLUMN fail FORMAT 999 HEADING 'Errs'
COLUMN ok FORMAT A3 HEADING 'Ok?'
SELECT
job jid,
log_user subu,
priv_user secd,
substr(what,1,50) proc,
TO_CHAR(last_date, 'MM/DD') lsd,
SUBSTR(last_sec, 1, 5) lst,
TO_CHAR(next_date, 'MM/DD') nrd,
SUBSTR(next_sec, 1, 5) nrt,
failures fail,
DECODE(broken, 'Y', 'No', 'Yes') ok
FROM
sys.dba_jobs;
PROMPT
PROMPT *** INVALID DATABASE OBJECTS:
PROMPT
COLUMN owner FORMAT A16 HEADING "Object Owner"
COLUMN object_name FORMAT A30 HEADING "Object Name"
COLUMN object_type FORMAT A25 HEADING "Object Type"
COLUMN last_time FORMAT A18 HEADING "Last Change Time"
COLUMN no FORMAT 99999 HEADING "# of Invalid|Objects"
prompt
prompt SUMMARY:
SELECT
owner,
object_type,
count(*) no
FROM
dba_objects
WHERE
status = 'INVALID'
GROUP BY
owner,
object_type
ORDER BY
1,2;
PROMPT
PROMPT LISTING ONLY SYS INVALID OBJECTS:
PROMPT ...
PROMPT
col owner for a10 heading "Owner"
col object_name for a33 heading "Object Name"
col object_type for a15 heading "Object Type"
col last_ddl_time for a18 heading "Last DDL Time"
SELECT
owner,
object_type,
object_name,
TO_CHAR(last_ddl_time, 'DD-MON-YY hh:mi:ss') last_ddl_time
FROM
dba_objects
WHERE
status = 'INVALID'
and owner in ('SYS','SYSTEM')
ORDER BY
owner,
object_type,
object_name;
PROMPT
PROMPT *** NON-SYSTEM objects in SYSTEM tablespace:
PROMPT
COLUMN owner FORMAT A30 HEADING "Object Owner"
COLUMN segment_type FORMAT A22 HEADING "Object Type"
COLUMN no FORMAT 99999 HEADING "# of Objects"
set pagesize 0
SELECT
'+ '||count(*)||' NON-SYSTEM objects detected in SYSTEM tablespace with '||
'total size: '||NVL(round(sum(bytes)/1024/1024,2),0)||'Mb'
FROM
dba_segments
WHERE
tablespace_name = 'SYSTEM'
and owner not in ('SYS','SYSTEM','CSMIG','OUTLN');
set pagesize 50
SELECT
owner,
segment_type,
count(*) no
FROM
dba_segments
WHERE
tablespace_name = 'SYSTEM'
and owner not in ('SYS','SYSTEM','CSMIG','OUTLN')
GROUP BY
owner,
segment_type
ORDER BY
1,2;
prompt
prompt + Run $ORACLE_HOME/rdbms/admin/utlrp.sql to recompile invalid objects (if any)
prompt + Make sure that all SYS and SYSTEM objects are valid
prompt + Address the application invaild objects to the application support team
prompt
set heading off
-- PROMPT
-- PROMPT *** NON-INDEXED FOREIGN KEYS:
-- PROMPT
-- prompt + Lists foreign keys that are missing indexes on the foreign key
-- prompt + columns in the child table.
--
-- col msg for a70
--
-- SELECT (case when count(*)>0 then '+ '||count(*) ||' non-indexes foreign key detected.'
-- else '+ Non-indexes foreign key not detected. OK.'
-- end) msg
-- FROM
-- all_cons_columns acc,
-- all_constraints ac
-- WHERE
-- acc.owner not in ('SYS','SYSTEM','PERFSTAT')
-- AND acc.table_name not like 'MVIEW$%'
-- AND ac.constraint_name = acc.constraint_name
-- AND ac.constraint_type = 'R'
-- AND (acc.owner, acc.table_name, acc.column_name, acc.position) IN
-- (
-- SELECT
-- acc.owner,
-- acc.table_name,
-- acc.column_name,
-- acc.position
-- FROM
-- all_cons_columns acc,
-- all_constraints ac
-- WHERE
-- ac.constraint_name = acc.constraint_name
-- AND ac.constraint_type = 'R'
-- MINUS
-- SELECT
-- table_owner,
-- table_name,
-- column_name,
-- column_position
-- FROM
-- all_ind_columns
-- );
--
-- set pagesize 25
-- set heading on
--
-- COLUMN owner FORMAT A20 HEADING "Owner"
-- COLUMN no FORMAT 999999 HEADING "# of non-indexed FK"
-- SELECT
-- acc.owner,
-- count(*) no
-- FROM
-- all_cons_columns acc,
-- all_constraints ac
-- WHERE
-- acc.owner not in ('SYS','SYSTEM','PERFSTAT')
-- AND acc.table_name not like 'MVIEW$%'
-- AND ac.constraint_name = acc.constraint_name
-- AND ac.constraint_type = 'R'
-- AND (acc.owner, acc.table_name, acc.column_name, acc.position) IN
-- (
-- SELECT
-- acc.owner,
-- acc.table_name,
-- acc.column_name,
-- acc.position
-- FROM
-- all_cons_columns acc,
-- all_constraints ac
-- WHERE
-- ac.constraint_name = acc.constraint_name
-- AND ac.constraint_type = 'R'
-- MINUS
-- SELECT
-- table_owner,
-- table_name,
-- column_name,
-- column_position
-- FROM
-- all_ind_columns
-- )
-- GROUP BY
-- acc.owner
-- ORDER BY
-- acc.owner;
--
-- set pagesize 50
-- set heading on
--
-- prompt
-- prompt + Script for details report for non-indexed foregn keys:
-- prompt
-- prompt + set pagesize 50
-- prompt + set heading on
-- prompt
-- prompt + COLUMN owner FORMAT A15 HEADING "Owner"
-- prompt + COLUMN cons FORMAT A20 HEADING "Constraint"
-- prompt + COLUMN tab_name NOPRINT
-- prompt + COLUMN col_name NOPRINT
-- prompt + COLUMN pos NOPRINT
-- prompt + COLUMN cols FORMAT A43 HEADING "Table.Column(Pos)"
-- prompt +
-- prompt + BREAK ON owner SKIP 1
-- prompt
-- prompt + SELECT DISTINCT
-- prompt + acc.owner,
-- prompt + acc.constraint_name cons,
-- prompt + acc.table_name tab_name,
-- prompt + acc.column_name col_name,
-- prompt + acc.position pos,
-- prompt + acc.table_name || '.' ||
-- prompt + acc.column_name || '(' ||
-- prompt + acc.position || ')' cols
-- prompt + FROM
-- prompt + all_cons_columns acc,
-- prompt + all_constraints ac
-- prompt + WHERE
-- prompt + acc.owner not in ('SYS','SYSTEM','PERFSTAT')
-- prompt + AND acc.table_name not like 'MVIEW$%'
-- prompt + AND ac.constraint_name = acc.constraint_name
-- prompt + AND ac.constraint_type = 'R'
-- prompt + AND (acc.owner, acc.table_name, acc.column_name, acc.position) IN
-- prompt + (
-- prompt + SELECT
-- prompt + acc.owner,
-- prompt + acc.table_name,
-- prompt + acc.column_name,
-- prompt + acc.position
-- prompt + FROM
-- prompt + all_cons_columns acc,
-- prompt + all_constraints ac
-- prompt + WHERE
-- prompt + ac.constraint_name = acc.constraint_name
-- prompt + AND ac.constraint_type = 'R'
-- prompt + MINUS
-- prompt + SELECT
-- prompt + table_owner,
-- prompt + table_name,
-- prompt + column_name,
-- prompt + column_position
-- prompt + FROM
-- prompt + all_ind_columns
-- prompt + )
-- prompt + ORDER BY
-- prompt + acc.owner,
-- prompt + acc.table_name,
-- prompt + acc.column_name,
-- prompt + acc.position;
-- prompt
--
-- prompt + Having Unindexed foreign keys can be a performance issue. There are two issues
-- prompt + associated with unindexed foreign keys. The first is the fact that a table
-- prompt + lock will result if you update the parent records primary key (very very
-- prompt + unusual) or if you delete the parent record and the child's foreign key is not
-- prompt + indexed.
-- prompt
-- prompt + The second issue has to do with performance in general of a parent child
-- prompt + relationship. Consider that if you have an on delete cascade and have not
-- prompt + indexed the child table (eg: EMP is child of DEPT. Delete deptno = 10 should
-- prompt + cascade to EMP. If deptno in emp is not indexed -- full table scan). This full
-- prompt + scan is probably undesirable and if you delete many rows from the parent
-- prompt + table, the child table will be scanned once for each parent row deleted.
-- prompt
-- prompt + Also consider that for most (not all, most) parent child relationships, we
-- prompt + query the objects from the 'master' table to the 'detail' table. The glaring
-- prompt + exception to this is a code table (short code to long description). For
-- prompt + master/detail relationships, if you do not index the foreign key, a full scan
-- prompt + of the child table will result.
set pagesize 0
prompt *** BLOCK CONTENTION STATISTICS:
prompt
column inst_id heading 'Inst|ID' format 9999
column class heading 'Class Type' format a20 TRUNC
column count heading 'Times Waited' format 99,999,999,999
column time heading 'Total Waited Time' format 99,999,999,999
column wtp heading 'Total Waited %' format 99.99
BREAK ON inst_id
select
w.inst_id,
w.class,
w.count,
w.time,
round(w.time/t.time*100,2) wtp
from
gv$waitstat w,
(select inst_id,sum(time) time
from gv$waitstat
where count > 0
group by inst_id
) t
where
t.inst_id = w.inst_id
and w.count > 0
order by w.inst_id asc,w.time desc;
prompt
prompt
prompt *** TOP SYSTEM Timed Events (Waits):
prompt
COLUMN event FORMAT A50 HEADING "Wait Event" TRUNC
COLUMN time_waited FORMAT 9999999999999 HEADING "Time|Waited"
COLUMN wait_pct FORMAT 99.90 HEADING "Wait|(%)"
BREAK ON inst_id
SELECT
w.inst_id,
w.event,
w.time_waited,
round(w.time_waited/tw.twt*100,2) wait_pct
FROM
gv$system_event w,
(select inst_id, sum(time_waited) twt
from gv$system_event
where time_waited>0
and event NOT IN
('Null event',
'client message',
'rdbms ipc reply',
'smon timer',
'rdbms ipc message',
'PX Idle Wait',
'PL/SQL lock timer',
'file open',
'pmon timer',
'WMON goes to sleep',
'virtual circuit status',
'dispatcher timer',
'SQL*Net message from client',
'parallel query dequeue wait',
'pipe get')
group by inst_id
) tw
WHERE
w.inst_id = tw.inst_id
and w.time_waited>0
and round(w.time_waited/tw.twt*100,2) > 1
and w.event NOT IN
('Null event',
'client message',
'rdbms ipc reply',
'smon timer',
'rdbms ipc message',
'PX Idle Wait',
'PL/SQL lock timer',
'file open',
'pmon timer',
'WMON goes to sleep',
'virtual circuit status',
'dispatcher timer',
'SQL*Net message from client',
'parallel query dequeue wait',
'pipe get')
ORDER by 1 asc, 4 desc;
prompt
prompt
prompt *** LOCKS HELD IN THE DATABASE:
prompt
prompt
PROMPT +------------------------------------------------------------+
PROMPT |LOCKS DEFINED |
PROMPT | |
PROMPT |RS - Row Share - no exclusive WRITE |
PROMPT |RX - Row eXclusive - no exclusive READ or WRITE |
PROMPT |S - Share - no modifications |
PROMPT |SRX - Share Row eXclusive - no modifications or RX |
PROMPT |X - eXclusive - no access period |
PROMPT | |
PROMPT |TX - Row Locks |
PROMPT |TM - Table Locks |
PROMPT +--------------------------------+-------+---+---+---+---+---+
PROMPT |SQL Statement |Mode |RS |RX |S |SRX|X |
PROMPT | | | | | | | |
PROMPT +--------------------------------+-------+---+---+---+---+---+
PROMPT |SELECT ... FROM table ... |none |Y |Y |Y |Y |Y |
PROMPT +--------------------------------+-------+---+---+---+---+---+
PROMPT |INSERT INTO table ... |none |Y* |Y* |N |N |N |
PROMPT +--------------------------------+-------+---+---+---+---+---+
PROMPT |UPDATE table ... |none |Y* |Y* |N |N |N |
PROMPT +--------------------------------+-------+---+---+---+---+---+
PROMPT |DELETE FROM table ... |none |Y* |Y* |N |N |N |
PROMPT +--------------------------------+-------+---+---+---+---+---+
PROMPT |SELECT ... FROM table ... |none |Y* |Y* |Y* |Y* |N |
PROMPT | FOR UPDATE OF ... | | | | | | |
PROMPT +--------------------------------+-------+---+---+---+---+---+
PROMPT
PROMPT * If no conflicting locks are held by another transaction.
PROMPT Otherwise, waits occur.
PROMPT
PROMPT ...
PROMPT
COLUMN username FORMAT A10 HEADING "Username" WORD_WRAPPED
COLUMN type HEADING "TY"
COLUMN opid FORMAT A10 HEADING "SID/SER"
COLUMN lmode FORMAT A5 HEADING "Lock"
COLUMN lrequest FORMAT A4 HEADING "Req?"
COLUMN obj FORMAT A20 HEADING "Object" WORD_WRAPPED
COLUMN block FORMAT A12 HEADING "Blocking?"
select
ORACLE_USERNAME username,
-- OS_USER_NAME osuser,
-- PROCESS as ospid,
l.SID||'/'||s.serial# opid,
DECODE(l.TYPE
-- Long locks
, 'TM', 'dml/data enq (TM)'
, 'TX', 'transac enq (TX) '
, 'UL', 'pls usr lock (UL)'
-- Short locks
, 'BL', 'buf hash tbl (BL)'
, 'CF', 'control file (CF)'
, 'CI', 'cross inst f (CI)'
, 'CU', 'cursor bind (CU) '
, 'DF', 'data file (CF) '
, 'DL', 'direct load (DL) '
, 'DM', 'mount/strtup (DM)'
, 'DR', 'reco lock (DR) '
, 'DX', 'distrib tran (DX)'
, 'FI', 'sga opn file (FI)'
, 'FS', 'file set (FS) '
, 'IN', 'instance num (IN)'
, 'IR', 'instce recvr (IR)'
, 'IS', 'get state (IS) '
, 'IV', 'libcache inv (IV)'
, 'JQ', 'job queue (JQ) '
, 'KK', 'log sw kick (KK) '
, 'LS', 'log switch (LS) '
, 'MM', 'mount def (MM) '
, 'MR', 'media recvry (MR)'
, 'PF', 'pwfile enq (PF) '
, 'PR', 'process strt (PR)'
, 'RW', 'row wait (RW) '
, 'RT', 'redo thread (RT) '
, 'SC', 'scn enq (SC) '
, 'SM', 'smon lock (SM) '
, 'SN', 'seqno instce (SN)'
, 'SQ', 'seqno enq (SQ) '
, 'ST', 'space transc (ST)'
, 'SV', 'seqno value (SV) '
, 'TA', 'generic enq (TA) '
, 'TD', 'dll enq (TD) '
, 'TE', 'extend seg (TE) '
, 'TS', 'temp segment (TS)'
, 'TT', 'temp table (TT) '
, 'UN', 'user name (UN) '
, 'WL', 'write redo (WL) '
, 'TYPE = ' || l.TYPE) AS type ,
DECODE(l.lmode, 0, 'none', 1, 'null', 2, 'RS', 3, 'RX', 4, 'S', 5, 'SRX', 6,
'EXCL', TO_CHAR(l.lmode)) lmode ,
-- DECODE(l.request, 0, 'none', 1, 'null', 2, 'RS', 3, 'RX', 4, 'S', 5, 'SRX', 6,
-- 'X', TO_CHAR(l.request)) AS lrequest,
decode(BLOCK,
0, 'Not Blocking',
1, 'Blocking',
2, 'Global', block) block,
OWNER||'.'||OBJECT_NAME obj
from sys.v_$locked_object lo, dba_objects do, sys.v_$lock l,
(SELECT a.sid, a.serial#
FROM v$session a, v$bgprocess b
WHERE a.paddr = b.paddr(+)) s
where lo.OBJECT_ID = do.OBJECT_ID
AND l.SID = lo.SESSION_ID
and s.sid = l.sid;
set pagesize 25
set heading on
prompt
prompt *** PL/SQL THAT MAY NEED TO BE CACHED
prompt
col owner for a19
col name for a30 heading "Name" WORD_WRAPPED
select owner, name, sharable_mem,kept
from v$db_object_cache
where sharable_mem >100000
and type in ('PACKAGE','PACKAGE BODY','FUNCTION','PROCEDURE')
and kept ='NO';
prompt
prompt + Use the DBMS_SHARED_POOL package to pin objects
prompt + EXEC SYS.DBMS_SHARED_POOL.KEEP('<owner>','<name>');
prompt
prompt
prompt *** SEGMENT USAGE STATISTICS
prompt
prompt *** Most i/o operation (TOP 5):
prompt
col object_type format a15 heading "Object Type"
col object_name format a27 heading "Object Name"
col statistic_name format a22 heading "Statistic Name"
col value format 99999999999 heading "Value"
SELECT * from (
SELECT object_type, object_name, statistic_name, VALUE
FROM v$segment_statistics
WHERE statistic_name LIKE '%phys%' AND VALUE > 50
ORDER BY 4 DESC
) where rownum < 6;
prompt
prompt *** Most buffer gets (TOP 5):
prompt
SELECT * from (
SELECT object_type, object_name, statistic_name,VALUE
FROM v$segment_statistics
WHERE statistic_name LIKE '%logi%' AND VALUE > 50
ORDER BY 4 DESC
) where rownum < 6;
prompt
prompt + Review the query causing high amount of buffer gets.
prompt + Use StatsPack to detect them.
prompt + Create additional indexes to avoid FULL table scans.
prompt
prompt
prompt *** Most Buffer Busy Waits (TOP 5):
prompt
SELECT * from (
SELECT object_type, object_name, statistic_name,VALUE
FROM v$segment_statistics
WHERE statistic_name LIKE '%buffer%' AND VALUE > 0
ORDER BY 4 DESC
) where rownum < 6;
prompt
set pagesize 0
select
'+ Buffer Busy Wait Ratio: '||round((bbc.total_waits*100/(cg.value+dbg.value)),4)
from
v$system_event bbc, v$sysstat cg, v$sysstat dbg
where
bbc.event='buffer busy waits'
and cg.name ='consistent gets'
and dbg.name ='db block gets';
set pagesize 100
-- prompt
-- prompt + As buffer busy waits are due to contention for particular blocks
-- prompt + then you cannot take any action until you know which blocks are
-- prompt + being competed for and why.
-- prompt + Eliminating the cause of the contention is the best option.
-- prompt + Note that "buffer busy waits" for data blocks are often due to
-- prompt + several processes repeatedly reading the same blocks
-- prompt + (eg: if lots of people scan the same index) -
-- prompt + the first session processes the blocks that are in the buffer cache
-- prompt + quickly but then a block has to be read from disk - the other sessions
-- prompt + (scanning the same index) quickly 'catch up' and want the block which
-- prompt + is currently being read from disk - they wait for the buffer as someone
-- prompt + is already reading the block in.
-- prompt
-- prompt + The following hints may be useful for particular types of contention -
-- prompt + these are things that MAY reduce contention for particular situations:
-- prompt
-- prompt + Block Type and Possible Actions
-- prompt
-- prompt + data blocks
-- prompt + Eliminate HOT blocks from the application. Check for repeatedly
-- prompt + scanned / unselective indexes. Change PCTFREE and/or PCTUSED. Check for
-- prompt + 'right- hand-indexes' (indexes that get inserted into at the same point
-- prompt + by many processes). Increase INITRANS. Reduce the number of rows per block
-- prompt + segment header
-- prompt + Increase of number of FREELISTs. Use FREELIST GROUPs (even in single
-- prompt + instance this can make a difference).
-- prompt + freelist blocks
-- prompt + Add more FREELISTS. In case of Parallel Server make sure that each instance
-- prompt + has its own FREELIST GROUP(s).
-- prompt + undo header
-- prompt + Add more rollback segments.
-- prompt
prompt
prompt *** Most Row Level Locks (TOP 5):
prompt
SELECT * from (
SELECT object_type,object_name, statistic_name,VALUE
FROM v$segment_statistics
WHERE statistic_name LIKE '%row%' AND VALUE > 0
ORDER BY 4 DESC
) where rownum < 6;
prompt
prompt *** Most ITL waits (TOP 5):
prompt
SELECT * from (
SELECT object_type,object_name, statistic_name,VALUE
FROM v$segment_statistics
WHERE statistic_name LIKE '%ITL%' AND VALUE > 0
ORDER BY 4 DESC
) where rownum < 6;
-- prompt
-- prompt + What Is an ITL Wait?
-- prompt + This can occur because either (i) the block is so packed that the ITL
-- prompt + cannot grow to create a free slot, or (ii) the MAXTRANS has already
-- prompt + been reached. In this case, the transaction that needs to lock a row
-- prompt + has to wait until a slot becomes available. This wait is termed as ITL
-- prompt + waits and can be seen from the view v$session_wait, in which the session
-- prompt + is waiting on an event named "enqueue."
-- prompt
-- prompt + The INITTRANS setting controls Initial Transaction Slots (ITLs).
-- prompt + A transaction slot is required for any session that needs to modify a
-- prompt + block in an object. For tables INITRANS defaults to 1 for indexes, 2.
-- prompt
-- prompt + The MAXTRANS setting controls the maximum number of ITLs that a block
-- prompt + can allocate (usually defaults to 255). If a block is sparsely populated
-- prompt + then Oracle will dynamically increase the number of ITLs up to MAXTRANS.
-- prompt
-- prompt + ITL's, INITRANS and Block Waits
-- prompt + However, if the block has little or no free space then transactions will
-- prompt + serialize waiting on a free ITL. This is one cause for data base block waits.
-- prompt + By setting INITRANS to the number of expected simultaneous DML transaction
-- prompt + for a single block, you can avoid serialization for ITL slots.
-- prompt
-- prompt + The maximum value suggested for INITRANS is 100 and settings over this
-- prompt + size rarely improve performance. Therefore a setting of INITRANS to the
-- prompt + average number of simultaneous DML users and setting MAXTRANS to 100 will
-- prompt + most likely result in the best utilization of resources and performance.
-- prompt + Remember, each ITL requires approximately 23 bytes in the block header.
-- prompt
prompt
prompt *** CROSSTAB of object and statistic
prompt
col obj format a20 heading "Object|Name" WORD_WRAP
col itl format 999999 heading "ITL|Waits"
col bbw format 999999999 heading "Buffer|Busy|Waits"
col phr format 999999999999 heading "Physical|Reads"
col lr format 999999999999 heading "Logical|Reads"
col rlw format 999999999 heading "Row|Lock|Waits"
set pages 100
select * from
(
select
DECODE
(GROUPING(a.object_name), 1, 'All Objects', a.object_name)
AS obj,
sum(case when
a.statistic_name = 'ITL waits'
then
a.value else null end) "itl",
sum(case when
a.statistic_name = 'buffer busy waits'
then
a.value else null end) "bbw",
sum(case when
a.statistic_name = 'row lock waits'
then
a.value else null end) "rlw",
sum(case when
a.statistic_name = 'physical reads'
then
a.value else null end) phr,
sum(case when
a.statistic_name = 'logical reads'
then
a.value else null end) lr
from
v$segment_statistics a
where
owner not like 'SYS%'
group by
rollup(a.object_name)) b
where (b."itl">0 or b."bbw">0)
order by 3 desc
;
prompt
prompt + The cross tab report generates a listing showing the statistics of concern
prompt + as headers across the page rather than listings going down the page and
prompt + summarizes them by object. This allows us to easily compare total buffer
prompt + busy waits to the number of ITL or row lock waits. This ability to compare
prompt + the ITL and row lock waits to buffer busy waits lets us see what objects may
prompt + be experiencing contention for ITL lists, which may be experiencing excessive
prompt + locking activity and through comparisons, which are highly contended for
prompt + without the row lock or ITL waits.
prompt
-- prompt + AN example of the output of the report, edited for length, is shown below.
-- prompt
-- prompt + Object ITL Buffer Row Lock Physical Logical
-- prompt + Waits Busy Waits Waits Reads Reads
-- prompt + ************* ********* ********** ********* *********** **********
-- prompt + BILLING 0 63636 38267 1316055 410219712
-- prompt + BILLING_INDX1 1 16510 55 151085 21776800
-- prompt + ...
-- prompt + DELIVER_INDX1 1963 36096 32962 1952600 60809744
-- prompt + DELIVER_INDX2 88 16250 9029 18839481 342857488
-- prompt + DELIVER_PK 2676 99748 29293 15256214 416206384
-- prompt + ...
-- prompt + All Objects 12613 20348859 1253057 1139977207 20947864752
-- prompt +
-- prompt + In the above report the BILLING_INDX1 index has a large amount of buffer
-- prompt + Reads busy waits but we can.t account for them from the ITL or Row lock
-- prompt + waits, this indicates that the index is being constantly read and the blocks
-- prompt + then aged out of memory forcing waits as they are re-read for the next
-- prompt + process. On the other hand, almost all of the buffer busy waits for the
-- prompt + DELIVER_INDX1 index can be attributed to ITL and Row Lock waits.
-- prompt + In situations where there are large numbers of ITL waits we need to consider
-- prompt + the increase of the INITRANS setting for the table to remove this source of
-- prompt + contention. If the predominant wait is row lock waits then we need to
-- prompt + determine if we are properly using locking and cursors in our application
-- prompt + (for example, we may be over using the SELECT.FOR UPDATE type code.)
-- prompt + If, on the other hand all the waits are un-accounted for buffer busy waits,
-- prompt + then we need to consider increasing the amount of database block buffers
-- prompt + we have in our SGA.
-- prompt
prompt
prompt *** SEGMENTS WITH MORE THAN 100 extents
prompt ...
col owner for a13
col segment_name for a22
col segment_type for a18
col segsize for 999999.90 heading "Segment|Size (Mb)"
col segcount for 999999 heading "# of extents"
select
OWNER,
SEGMENT_NAME,
SEGMENT_TYPE,
sum(bytes)/1024/1024 segsize,
count(*) segcount
from
dba_segments
where
owner not in ('SYS','SYSTEM')
group by
OWNER, SEGMENT_NAME, SEGMENT_TYPE
having
count(*) > 100;
prompt
prompt + Segments with high # of extents in dictionary manged tablespace might cause
prompt + performance isssues.
prompt + Use Locally managed tablespaces.
prompt
prompt
prompt *** TABLES with many indexes (>6)
prompt
set pagesize 0
select count(*)||' tables detected with more than 4 indexes'
from
(select owner, table_name, count(*)
from dba_indexes
where owner not in ('SYS','SYSTEM')
group by owner, table_name
having count(*) > 6
);
set pagesize 50
col owner for a30 heading Owner
col table_name for a30 heading "Table Name"
col no for 999 heading "# of indexes"
select
owner,
table_name,
count(*) no
from
dba_indexes
where
owner not in ('SYS','SYSTEM')
group by
owner, table_name
having
count(*) > 6
order by 3 desc;
prompt
prompt + Consider to use index monitoring feature
prompt + Monitoring an Index determine weather the particular index is used or not
prompt + used, so that you can drop un-necessary indexes.
prompt
-- prompt + Enable Monitoring By :
-- prompt + ALTER INDEX <INDEXNAME> MONITORING USAGE ;
-- prompt
--
--
-- prompt + use the following query to generate the script for INDEX MONITORING:
-- prompt
-- prompt + select
-- prompt + ' ALTER INDEX '||owner||'.'||index_name||' MONITORING USAGE;'
-- prompt + from dba_indexes i
-- prompt + where table_name in (select table_name from (
-- prompt + select owner, table_name, count(*)
-- prompt + from dba_indexes
-- prompt + where owner not in ('SYS','SYSTEM')
-- prompt + group by owner, table_name
-- prompt + having count(*) > 6));
-- prompt
-- prompt + Check the monitoring status using "V$OBJECT_USAGE" view (column "USED"):
-- prompt
-- prompt + select * from V$OBJECT_USAGE;
-- prompt
-- prompt + INDEX_NAME TABLE_NAME MON USED START_MONITORING END_MONITORING
-- prompt + *************** ************* *** **** ***************** **************
-- prompt + ....
-- prompt
-- prompt + Run the application for a sufficient (~1 week) time to use the table and
-- prompt + associated indexes. Work with application team to remove unused indexes.
-- prompt
-- prompt + Disable the monitoring By:
-- prompt + ALTER INDEX <INDEXNAME> NOMINTORING USAGE;
-- prompt
prompt
prompt *** SEQUENCE CACHE STATS (listing only non-cached)
prompt
col sequence_owner format a15 heading "Sequence|Owner"
col sequence_name format a30 heading "Sequence Name"
col cache_size format 999999999 heading "Sequnce|cache|size"
col comments format a21 heading "Comments"
select
sequence_owner,
sequence_name,
cache_size,
(case when cache_size < 20
then '- Set cache to min 20'
else 'Ok'
end) comments
from
dba_sequences
where
sequence_owner not in ('SYS','SYSTEM','DBSNMP')
and cache_size = 0
order by 1,2,3;
prompt
prompt On application with high amount of inserts set cache size to min. 40
prompt
prompt
prompt
prompt *** ROLLBACK / UNDO segments statistics
prompt
set pagesize 0
col msg format A50
prompt init parameters:
prompt
select
name||'='||value
from v$parameter
where name like 'undo%'
or name like 'rollback%';
prompt
select (case when value = 'MANUAL' then
'+ '||value ||' UNDO management is used.'||chr(10)||
'+ Recommendation: Enable Automatic UNDO management.' ||chr(10)||
'+ set undo_management=AUTO' ||chr(10)||
'+ undo_tablespace=<tablespace_name>' ||chr(10)||
'+ undo_retention=1800' ||chr(10)||
'+ Additionally the following parameter can be set to suppress '||
'errors while executing'||chr(10)||
'+ manual undo management mode operations such as '||
'.SET TRANSACTION USE ROLLBACK SEGMENT ...'
else '+ Automatic UNDO management is used.'
end) msg
from v$parameter
where name ='undo_management';
set pagesize 50
column "Rollback Segment" format a16
column "Size (Kb)" format 9,999,999
column "Gets" format 999,999,990
column "Waits" format 9,999,990
column "% Waits" format 90.00
column "# Shrinks" format 999,990
column "# Extends" format 999,990
select rn.Name "Rollback Segment", rs.RSSize/1024 "Size (KB)", rs.Gets "Gets",
rs.waits "Waits", (rs.Waits/rs.Gets)*100 "% Waits",
rs.Shrinks "# Shrinks", rs.Extends "# Extends"
from sys.v_$RollName rn, sys.v_$RollStat rs
where rn.usn = rs.usn;
prompt
prompt GETS - # of gets on the rollback segment header
prompt WAITS - # of waits for the rollback segment header
ptompt
set pagesize 0
select 'The average of waits/gets is '||
round((sum(waits) / sum(gets)) * 100,2)||'%'
From v$rollstat;
set pagesize 50
prompt
PROMPT
prompt *** ROLLBACK / UNDO SEGMENTS CONTENTION:
prompt
COLUMN class FORMAT A30 HEADING 'Header Type'
COLUMN count FORMAT 999,999,999,999 HEADING 'Number|of Waits'
COLUMN con_get FORMAT 999,999,999,999,999 HEADING 'Logical|Reads'
COLUMN pct FORMAT 990.99 HEADING 'Percent of|Contention'
SELECT
a.class,
count,
SUM(value) con_get,
((count / SUM(value)) * 100) pct
FROM
v$waitstat a,
v$sysstat b
WHERE
name IN ('db block gets', 'consistent gets')
AND a.class IN (
'system undo header',
'system undo block',
'undo header',
'undo block')
GROUP BY
a.class,
count;
prompt
prompt Another way to gauge rollback contention is:
prompt
column xn1 format 9999999
column xv1 new_value xxv1 noprint
set head on
select class, count
from v$waitstat
where class in ('system undo header', 'system undo block',
'undo header', 'undo block' );
set head off
select 'Total requests = '||sum(count) xn1, sum(count) xv1
from v$waitstat;
select 'Contention for system undo header = '||
(round(count/(&xxv1+0.00000000001),4)) * 100||'%'
from v$waitstat
where class = 'system undo header';
select 'Contention for system undo block = '||
(round(count/(&xxv1+0.00000000001),4)) * 100||'%'
from v$waitstat
where class = 'system undo block';
select 'Contention for undo header = '||
(round(count/(&xxv1+0.00000000001),4)) * 100||'%'
from v$waitstat
where class = 'undo header';
select 'Contention for undo block = '||
(round(count/(&xxv1+0.00000000001),4)) * 100||'%'
from v$waitstat
where class = 'undo block';
prompt
prompt + If the percentage for an area is more than 1% or 2%, consider
prompt + creating more rollback/undo segments.
prompt
set head on
prompt
prompt
prompt *** UNDO USAGE STATISTICS:
prompt
col BeginT format a6 heading "Begin|Time" WORD_WRAPPED
col EndT format a6 heading "End|Time" WORD_WRAPPED
col UDBPS format 9999 heading "UNDO|Blk|Per|Sec"
col T1 format 9999999999 heading "Total|Undo|Blocks|Used"
col T2 format 9999999999999 heading "Total|Num|Trans|Executed"
col T3 format 999999999 heading "Longest|Query|(in secs)"
col T4 format 999999 heading "Highest|Conc TX|Count"
col T5 format 99999 heading "# of|ORA|01555"
col T6 format 99999 heading "# of|Space|Err"
select TO_CHAR(MIN(Begin_Time),'DDMMYYHH24MISS') BeginT,
TO_CHAR(MAX(End_Time),'DDMMYYHH24MISS') EndT,
ceil(sum(undoblks)/((max(end_time)-min(begin_time))*24*60*60)) UDBPS,
SUM(Undoblks) T1,
SUM(Txncount) T2,
MAX(Maxquerylen) T3,
MAX(Maxconcurrency) T4,
SUM(Ssolderrcnt) T5,
SUM(Nospaceerrcnt) T6
from V$UNDOSTAT;
prompt
prompt
prompt *** UNDO TS size estimation:
prompt
prompt + Undo Space in Bytes = (UR * UDBPS * DB_Block Size) + Overhead
prompt + where
prompt + UR = Undo Retention Time in Seconds
prompt + UDBPS = Undo Blocks used Per Second
prompt + Overhead = One DB block for metadata
prompt
prompt Current UNDO tablespace(s):
prompt
set pagesize 0
select '+ UNDO tablespace: '||ts||'. Size: '||sz||'MB'
from
(
select
t.tablespace_name ts, ROUND(sum(bytes)/1024/1024,2) sz
from
dba_tablespaces t,
dba_data_files f
where
f.tablespace_name = t.tablespace_name
and t.contents= 'UNDO'
group by
t.tablespace_name
);
prompt
prompt The following size estimation is based on the current DB usage.
prompt
SELECT '+ Estimated UNDO TS size for undo_retention='||rd||' (current) is '||trunc((rd * (udbps * overhead) + overhead)/1024/1024)||'MB'
FROM
(SELECT value AS RD
FROM v$parameter
WHERE name = 'undo_retention'),
(SELECT (sum (undoblks) /
sum ( ((end_time - begin_time) * 86400))) as UDBPS
FROM v$undostat),
(SELECT value AS OVERHEAD
FROM v$parameter
WHERE name = 'db_block_size');
prompt
prompt
prompt + On Oracle 10g use Undo Advisor PL/SQL package to estimate the UNDO size:
prompt + set serveroutput on;
prompt + declare utbsize_in_MB number;
prompt + begin
prompt + utbsize_in_MB := DBMS_UNDO_ADV.RBU_MIGRATION;
prompt + dbms_output.put_line(TO_CHAR(utbsize_in_MB));
prompt + end;
prompt + /
prompt
prompt
prompt ROLLBACK SEGMENTS USAGE BY ACTIVE TRANSACTIONS
prompt ...
set pagesize 50
COLUMN name FORMAT A20 HEADING "Rollback Segment"
COLUMN sid FORMAT 990 HEADING "Session|ID"
COLUMN username FORMAT A16 HEADING "Oracle|Username"
COLUMN osuser FORMAT A16 HEADING "O/S|Username"
COLUMN program FORMAT A16 HEADING "Program"
SELECT
r.name,
l.sid,
s.username,
s.osuser,
s.program
FROM
v$rollname r,
v$session s,
v$lock l
WHERE
r.usn = TRUNC(l.id1 / 65536)
AND l.sid = s.sid
AND l.type = 'TX'
ORDER BY
r.name;
prompt
prompt *** MISSING STATISTICS ?
prompt
set pagesize 0
select 'optimizer_mode='||value from v$parameter where name = 'optimizer_mode';
prompt
select (case when value = 'RULE' then '+ '||value ||
' based optimizer is used. Ignore the statistics,'||chr(10)||
'+ ... unless you are not using SQL hints invoking COST based optimizer.'
else '+ Cost based optimizer is used. Make sure to keep the statistics '||
'uptodate.'
end)
from v$parameter where name = 'optimizer_mode';
set pagesize 50
column schema_name format a19
column clusters_with format 99999999 heading "CLUSTERS|WITH|STATS"
column tables_with format 99999999 heading "TABLES|WITH|STATS"
column indexes_with format 99999999 heading "INDEXES|WITH|STATS"
column clusters_without format 99999999 heading "CLUSTERS|WITHOUT|STATS"
column tables_without format 99999999 heading "TABLES|WITHOUT|STATS"
column indexes_without format 99999999 heading "INDEXES|WITHOUT|STATS"
select
u.name schema_name,
nvl(c.stats, 0) clusters_with,
t.stats tables_with,
nvl(i.stats, 0) indexes_with,
nvl(c.total - c.stats, 0) clusters_without,
t.total - t.stats tables_without,
nvl(i.total - i.stats, 0) indexes_without
from
( select
o1.owner#,
count(c1.avgchn) stats,
count(*) total
from
sys.clu$ c1,
sys.obj$ o1
where
c1.obj# = o1.obj#
group by
o1.owner#
) c,
( select
o2.owner#,
count(t2.rowcnt) stats,
count(*) total
from
sys.tab$ t2,
sys.obj$ o2
where
t2.obj# = o2.obj# and
t2.tab# is null
group by
o2.owner#
) t,
( select
o3.owner#,
count(i3.blevel) stats,
count(*) total
from
sys.ind$ i3,
sys.obj$ o3
where
i3.obj# = o3.obj# and
i3.type# between 1 and 7
group by
o3.owner#
) i,
sys.user$ u
where
u.user# = t.owner# and
u.user# = c.owner# (+) and
u.user# = i.owner# (+)
order by
t.total - t.stats
+ nvl(c.total - c.stats, 0)
+ nvl(i.total - i.stats, 0);
prompt
prompt Schemas detected with stats older than 30 days:
prompt
set pagesize 50
col owner for a20 heading "Schema"
col no for 999999 heading "# of objects"
select owner, sum(no) no
from
(
select owner, trunc(last_analyzed) dt, count(*) no
from dba_tables
where owner not in ('SYS','SYSTEM')
group by owner, trunc(last_analyzed)
)
where
dt < sysdate - 30
group by owner
order by 2 desc, 1 asc;
prompt
prompt + Use only DBMS_STATS package to gather statistics.
prompt
prompt + Ex.: exec DBMS_STATS.GATHER_schema_stats (ownname => '<schemaname>',
prompt + options => 'GATHER',
prompt + cascade => TRUE,
prompt + estimate_percent => 30,
prompt + degree => 4);
prompt
prompt + ! Avoid using (estimate_percent => dbms_stats.auto_sample_size)
prompt
prompt + Recommendation: Save database statistics on regularly basis (once a month)
prompt + using:
prompt + dbms_stats.create_stat_table('<owner>','<stats_tab_name>');
prompt + dbms_stats.export_schema_stats('<owner>','<stats_tab_name>','<owner>');
prompt
prompt
prompt *** TABLESPACE FREE SPACE report:
prompt
COLUMN ts HEADING "Tablespace Name" FORMAT A25
COLUMN msg HEADING "Tablespace Name" FORMAT A27
COLUMN megs_alloc HEADING "Alloc|(Mbytes)" FORMAT 999999
COLUMN megs_used HEADING "Used |(Mbytes)" FORMAT 999999
COLUMN Pct_Free HEADING "% Free" FORMAT 999.9
SELECT a.tablespace_name ts,
ROUND(a.bytes_alloc / 1024 / 1024, 2) megs_alloc,
-- ROUND(NVL(b.bytes_free, 0) / 1024 / 1024, 2) megs_free,
ROUND((a.bytes_alloc - NVL(b.bytes_free, 0)) / 1024 / 1024, 2) megs_used,
ROUND((NVL(b.bytes_free, 0) / a.bytes_alloc) * 100,2) Pct_Free,
(case when ROUND((NVL(b.bytes_free, 0) / a.bytes_alloc) * 100,2)<=0
then 'Immediate action required!'
when ROUND((NVL(b.bytes_free, 0) / a.bytes_alloc) * 100,2)<5
then 'Critical (<5% free)'
when ROUND((NVL(b.bytes_free, 0) / a.bytes_alloc) * 100,2)<15
then 'Warning (<15% free)'
when ROUND((NVL(b.bytes_free, 0) / a.bytes_alloc) * 100,2)<25
then 'Warning (<25% free)'
when ROUND((NVL(b.bytes_free, 0) / a.bytes_alloc) * 100,2)>60
then 'Waste of space? (>60% free)'
else 'OK'
end) msg
FROM ( SELECT f.tablespace_name,
SUM(f.bytes) bytes_alloc,
SUM(DECODE(f.autoextensible, 'YES',f.maxbytes,'NO', f.bytes)) maxbytes
FROM DBA_DATA_FILES f
GROUP BY tablespace_name) a,
( SELECT f.tablespace_name,
SUM(f.bytes) bytes_free
FROM DBA_FREE_SPACE f
GROUP BY tablespace_name) b
WHERE a.tablespace_name = b.tablespace_name (+)
UNION
SELECT h.tablespace_name,
ROUND(SUM(h.bytes_free + h.bytes_used) / 1048576, 2),
-- ROUND(SUM((h.bytes_free + h.bytes_used) - NVL(p.bytes_used, 0)) / 1048576, 2),
ROUND(SUM(NVL(p.bytes_used, 0))/ 1048576, 2),
ROUND((SUM((h.bytes_free + h.bytes_used) - NVL(p.bytes_used, 0)) / SUM(h.bytes_used + h.bytes_free)) * 100,2),
(case when ROUND((SUM((h.bytes_free + h.bytes_used) - NVL(p.bytes_used, 0)) / SUM(h.bytes_used + h.bytes_free)) * 100,2)<=0 then 'Immediate action required!'
when ROUND((SUM((h.bytes_free + h.bytes_used) - NVL(p.bytes_used, 0)) / SUM(h.bytes_used + h.bytes_free)) * 100,2)<5 then 'Critical (<5% free)'
when ROUND((SUM((h.bytes_free + h.bytes_used) - NVL(p.bytes_used, 0)) / SUM(h.bytes_used + h.bytes_free)) * 100,2)<15 then 'Warning (<15% free)'
when ROUND((SUM((h.bytes_free + h.bytes_used) - NVL(p.bytes_used, 0)) / SUM(h.bytes_used + h.bytes_free)) * 100,2)<25 then 'Warning (<25% free)'
when ROUND((SUM((h.bytes_free + h.bytes_used) - NVL(p.bytes_used, 0)) / SUM(h.bytes_used + h.bytes_free)) * 100,2)>60 then 'Waste of space? (>60% free)'
else 'OK'
end) msg
FROM sys.V_$TEMP_SPACE_HEADER h, sys.V_$TEMP_EXTENT_POOL p
WHERE p.file_id(+) = h.file_id
AND p.tablespace_name(+) = h.tablespace_name
GROUP BY h.tablespace_name
ORDER BY 1;
PROMPT
PROMPT *** TABLESPACE FRAGMENTATION REPORT (ts with more than 100 free chunks) (TOP 5)
PROMPT
col tsname format A31 heading "Tablespace Name"
col totsize format 999999.990 heading "Total|size (Mb)"
col smsize format 999999.990 heading "Smalles|chunk (Kb)"
col bigsize format 999999.990 heading "Biggest|chunk (Mb)"
col nochunk format 999999 heading "# of free|space chunks"
select * from
(
select
tablespace_name tsname,
round(sum(bytes)/1024/1024,2) totsize,
round(min(bytes)/1024,2) smsize,
round(max(bytes)/1024/0124,2) bigsize,
count(*) nochunk
from dba_free_space
where tablespace_name not in (select tablespace_name
from dba_tablespaces
where contents = 'UNDO')
group by tablespace_name
having count(*)>100
order by 3 desc
)
where rownum < 6;
prompt
prompt + Tablespace fragmentation is not a performance problem, but merely
prompt + a waste of space problem.
prompt
prompt
prompt *** TABLESPACE analysis
prompt
set pagesize 50
col tablespace_name for a21 heading "Tablespace Name"
col bs for a4 heading "Bl|Size"
col contents for a4 heading "Cont|ents" TRUNC
col log for a4 heading "Logg|ing"
col extent_management for a6 heading "Extent|Mgmt" TRUNC
col allocation_type for a7 heading "Alloc"
col ssm for A5 heading "ASSM?"
col status for A6 heading "Status" TRUNC
col fno for 99999 heading "Total|Files"
col fsize for 999.99 heading "Total|Size|(GB)"
select
t.tablespace_name,
block_size/1024||'k' bs,
t.status,
contents,
decode(logging,'LOGGING','Yes','No') log,
extent_management,
allocation_type,
decode(segment_space_management,'AUTO','Yes','No') ssm,
count(*) fno,
round(sum(bytes)/1024/1024/1024,2) fsize
from
dba_tablespaces t,
dba_data_files d
where t.tablespace_name = d.tablespace_name
group by
t.tablespace_name,
block_size/1024||'k',
t.status,
contents,
decode(logging,'LOGGING','Yes','No'),
extent_management,
allocation_type,
decode(segment_space_management,'AUTO','Yes','No')
order by
contents,
tablespace_name;
prompt
prompt + Localy Manged (9i and 10g) tablespaces and ASSM (10g) are recommended
prompt
prompt
set pagesize 0
select (case when count(*)=0 then '+ Temporary Tablespace not defined!'
else '+ Temporary tablespace: '||max(tablespace_name)
end)
from dba_tablespaces
where contents = 'TEMPORARY'
group by tablespace_name;
select (case when count(*)=0 then '+ No temp files defined! TEMP files have to be'||
' used for TEMP tablespaces.'
else '+ Tablespace '||max(tablespace_name)||' has '||count(*)||' temp files.'
end)
from dba_temp_files
group by tablespace_name;
prompt
prompt *** Datafile status
prompt
set pagesize 50
col message for a70
col status for a15 heading "File status"
col enabled for a15 heading "Enabled mode"
col bs for a10 heading "Block size"
col dfno for 99999 heading "# Files"
select status, enabled, block_size/1024||'k' bs, count(*) dfno
from v$datafile
group by status, enabled, block_size/1024||'k'
order by block_size/1024||'k', status, enabled;
prompt
prompt *** Tempfile status
prompt
select status, enabled, block_size/1024||'k' bs, count(*) dfno
from v$tempfile
group by status, enabled, block_size/1024||'k'
order by block_size/1024||'k', status, enabled;
set pagesize 0
PROMPT
PROMPT *** I/O DISTRIBUTION BY TABLESPACE (TOP 5)
PROMPT
VARIABLE n_rd NUMBER
VARIABLE n_wr NUMBER
SET TERMOUT OFF
BEGIN
SELECT
SUM(phyblkrd),
SUM(phyblkwrt)
INTO
:n_rd,
:n_wr
FROM
v$filestat
;
END;
/
DEFINE nameln = 30
DEFINE devsep = "GREATEST(0, -
INSTR(df.name, '/', -1), -
INSTR(df.name, ':', -1), -
INSTR(df.name, '\', -1))"
DEFINE device = "SUBSTR(df.name, 1, &&devsep)"
DEFINE filenm = "SUBSTR(df.name, &&devsep + 1)"
SET TERMOUT ON
set echo off
set pagesize 100
COLUMN name FORMAT A22 HEADING "Name"
COLUMN tsize FORMAT 9,990 HEADING "GBytes"
COLUMN reads FORMAT 999999,990 HEADING "Reads|(1000s)"
COLUMN readp FORMAT 999 HEADING "Read| %" JUSTIFY RIGHT
COLUMN writs FORMAT 999999,990 HEADING "Writes|(1000s)"
COLUMN writp FORMAT 990 HEADING "Wri-|te %" JUSTIFY RIGHT
COLUMN total FORMAT 999999,990 HEADING "Total|(1000s)"
COLUMN totlp FORMAT 990 HEADING "Tot-|al %" JUSTIFY RIGHT
SELECT * FROM
(
SELECT
MAX(ts.name) name,
SUM(df.bytes / (1024 * 1024 * 1024)) tsize,
SUM(st.phyblkrd) / 1000 reads,
SUM(st.phyblkrd) / (:n_rd) * 100 readp,
SUM(st.phyblkwrt) / 1000 writs,
SUM(st.phyblkwrt) / (:n_wr) * 100 writp,
SUM(st.phyblkrd + st.phyblkwrt) / 1000 total,
SUM(st.phyblkrd + st.phyblkwrt) / (:n_rd + :n_wr) * 100 totlp
FROM
v$datafile df,
v$filestat st,
sys.file$ sf,
sys.ts$ ts
WHERE
df.file# = st.file#
AND df.file# = sf.file#
AND sf.ts# = ts.ts#
GROUP BY
ts.name
ORDER BY 8 DESC
)
WHERE ROWNUM < 6;
PROMPT
PROMPT *** I/O DISTRIBUTION BY DEVICE (TOP 5)
PROMPT
SELECT * FROM
(
SELECT
MAX(&&device) name,
SUM(df.bytes / (1024 * 1024 * 1024)) tsize,
SUM(st.phyblkrd) / 1000 reads,
SUM(st.phyblkrd) / (:n_rd) * 100 readp,
SUM(st.phyblkwrt) / 1000 writs,
SUM(st.phyblkwrt) / (:n_wr) * 100 writp,
SUM(st.phyblkrd + st.phyblkwrt) / 1000 total,
SUM(st.phyblkrd + st.phyblkwrt) / (:n_rd + :n_wr) * 100 totlp
FROM
v$datafile df,
v$filestat st,
sys.file$ sf,
sys.ts$ ts
WHERE
df.file# = st.file#
AND df.file# = sf.file#
AND sf.ts# = ts.ts#
GROUP BY
&&device
ORDER BY 8 DESC
)
where rownum <6;
PROMPT
PROMPT *** I/O DISTRIBUTION BY FILE (TOP 5)
PROMPT
SELECT * FROM
(
SELECT
MAX(df.name) name,
SUM(df.bytes / (1024 * 1024 * 1024)) tsize,
SUM(st.phyblkrd) / 1000 reads,
SUM(st.phyblkrd) / (:n_rd) * 100 readp,
SUM(st.phyblkwrt) / 1000 writs,
SUM(st.phyblkwrt) / (:n_wr) * 100 writp,
SUM(st.phyblkrd + st.phyblkwrt) / 1000 total,
SUM(st.phyblkrd + st.phyblkwrt) / (:n_rd + :n_wr) * 100 totlp
FROM
v$datafile df,
v$filestat st,
sys.file$ sf,
sys.ts$ ts
WHERE
df.file# = st.file#
AND df.file# = sf.file#
AND sf.ts# = ts.ts#
GROUP BY
df.name
ORDER BY 8 DESC
)
where rownum <6;
set pagesize 0
col namevalue format a79 heading "Name" WORD_WRAPPED
set pagesize 0
col namevalue format a79 heading "Name" WORD_WRAPPED
prompt
prompt *** RMAN
prompt
select
'CONFIGURE '||name ||' '|| value namevalue
from
v$rman_configuration
order by
conf#;
set pagesize 50
prompt
prompt *** DATABASE ACCOUNTS report:
prompt
col USERNAME format a15 heading "Username" WORD_WRAPPED
col STATUS format a10 heading "Account|Status" WORD_WRAPPED
col PROFILE format a27 heading "Profile" WORD_WRAPPED
col CREATED format a8 heading "Created|Time"
col PWD_CHANGE_TIME format a8 heading "Pwd|Last|Time|Chg"
col DAYS_BEFORE_EXPIRE format 99999 heading "Days|Before|Expire"
select
u.name AS "USERNAME",
decode(d.account_status,
'EXPIRED(GRACE)','EXP(GRACE)',
d.account_status) AS "STATUS",
d.profile AS "PROFILE",
to_char(u.ctime,'DD/MM/YY') AS "CREATED",
to_char(u.ptime,'DD/MM/YY') AS "PWD_CHANGE_TIME",
(case when trunc(u.ptime + days - sysdate) < 0
then 0
else trunc(u.ptime + days - sysdate)
end) AS "DAYS_BEFORE_EXPIRE"
from
(select name,ptime,ctime from sys.user$) u,
(select username,profile,account_status from sys.dba_users) d,
(select profile, decode(limit,'UNLIMITED',99999,'DEFAULT',99999,limit) "DAYS"
from sys.dba_profiles where resource_name = 'PASSWORD_LIFE_TIME') p
where u.name=d.username
-- and u.name not in (select username from dba_users where profile like '%GDBA%')
and d.profile = p.profile
order by 6 desc,2,3,1;
prompt
prompt *** SECURITY (roles and privileges) checking:
prompt
set pagesize 50
set recsep off
col grantee for a20 heading "Grantee"
col granted_role for a20 heading "Granted Role"
col privilege for a20 heading "Granted Privilege"
col admin_option for a10 heading "Admin|Option?"
col default_role for a10 heading "Default|Role?"
select
grantee,
granted_role,
admin_option,
default_role
from
dba_role_privs
where
granted_role in ('DBA','EXP_FULL_DATABASE','IMP_FULL_DATABASE','OEM_MONITOR')
and
grantee not in ('SYS','SYSTEM','DBA')
order by 1;
select
grantee,
privilege,
admin_option
from
dba_sys_privs
where privilege in
('ALTER DATABASE',
'ALTER PROFILE',
'ALTER SYSTEM',
'ALTER TABLESPACE',
'BECOME USER',
'BACKUP ANY TABLE',
'ALTER USER',
'CREATE ANY DIRECTORY',
'CREATE DATABASE',
'CREATE ROLLBACK SEGMENT',
'CREATE TABLESPACE',
'DROP ANY DIRECTORY',
'DROP DATABASE',
'DROP PROFILE',
'DROP ROLLBACK SEGMENT',
'DROP TABLESPACE',
'DROP USER',
'EXECUTE ANY PROCEDURE',
'GRANT ANY OBJECT PRIVILEGE',
'GRANT ANY PRIVILEGE',
'GRANT ANY ROLE',
'INSERT ANY TABLE',
'MANAGE TABLESPACE',
'UNDER ANY TABLE',
'UNDER ANY TYPE',
'UNDER ANY VIEW',
'UPDATE ANY TABLE',
'ALTER ANY PROCEDURE',
'ALTER ANY ROLE',
'ALTER ANY TABLE',
'ALTER ANY TRIGGER',
'ALTER ANY TYPE',
'DELETE ANY TABLE',
'DROP ANY PROCEDURE',
'DROP ANY ROLE',
'DROP ANY TABLE',
'FORCE ANY TRANSACTION',
'FORCE TRANSACTION',
'ALTER ROLLBACK SEGMENT',
'CREATE USER',
'CREATE PUBLIC DATABASE LINK',
'RESTRICTED SESSION',
'SELECT ANY TABLE')
and grantee not in
('SYS',
'SYSTEM',
'DBA',
'IMP_FULL_DATABASE',
'EXP_FULL_DATABASE')
order by 1;
prompt
prompt
prompt *** PROFILES PASSWORD AGING SETTINGS:
prompt
set pagesize 100
col profile format a24 heading "Profile Name"
col resource_name format a24 heading "Resource Name"
col limit format a18 heading "Limit"
col com format a10 heading "Comment"
BREAK ON profile SKIP 1
SELECT
profile,
resource_name,
limit,
(case when limit = 'UNLIMITED' or limit = 'DEFAULT'
then 'Unsecured?'
end) com
FROM
sys.dba_profiles
WHERE
resource_name In ('FAILED_LOGIN_ATTEMPTS',
'PASSWORD_LIFE_TIME',
'PASSWORD_REUSE_TIME',
'PASSWORD_REUSE_MAX',
'PASSWORD_VERIFY_FUNCTION',
'PASSWORD_LOCK_TIME',
'PASSWORD_GRACE_TIME')
ORDER BY
profile,
resource_name;
-- prompt
-- prompt + Standard GDBA password aging profiles:
-- prompt +++++++++++++++++++++++++++++++++++++++++++++
-- prompt + create PROFILE GDBA_USER LIMIT
-- prompt + PASSWORD_LIFE_TIME 90
-- prompt + PASSWORD_GRACE_TIME 10
-- prompt + PASSWORD_REUSE_TIME 1800
-- prompt + PASSWORD_REUSE_MAX UNLIMITED
-- prompt + FAILED_LOGIN_ATTEMPTS 5
-- prompt + PASSWORD_LOCK_TIME 1/24
-- prompt + PASSWORD_VERIFY_FUNCTION verify_function;
-- prompt +++++++++++++++++++++++++++++++++++++++++++++
-- prompt + create PROFILE HP_GDBA_ADMIN LIMIT
-- prompt + PASSWORD_LIFE_TIME 45
-- prompt + PASSWORD_GRACE_TIME 10
-- prompt + PASSWORD_REUSE_TIME 1800
-- prompt + PASSWORD_REUSE_MAX UNLIMITED
-- prompt + FAILED_LOGIN_ATTEMPTS 5
-- prompt + PASSWORD_LOCK_TIME 1/24
-- prompt + PASSWORD_VERIFY_FUNCTION verify_function12;
-- prompt +++++++++++++++++++++++++++++++++++++++++++++
-- prompt + Create PROFILE HP_APP_SERVICE LIMIT
-- prompt + PASSWORD_LIFE_TIME 365
-- prompt + PASSWORD_GRACE_TIME 10
-- prompt + PASSWORD_REUSE_TIME 1800
-- prompt + PASSWORD_REUSE_MAX UNLIMITED
-- prompt + FAILED_LOGIN_ATTEMPTS 5
-- prompt + PASSWORD_LOCK_TIME 1/24
-- prompt + PASSWORD_VERIFY_FUNCTION verify_function12;
-- prompt +++++++++++++++++++++++++++++++++++++++++++++
-- prompt
-- prompt + More info in "Database Security - User Account Setup.doc" on GDBA share
-- prompt
prompt
prompt *** NLS settings
prompt
COLUMN "Parameter" FORMAT A25
COLUMN "Database Value" FORMAT A20
COLUMN "Instance Value" FORMAT A15
COLUMN "Session Value" FORMAT A15
SELECT
d.parameter "Parameter",
d.value "Database Value",
i.value "Instance Value",
s.value "Session Value"
FROM
nls_database_parameters d,
nls_instance_parameters i,
nls_session_parameters s
WHERE
d.parameter = i.parameter(+)
AND d.parameter = s.parameter(+);
prompt
prompt + Make sure you are using the correct NLS_LANG when using RMAN, exp/imp
prompt
set pagesize 0
prompt
prompt *** CURRENT SESSION DETAILS:
prompt
SELECT
'SID: ' || s.sid || CHR(10) ||
'Serial#: ' || s.serial# || CHR(10) ||
'Process: ' || s.process || CHR(10) ||
'SPID: ' || p.spid || CHR(10) ||
'OS User: ' || s.osuser || CHR(10) ||
'Oracle User: ' || s.username || CHR(10) ||
'Machine: ' || s.machine || CHR(10) ||
'Terminal: ' || s.terminal || CHR(10) ||
'Logon Time: ' || TO_CHAR(s.logon_time, 'DD-MON-RR HH24:MI')
FROM
v$session s,
v$process p
WHERE
audsid = USERENV('SESSIONID')
AND s.paddr = p.addr
and lower(substr(s.program,1,7)) = 'sqlplus';
prompt
select 'Timestamp: '||to_char(sysdate,'Mon dd yyyy hh24:mi:ss') from dual;
prompt
prompt
prompt *** END of healthcheck report
spool off
select
'+ Script Outp saved to: healthcheck_&&dbname×tamp&&suffix ' "Output script"
from dual;
prompt
prompt + If you run this script on your Windows PC the output will be saved into
prompt + %ORACLE_HOME%\bin
prompt
rem Oracle Database Healthcheck script
rem
rem Note: In order to analyze the alert.log the script is creating a directory for bdump
rem
rem The report should be run after the system has been up for at least 10 hours
rem and should be run on several occasions over a period of time to get a feel for
rem what the real condition of the database is. A one-time sample run on an
rem intactive system will not give an accurate picture of what is really occuring
rem within the database.
rem
rem
undefine all
ttitle off
whenever sqlerror continue
set echo off
set feedback off
set term off
column timecol new_value timestamp
column spool_extension new_value suffix
select to_char(sysdate,'Mondd_hh24mi') timecol,
'.out' spool_extension from sys.dual;
column outp new_value dbname
select value || '_' outp
from v$parameter where name = 'db_name';
spool healthcheck_&&dbname×tamp&&suffix
SET LINESIZE 80
SET ECHO OFF
SET NEWPAGE 1
SET HEADING OFF
SET TERMOUT ON
SET NUMWIDTH 9
SET VERIFY OFF
SET FEEDBACK OFF
SET PAGESIZE 58
SET RECSEP OFF
COLUMN nl NEWLINE
prompt
prompt *** Database Health Check ver 2.0
prompt
BEGIN
SYS.DBMS_APPLICATION_INFO.set_module ('HealthCheck', NULL);
END;
/
select 'Timestamp: '||to_char(sysdate,'Mon dd yyyy hh24:mi:ss') from dual;
prompt
select
' RAC database? : '||DECODE(vp1.value,'TRUE','Yes ('||
decode(vp1.value,'TRUE',' instances: '||vp2.value)||')','No') ||chr(10)||
' Current instance: '||instance_name
from
v$instance,
(select value
from v$parameter
where name like 'cluster_database'
) vp1,
(select value
from v$parameter
where name like 'cluster_database_instances'
) vp2;
prompt
prompt *** INSTANCE DETAILS:
prompt
select
' instance id : '||inst_id ||' ( '||instance_name||
' on ' ||host_name ||' )' ||chr(10)||
' inst status : '||status ||chr(10)||
' db status : '||database_status ||chr(10)||
' active state : '||active_state ||chr(10)||
' shutd pending?: '||shutdown_pending ||chr(10)||
' startup time : '||to_char(startup_time,'dd/mm/yyyy hh24:mi:ss')||chr(10)||
' uptime (days) : '||trunc(sysdate-startup_time+1) ||chr(10)||
' version : '||version ||chr(10)||
' logins : '||logins ||chr(10)||
' instance role : '||instance_role ||chr(10)||
' archiver? : '||archiver
from gv$instance
order by inst_id;
prompt
SET PAGESIZE 0
col msg for a80
select
(case when trunc(sysdate-startup_time+1) < 2
then '+ Database was recently restarted.'||chr(10)||
'+ The inormation about wait events and hit ratios might be not useful'
end) msg
from v$instance;
prompt
prompt *** DATABASE DETAILS:
prompt
COLUMN sort1 NOPRINT
SELECT
1 sort1,' Database name : '||name ||chr(10)||
' Database id : '||dbid ||chr(10)||
' Created : '||to_char(created,
'dd/mm/yyyy hh24:mi:ss') ||chr(10)||
' Log mode : '||log_mode ||
decode (log_mode,'ARCHIVELOG','',' !!!') ||chr(10)||
' Force logging : '||force_logging ||chr(10)||
' Open mode : '||open_mode ||chr(10)||
' Remote archiving : '||remote_archive ||chr(10)||
' Database role : '||database_role
FROM v$database
UNION
SELECT
2 sort1,' Datafiles : '||trim(TO_CHAR(COUNT(*),'9,990'))||chr(10)||
' Data f. size (Gb): '||trim(TO_CHAR(SUM(bytes)/
1073741824, '9,990'))
FROM v$datafile
UNION
SELECT
3 sort1,' Tempfiles : '||trim(TO_CHAR(COUNT(*),'9,990'))||chr(10)||
' Temp f. size (Gb): '||trim(TO_CHAR(SUM(bytes)/
1073741824, '9,990'))
FROM v$tempfile
UNION
SELECT
4 sort1,' Segm size (Gb) : '||trim(TO_CHAR(SUM(bytes)/
1073741824, '9,990'))
FROM dba_segments
UNION
SELECT
5 sort1,' Tables / Indexes : '||
trim(TO_CHAR(SUM(DECODE(type#, 2, 1, 0)), '999,990'))||'/'||
trim(TO_CHAR(SUM(DECODE(type#, 1, 1, 0)), '999,990'))
FROM sys.obj$
WHERE owner# <> 0
UNION
SELECT
6 sort1,' Total DB Users : ' ||trim( TO_CHAR(COUNT(*), '9,990'))
FROM sys.user$
WHERE type# = 1
UNION
SELECT
7 sort1,' Online Sessions : ' ||trim( TO_CHAR(COUNT(*), '9,990'))
FROM gv$session
WHERE type='USER'
UNION
SELECT
8 sort1,' Active Sessions : ' ||trim( TO_CHAR(COUNT(*), '9,990'))
FROM gv$session
WHERE type='USER' and status = 'ACTIVE'
UNION
SELECT
9 sort1,' Session highwater: ' ||
trim(TO_CHAR(sessions_highwater, '9,990'))
FROM
v$license
UNION
SELECT
10 sort1,' SGA (Mb) : '||trim(TO_CHAR(SUM(value)/
1048576, '99,990.99'))
FROM
v$sga;
SET PAGESIZE 50
set head on
prompt
prompt *** Database components
prompt
col comp_name for a40 heading "Component Name"
col version for a12 heading "Version"
col status for a15 heading "Status"
select
COMP_NAME,
VERSION,
STATUS
from
dba_registry;
prompt
prompt
prompt *** Current SESSION workload:
prompt
col inst_id format 9999 heading "Inst|ID"
col status format a20 heading "Session status"
col sno format 9999999999 heading "# sessions"
BREAK ON inst_id
select inst_id,status, count(*) sno
from gv$session
where type='USER'
group by inst_id,status
order by 1,2;
prompt
prompt *** DATABASE RESOURCE USAGE
prompt
col inst_id format 9999 heading "Inst|ID"
col resource_name format a22 heading "Resource|Name"
col current_utilization format 9999999999 heading "Curret|Utilization"
col max_utilization format 9999999999 heading "Max|Utilization"
col initial_allocation format a18 heading "Initial|Allocation"
col mpr format a4 heading "Max %|reached"
BREAK ON inst_id
select
inst_id,
resource_name,
current_utilization,
max_utilization,
initial_allocation
from
gv$resource_limit
where
max_utilization > 0
order by
inst_id,
resource_name;
prompt
prompt *** CONTROLFILES location and mirroring
prompt
COLUMN num FORMAT 99 HEADING '#'
COLUMN name FORMAT a60 HEADING 'Location' WORD_WRAPPED
COLUMN status FORMAT a7 HEADING 'Status'
SELECT
rownum num,
name,
status
FROM
v$controlfile;
prompt
set pagesize 0
select (case
when count(1) <2 then '+ At least 2 controlfiles are recommended'
when count(1) >=2 and count(1) <=3 then '+ '||count(1)||' mirrors for controlfile detected. - OK'
else '+ More than 3 controlfiles might have additional overhead. Check the wait events.'
end)
from v$controlfile;
col msg format a79
select
(case when value <45 then '+ ! "control_file_record_keep_time='||value||'" to low. Set to at least 45'
else '+ "control_file_record_keep_time='||value||'" - OK.'
end) msg
from v$parameter where name = 'control_file_record_keep_time';
set pagesize 50
set heading on
prompt
prompt *** REDO LOGs location, size and mirroring
prompt
COLUMN thread# FORMAT 9 HEADING "Th"
COLUMN group# FORMAT 90 HEADING "Group"
COLUMN sequence# FORMAT 999999999990 HEADING "Sequence|Number"
COLUMN mbytes FORMAT 9999990 HEADING "Size|(Mb)"
COLUMN members FORMAT 90 HEADING "Members"
COLUMN first_time FORMAT A15 HEADING "First|Time"
COLUMN status FORMAT A12 HEADING "Status" TRUNC
COLUMN archived FORMAT A3 HEADING "Arc"
SELECT
thread#,
group#,
sequence#,
bytes / 1024 / 1024 mbytes,
TO_CHAR(first_time, 'DD-MON-YY HH24:MI') first_time,
status,
archived
FROM
v$log
ORDER BY
thread#,
group#;
-- prompt
-- prompt + STATUS Description
-- prompt + ********** ***********************************************************
-- prompt + UNUSED Indicates the online redo log has never been written to.
-- prompt + This is the state of a redo log that was just added, or just
-- prompt + after a RESET-LOGS, when it is not the current redo log.
-- prompt + CURRENT Indicates this is the current redo log. This implies that
-- prompt + the redo log is active. The redo log could be open or closed.
-- prompt + ACTIVE Indicates the log is active but is not the current log. It is
-- prompt + needed for instance recovery. It might or might not be
-- prompt + archived.
-- prompt + CLEARING Indicates the log is being recreated as an empty log after
-- prompt + an ALTER DATABASE CLEAR LOGFILE command. After the log is
-- prompt + cleared, the status changes to UNUSED.
-- prompt + CLEARING Indicates that the current log is being cleared of a closed
-- prompt + _CURRENT Thread. The log can stay in this status if there is some
-- prompt + failure in the switch such as an I/O error writing the new
-- prompt + log header.
-- prompt + INACTIVE Indicates the log is no longer needed for instance recovery.
-- prompt + It may be in use for media recovery. It might or might not be
-- prompt + archived.
-- prompt + INVALID Thread. File is inaccessible.
-- prompt + STALE File contents are incomplete (such as when an instance is shut
-- prompt + down with SHUTDOWN ABORT or due to a system crash).
-- prompt + DELETED File is no longer used.
prompt
set pagesize 0
select 'Group# '||group#||': '||
(case
when members<2 then '+ Redo log mirroring is recommended'
else ' '||members||' members detected. - OK'
end)
from v$log
where members < 2;
prompt
select (case
when count(*) > 1 then '+ Groups with different size detected. - Not recommended.'
else '+ All groups have the same size. - OK'
end)
from (select distinct bytes from v$log);
select
(case
when count(*)>3 then '+ '||count(*)||' times detected when log switches occured more than 1 log per 5 minutes.'||chr(10)||
'+ You may consider to increase the redo log size.'
else '+ Redo log size: OK'
end)
from (
select trunc(FIRST_TIME,'HH') Week, count(*) arch_no, trunc(10*count(*)/60) archpermin
from v$log_history
group by trunc(FIRST_TIME,'HH')
having trunc(5*count(*)/60)>1
);
prompt
prompt *** REDO CONTENTION STATISTICS:
prompt
prompt The following shows how often user processes had to wait for space in
prompt the redo log buffer:
set head off
select name||' = '||value
from v$sysstat
where name = 'redo log space requests';
set head on
prompt
prompt This value should be near 0. If this value increments consistently,
prompt processes have had to wait for space in the redo buffer. If this
prompt condition exists over time, increase the size of LOG_BUFFER in the
prompt init.ora file in increments of 5% until the value nears 0.
prompt ** NOTE: increasing the LOG_BUFFER value will increase total SGA size.
prompt
prompt
prompt Another way to check the contention on redo log buffer:
prompt
prompt The following query output shows the system waits on
prompt 'log file sync' and 'db file parallel write'
prompt
select (case when sync_waits = 0 then '+ Waits for log file sync not detected: log_buffer is OK'
else '+ Waits for log file sync detected ('||sync_waits||' times): Consider to increase the log_buffer'
end)
from (
select
decode(
sum(w.total_waits),
0, 0,
nvl(100 * sum(l.total_waits) / sum(w.total_waits), 0)
) sync_waits
from
sys.v_$bgprocess b,
sys.v_$session s,
sys.v_$session_event l,
sys.v_$session_event w
where
b.name like 'DBW_' and
s.paddr = b.paddr and
l.sid = s.sid and
l.event = 'log file sync' and
w.sid = s.sid and
w.event = 'db file parallel write'
);
prompt
prompt
prompt *** REDO LOG SWITCHING ACTIVITY (last 30 days):
prompt
set pagesize 0
select '+ Daily (max) : '||max(no)||' switches. Size: '||round((max(no) * max(logsize ))/1024,2)||'Gb'||chr(10)||
'+ Daily (avg) : '||trunc(avg(no))||' switches. Size: '||round((avg(no) * max(logsize ))/1024,2)||'Gb'||chr(10)||
'+ Daily (min) : '||min(no)||' switches. Size: '||round((min(no) * max(logsize ))/1024,2)||'Gb'
from (
select trunc(FIRST_TIME,'DD'), count(*) no
from v$log_history
where FIRST_TIME > sysdate - 31
group by trunc(FIRST_TIME,'DD')),
(select max(bytes/1024/1024) logsize from v$log);
prompt
select '+ Weekly (max): '||max(no)||' switches. Size: '||round((max(no) * max(logsize ))/1024,2)||'Gb'||chr(10)||
'+ Weekly (avg): '||trunc(avg(no))||' switches. Size: '||round((avg(no) * max(logsize ))/1024,2)||'Gb'||chr(10)||
'+ Weekly (min): '||min(no)||' switches. Size: '||round((min(no) * max(logsize ))/1024,2)||'Gb'
from (
select trunc(FIRST_TIME,'WW'), count(*) no
from v$log_history
where FIRST_TIME > sysdate - 31
group by trunc(FIRST_TIME,'WW')),
(select max(bytes/1024/1024) logsize from v$log);
set pagesize 50
prompt
prompt List redo log switches for the last 24 hours per hour:
prompt
col Hour format a20 heading "Hour"
col switches format 999999 heading "# of redo|switches"
select to_char(trunc(FIRST_TIME,'HH'),'dd-MON-yy hh24')||'h' Hour, count(*) switches
from v$log_history
where FIRST_TIME > (sysdate-1) - 1/24
group by to_char(trunc(FIRST_TIME,'HH'),'dd-MON-yy hh24')||'h'
order by 1;
prompt
prompt
prompt *** SGA DETAILS:
prompt
set pagesize 50
COLUMN inst_id format 9999 HEADING "Inst|id"
COLUMN name FORMAT A25 HEADING "Part"
COLUMN value FORMAT 99999999999999990 HEADING "Bytes"
COLUMN k FORMAT 999999999990.0 HEADING "Kbytes"
COLUMN m FORMAT 9999990.0 HEADING "Mbytes"
BREAK ON inst_id
COMPUTE SUM LABEL Total OF m ON inst_id
SET HEADING ON
SELECT
inst_id,
name,
value,
value / 1024 k,
value / 1024 /1024 m
FROM
GV$SGA
order by 1;
prompt
prompt + !!! BUG alert:
prompt + !!! ORA-600 error might occurs with instance crash when the parameter
prompt + !!! SGA_TARGET is set to an exact multiple of 4Gb due to Bug 4433838.
prompt
prompt
prompt *** SGA HIT RATIOS:
set pagesize 0
COLUMN val2 NEW_VALUE lib NOPRINT
SELECT (1 - (SUM(reloads) / SUM(pins)))*100 val2
FROM v$librarycache;
COLUMN val2 NEW_VALUE dict NOPRINT
SELECT (1 - (SUM(getmisses) / SUM(gets)))*100 val2
FROM v$rowcache;
COLUMN val2 NEW_VALUE phys_reads NOPRINT
SELECT value val2
FROM v$sysstat
WHERE name = 'physical reads';
COLUMN val2 NEW_VALUE log1_reads NOPRINT
SELECT value val2
FROM v$sysstat
WHERE name = 'db block gets';
COLUMN val2 NEW_VALUE log2_reads NOPRINT
SELECT value val2
FROM v$sysstat
WHERE name = 'consistent gets';
COLUMN val2 NEW_VALUE chr NOPRINT
SELECT (1 - (&phys_reads / (&log1_reads + &log2_reads)))*100 val2
FROM DUAL;
SET HEADING OFF
SELECT
' Data Block Buffer Hit Ratio (%): ' || &chr || chr(10) ||
' Shared SQL Pool' || chr(10) ||
' Dictionary Hit Ratio (%): ' || &dict || chr(10) ||
' Shared SQL Buffers (Library Cache)' || chr(10) ||
' Cache Hit Ratio (%): ' || &lib
FROM DUAL;
set pagesize 45
SET HEADING OFF
prompt
PROMPT *** LIBRARY CACHE STATISTICS:
prompt
select 'Instance id: '||inst_id,
'PINS - # of times an item in the library cache was executed - '||
sum(pins),
'RELOADS - # of library cache misses on execution steps - '||
sum (reloads),
'RELOADS / PINS * 100 = '||round((sum(reloads) / sum(pins) * 100),2)||'%'
from gv$librarycache
group by 'Instance id: '||inst_id
order by 1;
prompt
prompt + Increase memory until RELOADS is near 0 but watch out for
prompt + Paging/swapping
prompt + To increase library cache, increase SHARED_POOL_SIZE
prompt +
prompt + ** NOTE: Increasing SHARED_POOL_SIZE will increase the SGA size.
prompt +
prompt + Library Cache Misses indicate that the Shared Pool is not big
prompt + enough to hold the shared SQL area for all concurrently open cursors.
prompt + If you have no Library Cache misses (PINS = 0), you may get a small
prompt + increase in performance by setting CURSOR_SPACE_FOR_TIME = TRUE which
prompt + prevents ORACLE from deallocating a shared SQL area while an
prompt + application cursor associated with it is open.
prompt
SET HEADING ON
prompt
prompt *** CACHE HIT RATIO per Buffer Pool:
prompt
set pagesize 0
COLUMN val1 NEW_VALUE defblsize NOPRINT
select
to_number(value)/1024 val1
from
v$parameter
where
name like 'db_block_size';
set heading on
set pagesize 25
col pool FORMAT A20 HEADING "Buffer pool"
col ratio FORMAT 999.90 HEADING "Hit Ratio (%)"
col comment FORMAT A40 HEADING "Comment"
select name||'.'||block_size/1024||'k' pool,
100-round((physical_reads/(db_block_gets+consistent_gets+0.001))*100,2) as ratio,
(case when (100-round((physical_reads/(db_block_gets+consistent_gets+0.001))*100,2)) >90
then 'OK' else 'Consider to increase db_'||DECODE(block_size/1024,&defblsize,'',block_size/1024||'k_')||'cache_size' end) "comment"
from v$buffer_pool_statistics;
prompt
prompt *** FREE MEMORY AVAILABLE IN THE SGA:
col pool format A20
col name format A20
col sgas heading "Allocated|SGA (Mb)"
col frees heading "Free|(Kb)"
col freep heading "%|Free"
select pool,
name,
round(sgasize/1024/1024,2) sgas,
round(bytes/1024,2) frees,
round(bytes/sgasize*100, 2) freep
from (select sum(bytes) sgasize from sys.v_$sgastat) s, sys.v_$sgastat f
where f.name = 'free memory';
prompt
prompt *** MEMORY INIT PARAMETERS:
prompt
column inst_id heading "Inst|id" format 9999
column param heading "Parameter" format a50
BREAK ON inst_id
select
inst_id,
rpad(name,25,' ')||'= '||nvl(value ,0) param
from
gv$parameter
where
name like 'db%cache_size'
or name like 'log_buffer'
or name like '%pool_size'
or name like 'db_block_buffers'
or name like 'db_block_size'
or name like 'sga_max_size'
or name like 'sga_target'
or name like 'pga_aggregate_target'
order by inst_id, param;
set heading off
prompt
select 'Default block size : db_block_size = '||to_number(value)/1024||'k' from v$parameter where name like 'db_block_size'
union
select 'Default buffer pool: db_cache_size = '||value from v$parameter where name like 'db_cache_size'
union
select 'Other buffer pools : '||name||' = '||value from v$parameter where name <> 'db_cache_size' and name like 'db%k_cache_size' and value <>'0';
prompt
select
'+ Buffer Cache Advisory is: '||value||chr(10)||
DECODE (value,'ON','+ Review the buff cache advisory below and adjust the memory settings if needed',
'OFF','+ Enable the Buffer Cache Advisory by setting init parameter ''db_cache_advice=ON''',
'READY','+ The Buffer Cache Advisor is READY. Please switch it ON using'||chr(10)||
' ALTER SYSTEM SET DB_CACHE_ADVICE = ON')
from v$parameter where name like 'db_cache_advice';
prompt
prompt *** Buffer Pool Advisory:
prompt
select 'Instance ID: '||inst_id||' Buffer pool: '||pool||' status='||status from (
select inst_id,name||' '||block_size/1024||'k' pool, advice_status status, count(*)
from gV$DB_CACHE_ADVICE
group by inst_id,name||' '||block_size/1024||'k', advice_status)
order by 1;
column inst_id heading "Inst|id" format 9999
column block_size heading "Block|Size" format 999999
column size_factor heading 'Size Factor' format 99.90
column c1 heading 'Cache Size (m)' format 999,999,999,999
--column c2 heading 'Buffers' format 999,999,999
column c3 heading 'Estd Phys|Read Factor' format 999.90
column c4 heading 'Estd Phys| Reads' format 999999999999999
BREAK ON inst_id
set heading on
select name||'.'||block_size/1024||'k' pool,
100-round((physical_reads/(db_block_gets+consistent_gets+0.001))*100,2) as ratio,
(case when (100-round((physical_reads/(db_block_gets+consistent_gets+0.001))*100,2)) >90
then 'OK' else 'Consider to increase db_'||DECODE(block_size/1024,&defblsize,'',block_size/1024||'k_')||'cache_size' end) "comment"
from v$buffer_pool_statistics;
select
inst_id,
block_size,
size_factor,
size_for_estimate c1,
-- buffers_for_estimate c2,
estd_physical_read_factor c3,
estd_physical_reads c4
from
gv$db_cache_advice
where
name = 'DEFAULT'
and
advice_status = 'ON'
order by 1,2,3;
prompt
set pagesize 0
select
'+ Instance# '||s1.inst_id||
' "db_'||DECODE(s1.block_size/1024,&defblsize,'',s1.block_size/1024||'k_')||
'cache_size"='||s1.size_for_estimate||'MB'||chr(10)||
'+ Increasing "db_'||DECODE(s1.block_size/1024,&defblsize,'',s1.block_size/1024||'k_')||
'cache_size" to '||se.size_for_estimate||
' MB may reduce disk read factor to '||se.estd_physical_read_factor
from
(
select inst_id,block_size,size_factor,size_for_estimate,estd_physical_read_factor
from gv$db_cache_advice
where name = 'DEFAULT'
and advice_status = 'ON'
and size_factor = 1
) s1,
(
select sa.inst_id,sa.block_size,sa.size_factor,sa.size_for_estimate,sa.estd_physical_read_factor
from
(
select inst_id,block_size,size_factor,size_for_estimate,estd_physical_read_factor
from gv$db_cache_advice
where name = 'DEFAULT'
and advice_status = 'ON'
) sa
,
(select inst_id,block_size,max(size_factor) sfm
from gv$db_cache_advice
where name = 'DEFAULT' and advice_status = 'ON'
group by inst_id,block_size
) sm
where sa.inst_id = sm.inst_id
and sa.block_size = sm.block_size
and sa.size_factor = sm.sfm
) se
where se.inst_id = s1.inst_id
and se.block_size = s1.block_size
and se.estd_physical_read_factor < 0.90
order by s1.block_size, s1.inst_id;
set pagesize 50
prompt
prompt *** SORT Statistics:
prompt
COLUMN r FORMAT A10 HEADING "Sorted|Rows"
COLUMN m FORMAT A10 HEADING "Memory|Sorts"
COLUMN d FORMAT A7 HEADING "Disk|Sorts"
COLUMN pct FORMAT 990.00 HEADING "Disk|%"
COLUMN comm FORMAT A26 HEADING "Comment"
COLUMN p FORMAT A14 HEADING "SORT_AREA_SIZE"
SELECT
TO_CHAR(r.value) r,
TO_CHAR(m.value) m,
TO_CHAR(d.value) d,
DECODE(m.value, 0, 1, d.value / m.value * 100) pct,
DECODE(SIGN(DECODE(m.value, 0, 1, d.value / m.value) - 0.05),
1, 'Increase SORT_AREA_SIZE / PGA',
'OK') comm
--, p.value p
FROM
v$sysstat r,
v$sysstat m,
v$sysstat d,
v$parameter p
WHERE
r.name = 'sorts (rows)'
AND m.name = 'sorts (memory)'
AND d.name = 'sorts (disk)'
AND p.name = 'sort_area_size';
prompt
prompt
prompt *** PGA statistics:
prompt
set pagesize 0
select '+ Automatic SQL Execution Memory Management is '||decode (UPPER(value),'AUTO','ENABLED','DISABLED')||chr(10)||
'+ workarea_size_policy='||UPPER(value) from v$parameter where name = 'workarea_size_policy'
union all
select '+ pga_aggregate_target='||value from v$parameter where name = 'pga_aggregate_target';
select decode(UPPER(value),'MANUAL', '+ Enable Automatic SQL Execution Memory Management as described below:','')
from v$parameter where name = 'workarea_size_policy';
-- prompt
-- prompt + Optimization of the PGA memory structures could be very time consuming
-- prompt + depending on the type of operations the system was performing.
-- prompt + Oracle9i allows to leave configuration of the PGA up to Oracle by setting two
-- prompt + initialization parameters:
-- prompt + WORKAREA_SIZE_POLICY = AUTO
-- prompt + PGA_AGGREGATE_TARGET = ?????K
-- prompt
-- prompt + Actually the Automatic SQL Memory Management feature can be enabled by setting
-- prompt + only the parameter PGA_AGGREGATE_TARGET to a non-zero value.
-- prompt + The WORKAREA_SIZE_POLICY parameter tell the server that it should take over
-- prompt + PGA memory management. The PGA_AGGREGATE_TARGET parameter specifies the total
-- prompt + amount of memory the server can allocate to the PGA.
prompt + Oracle quote the following equations as a base for calculating the value
prompt + of this parameter:
prompt + PGA_AGGREGATE_TARGET = (TOTAL_MEM * 80%) * 20% for an OLTP system
prompt + PGA_AGGREGATE_TARGET = (TOTAL_MEM * 80%) * 50% for a DSS system
prompt + Where TOTAL_MEMORY is the total available memory for the system.
prompt + When the Automatic SQL Execution Memory Management feature is enabled, any
prompt + *_AREA_SIZE settings are ignored.
prompt
prompt *** DISPLAY PGA TARGET ADVICE:
prompt
-- prompt + Besides SGA, the Oracle database also assigns each server process a private
-- prompt + memory region called the .Program Global Area. (PGA). A PGA is created for
-- prompt + each server process when it is started (i.e. when a new session is initiated
-- prompt + while using the dedicate server configuration or when a new shared server
-- prompt + process is created). It contains data and control information for a server
-- prompt + process and, unlike the SGA, each server process has exclusive access to its
-- prompt + PGA.
-- prompt + Although the actual content of the PGA varies depending on whether a process
-- prompt + is running in dedicated or shared mode, it can generally be categorized as
-- prompt + follows:
-- prompt + o Session Memory: The memory allocated to hold session.s logon information
-- prompt + and other such details. For shared server processes, this information is
-- prompt + stored in the SGA.
-- prompt + o SQL Execution Memory: The memory allocated on behalf of SQL statements
-- prompt + being executed. The SQL Execution Memory has a persistent and a run time
-- prompt + area
-- prompt + o The persistent area is allocated when a cursor is opened for SQL
-- prompt + execution. It contains the information that persists across multiple
-- prompt + execution of the same statement (i.e. cursor) such as bind details,
-- prompt + data type conversion, etc. The persistent area is de-allocated when the
-- prompt + cursor is closed. For shared server processes, the persistent area is a
-- prompt + part of the SGA.
-- prompt + o The runtime area contains information used while a SQL statement is
-- prompt + being executed. Its size depends on the type and complexity of SQL
-- prompt + statement as well as the number and size of rows being processed. It is
-- prompt + de-allocated once the execution completes. For shared sever processes,
-- prompt + the run time area is resides in the PGA for DML/DDL operation and in
-- prompt + the SGA for queries. For complex queries, such as those used for
-- prompt + reporting purposes or as ad hoc queries in a data-warehousing
-- prompt + environment, a large portion of the run time SQL execution memory is
-- prompt + dedicated as the working area for operations such as sort, hash-join
-- prompt + and bitmap-merge.
-- prompt + Larger working areas can significantly improve the performance of these
-- prompt + operations and thereby, reduce the response time of queries. Ideally, the size
-- prompt + of work area should be large enough such that it can accommodate all input
-- prompt + data and auxiliary structure needed by the operation.
column p1 heading 'Target(M)'
column p2 heading 'Estimated|Cache Hit %'
column p3 heading 'Estimated|Over-Alloc.'
set heading on
set pagesize 50
BREAK ON inst_id
SELECT
inst_id,
ROUND(pga_target_for_estimate /(1024*1024)) p1,
PGA_TARGET_FACTOR "Size Factor",
estd_pga_cache_hit_percentage p2,
estd_overalloc_count p3
FROM
gv$pga_target_advice
order by 1 asc, 2 asc;
prompt
prompt + The following output shows us how many operations, or executions, were
prompt + performed in the work areas. These executions fall into three categories.
prompt + The optimal executions are those operations that were performed entirely in
prompt + memory. As the name suggests, this is the most favorable type of execution.
prompt + If the operation was too big to be performed in memory, then part of the
prompt + operation spills onto disk. If only one pass was needed on disk, then this
prompt + execution is noted in the onepass statistic. If more than one pass was needed
prompt + on disk, then this execution is noted in the multipass statistic. Ideally,
prompt + all executions should be in the optimal statistic and the statistics for
prompt + onepass and multipass should be zero. If the # of multipass executions is
prompt + high consider increasing the value of pga_aggregate_target parameter.
prompt
col name for a35
select
inst_id,
name,
value
from
gv$sysstat
where name like 'workarea executions%'
order by 1 asc, 2 desc;
set heading off
col msg for a50
select (case when value>1000 then
'+ You may need to increase pga_aggregate_target for inst #'||inst_id||chr(10)||
'+ Check the PGA Advisor for suggested size'
else '+ pga_agregate_target is OPTIMAL for inst #'||inst_id
end) msg
from gv$sysstat
where name like 'workarea executions - multipass';
prompt
prompt
prompt *** DISPLAY SHARED POOL ADVICE:
prompt
set pages 999
column s1 heading 'Pool |Size(M)'
column s2 heading 'Size|Factor'
column s3 heading 'Est|LC(M) '
column s4 heading 'Est LC|Mem. Obj.'
column s5 heading 'Est|Time|Saved|(sec)'
column s6 heading 'Est|Parse|Saved|Factor'
column s7 heading 'Est|Object Hits' format 999,999,999,999
prompt + This advisory gives advice on library cache memory and predicts the effect of
prompt + altering the shared pool size on the total amount parsing activities in the
prompt + system.
prompt + The following view displays information about estimated parse timesavings in
prompt + different sizes of shared pool. The sizes range from 50% to 200% of current
prompt + shared pool size, in equal intervals. The value of the interval depends on
prompt + current shared pool size. Parse Time Saved refers to the amount of time saved
prompt + by keeping library cache memory objects in the shared pool, as opposed to
prompt + having to reload these objects.
set heading on
BREAK ON inst_id
SELECT
inst_id,
shared_pool_size_for_estimate s1,
shared_pool_size_factor s2,
estd_lc_size s3,
estd_lc_memory_objects s4,
-- estd_lc_time_saved s5,
estd_lc_time_saved_factor s6,
estd_lc_memory_object_hits s7
FROM
gv$shared_pool_advice
order by 1,3;
PROMPT
PROMPT *** LIST ORACLE JOBS (Check for broken jobs here)
PROMPT
COLUMN jid FORMAT 9999 HEADING 'Id'
COLUMN subu FORMAT A10 HEADING 'Submitter' TRUNC
COLUMN secd FORMAT A10 HEADING 'Security' TRUNC
COLUMN proc FORMAT A19 HEADING 'Job' WORD_WRAPPED
COLUMN lsd FORMAT A5 HEADING 'Last|Ok|Date'
COLUMN lst FORMAT A5 HEADING 'Last|Ok|Time'
COLUMN nrd FORMAT A5 HEADING 'Next|Run|Date'
COLUMN nrt FORMAT A5 HEADING 'Next|Run|Time'
COLUMN fail FORMAT 999 HEADING 'Errs'
COLUMN ok FORMAT A3 HEADING 'Ok?'
SELECT
job jid,
log_user subu,
priv_user secd,
substr(what,1,50) proc,
TO_CHAR(last_date, 'MM/DD') lsd,
SUBSTR(last_sec, 1, 5) lst,
TO_CHAR(next_date, 'MM/DD') nrd,
SUBSTR(next_sec, 1, 5) nrt,
failures fail,
DECODE(broken, 'Y', 'No', 'Yes') ok
FROM
sys.dba_jobs;
PROMPT
PROMPT *** INVALID DATABASE OBJECTS:
PROMPT
COLUMN owner FORMAT A16 HEADING "Object Owner"
COLUMN object_name FORMAT A30 HEADING "Object Name"
COLUMN object_type FORMAT A25 HEADING "Object Type"
COLUMN last_time FORMAT A18 HEADING "Last Change Time"
COLUMN no FORMAT 99999 HEADING "# of Invalid|Objects"
prompt
prompt SUMMARY:
SELECT
owner,
object_type,
count(*) no
FROM
dba_objects
WHERE
status = 'INVALID'
GROUP BY
owner,
object_type
ORDER BY
1,2;
PROMPT
PROMPT LISTING ONLY SYS INVALID OBJECTS:
PROMPT ...
PROMPT
col owner for a10 heading "Owner"
col object_name for a33 heading "Object Name"
col object_type for a15 heading "Object Type"
col last_ddl_time for a18 heading "Last DDL Time"
SELECT
owner,
object_type,
object_name,
TO_CHAR(last_ddl_time, 'DD-MON-YY hh:mi:ss') last_ddl_time
FROM
dba_objects
WHERE
status = 'INVALID'
and owner in ('SYS','SYSTEM')
ORDER BY
owner,
object_type,
object_name;
PROMPT
PROMPT *** NON-SYSTEM objects in SYSTEM tablespace:
PROMPT
COLUMN owner FORMAT A30 HEADING "Object Owner"
COLUMN segment_type FORMAT A22 HEADING "Object Type"
COLUMN no FORMAT 99999 HEADING "# of Objects"
set pagesize 0
SELECT
'+ '||count(*)||' NON-SYSTEM objects detected in SYSTEM tablespace with '||
'total size: '||NVL(round(sum(bytes)/1024/1024,2),0)||'Mb'
FROM
dba_segments
WHERE
tablespace_name = 'SYSTEM'
and owner not in ('SYS','SYSTEM','CSMIG','OUTLN');
set pagesize 50
SELECT
owner,
segment_type,
count(*) no
FROM
dba_segments
WHERE
tablespace_name = 'SYSTEM'
and owner not in ('SYS','SYSTEM','CSMIG','OUTLN')
GROUP BY
owner,
segment_type
ORDER BY
1,2;
prompt
prompt + Run $ORACLE_HOME/rdbms/admin/utlrp.sql to recompile invalid objects (if any)
prompt + Make sure that all SYS and SYSTEM objects are valid
prompt + Address the application invaild objects to the application support team
prompt
set heading off
-- PROMPT
-- PROMPT *** NON-INDEXED FOREIGN KEYS:
-- PROMPT
-- prompt + Lists foreign keys that are missing indexes on the foreign key
-- prompt + columns in the child table.
--
-- col msg for a70
--
-- SELECT (case when count(*)>0 then '+ '||count(*) ||' non-indexes foreign key detected.'
-- else '+ Non-indexes foreign key not detected. OK.'
-- end) msg
-- FROM
-- all_cons_columns acc,
-- all_constraints ac
-- WHERE
-- acc.owner not in ('SYS','SYSTEM','PERFSTAT')
-- AND acc.table_name not like 'MVIEW$%'
-- AND ac.constraint_name = acc.constraint_name
-- AND ac.constraint_type = 'R'
-- AND (acc.owner, acc.table_name, acc.column_name, acc.position) IN
-- (
-- SELECT
-- acc.owner,
-- acc.table_name,
-- acc.column_name,
-- acc.position
-- FROM
-- all_cons_columns acc,
-- all_constraints ac
-- WHERE
-- ac.constraint_name = acc.constraint_name
-- AND ac.constraint_type = 'R'
-- MINUS
-- SELECT
-- table_owner,
-- table_name,
-- column_name,
-- column_position
-- FROM
-- all_ind_columns
-- );
--
-- set pagesize 25
-- set heading on
--
-- COLUMN owner FORMAT A20 HEADING "Owner"
-- COLUMN no FORMAT 999999 HEADING "# of non-indexed FK"
-- SELECT
-- acc.owner,
-- count(*) no
-- FROM
-- all_cons_columns acc,
-- all_constraints ac
-- WHERE
-- acc.owner not in ('SYS','SYSTEM','PERFSTAT')
-- AND acc.table_name not like 'MVIEW$%'
-- AND ac.constraint_name = acc.constraint_name
-- AND ac.constraint_type = 'R'
-- AND (acc.owner, acc.table_name, acc.column_name, acc.position) IN
-- (
-- SELECT
-- acc.owner,
-- acc.table_name,
-- acc.column_name,
-- acc.position
-- FROM
-- all_cons_columns acc,
-- all_constraints ac
-- WHERE
-- ac.constraint_name = acc.constraint_name
-- AND ac.constraint_type = 'R'
-- MINUS
-- SELECT
-- table_owner,
-- table_name,
-- column_name,
-- column_position
-- FROM
-- all_ind_columns
-- )
-- GROUP BY
-- acc.owner
-- ORDER BY
-- acc.owner;
--
-- set pagesize 50
-- set heading on
--
-- prompt
-- prompt + Script for details report for non-indexed foregn keys:
-- prompt
-- prompt + set pagesize 50
-- prompt + set heading on
-- prompt
-- prompt + COLUMN owner FORMAT A15 HEADING "Owner"
-- prompt + COLUMN cons FORMAT A20 HEADING "Constraint"
-- prompt + COLUMN tab_name NOPRINT
-- prompt + COLUMN col_name NOPRINT
-- prompt + COLUMN pos NOPRINT
-- prompt + COLUMN cols FORMAT A43 HEADING "Table.Column(Pos)"
-- prompt +
-- prompt + BREAK ON owner SKIP 1
-- prompt
-- prompt + SELECT DISTINCT
-- prompt + acc.owner,
-- prompt + acc.constraint_name cons,
-- prompt + acc.table_name tab_name,
-- prompt + acc.column_name col_name,
-- prompt + acc.position pos,
-- prompt + acc.table_name || '.' ||
-- prompt + acc.column_name || '(' ||
-- prompt + acc.position || ')' cols
-- prompt + FROM
-- prompt + all_cons_columns acc,
-- prompt + all_constraints ac
-- prompt + WHERE
-- prompt + acc.owner not in ('SYS','SYSTEM','PERFSTAT')
-- prompt + AND acc.table_name not like 'MVIEW$%'
-- prompt + AND ac.constraint_name = acc.constraint_name
-- prompt + AND ac.constraint_type = 'R'
-- prompt + AND (acc.owner, acc.table_name, acc.column_name, acc.position) IN
-- prompt + (
-- prompt + SELECT
-- prompt + acc.owner,
-- prompt + acc.table_name,
-- prompt + acc.column_name,
-- prompt + acc.position
-- prompt + FROM
-- prompt + all_cons_columns acc,
-- prompt + all_constraints ac
-- prompt + WHERE
-- prompt + ac.constraint_name = acc.constraint_name
-- prompt + AND ac.constraint_type = 'R'
-- prompt + MINUS
-- prompt + SELECT
-- prompt + table_owner,
-- prompt + table_name,
-- prompt + column_name,
-- prompt + column_position
-- prompt + FROM
-- prompt + all_ind_columns
-- prompt + )
-- prompt + ORDER BY
-- prompt + acc.owner,
-- prompt + acc.table_name,
-- prompt + acc.column_name,
-- prompt + acc.position;
-- prompt
--
-- prompt + Having Unindexed foreign keys can be a performance issue. There are two issues
-- prompt + associated with unindexed foreign keys. The first is the fact that a table
-- prompt + lock will result if you update the parent records primary key (very very
-- prompt + unusual) or if you delete the parent record and the child's foreign key is not
-- prompt + indexed.
-- prompt
-- prompt + The second issue has to do with performance in general of a parent child
-- prompt + relationship. Consider that if you have an on delete cascade and have not
-- prompt + indexed the child table (eg: EMP is child of DEPT. Delete deptno = 10 should
-- prompt + cascade to EMP. If deptno in emp is not indexed -- full table scan). This full
-- prompt + scan is probably undesirable and if you delete many rows from the parent
-- prompt + table, the child table will be scanned once for each parent row deleted.
-- prompt
-- prompt + Also consider that for most (not all, most) parent child relationships, we
-- prompt + query the objects from the 'master' table to the 'detail' table. The glaring
-- prompt + exception to this is a code table (short code to long description). For
-- prompt + master/detail relationships, if you do not index the foreign key, a full scan
-- prompt + of the child table will result.
set pagesize 0
prompt *** BLOCK CONTENTION STATISTICS:
prompt
column inst_id heading 'Inst|ID' format 9999
column class heading 'Class Type' format a20 TRUNC
column count heading 'Times Waited' format 99,999,999,999
column time heading 'Total Waited Time' format 99,999,999,999
column wtp heading 'Total Waited %' format 99.99
BREAK ON inst_id
select
w.inst_id,
w.class,
w.count,
w.time,
round(w.time/t.time*100,2) wtp
from
gv$waitstat w,
(select inst_id,sum(time) time
from gv$waitstat
where count > 0
group by inst_id
) t
where
t.inst_id = w.inst_id
and w.count > 0
order by w.inst_id asc,w.time desc;
prompt
prompt
prompt *** TOP SYSTEM Timed Events (Waits):
prompt
COLUMN event FORMAT A50 HEADING "Wait Event" TRUNC
COLUMN time_waited FORMAT 9999999999999 HEADING "Time|Waited"
COLUMN wait_pct FORMAT 99.90 HEADING "Wait|(%)"
BREAK ON inst_id
SELECT
w.inst_id,
w.event,
w.time_waited,
round(w.time_waited/tw.twt*100,2) wait_pct
FROM
gv$system_event w,
(select inst_id, sum(time_waited) twt
from gv$system_event
where time_waited>0
and event NOT IN
('Null event',
'client message',
'rdbms ipc reply',
'smon timer',
'rdbms ipc message',
'PX Idle Wait',
'PL/SQL lock timer',
'file open',
'pmon timer',
'WMON goes to sleep',
'virtual circuit status',
'dispatcher timer',
'SQL*Net message from client',
'parallel query dequeue wait',
'pipe get')
group by inst_id
) tw
WHERE
w.inst_id = tw.inst_id
and w.time_waited>0
and round(w.time_waited/tw.twt*100,2) > 1
and w.event NOT IN
('Null event',
'client message',
'rdbms ipc reply',
'smon timer',
'rdbms ipc message',
'PX Idle Wait',
'PL/SQL lock timer',
'file open',
'pmon timer',
'WMON goes to sleep',
'virtual circuit status',
'dispatcher timer',
'SQL*Net message from client',
'parallel query dequeue wait',
'pipe get')
ORDER by 1 asc, 4 desc;
prompt
prompt
prompt *** LOCKS HELD IN THE DATABASE:
prompt
prompt
PROMPT +------------------------------------------------------------+
PROMPT |LOCKS DEFINED |
PROMPT | |
PROMPT |RS - Row Share - no exclusive WRITE |
PROMPT |RX - Row eXclusive - no exclusive READ or WRITE |
PROMPT |S - Share - no modifications |
PROMPT |SRX - Share Row eXclusive - no modifications or RX |
PROMPT |X - eXclusive - no access period |
PROMPT | |
PROMPT |TX - Row Locks |
PROMPT |TM - Table Locks |
PROMPT +--------------------------------+-------+---+---+---+---+---+
PROMPT |SQL Statement |Mode |RS |RX |S |SRX|X |
PROMPT | | | | | | | |
PROMPT +--------------------------------+-------+---+---+---+---+---+
PROMPT |SELECT ... FROM table ... |none |Y |Y |Y |Y |Y |
PROMPT +--------------------------------+-------+---+---+---+---+---+
PROMPT |INSERT INTO table ... |none |Y* |Y* |N |N |N |
PROMPT +--------------------------------+-------+---+---+---+---+---+
PROMPT |UPDATE table ... |none |Y* |Y* |N |N |N |
PROMPT +--------------------------------+-------+---+---+---+---+---+
PROMPT |DELETE FROM table ... |none |Y* |Y* |N |N |N |
PROMPT +--------------------------------+-------+---+---+---+---+---+
PROMPT |SELECT ... FROM table ... |none |Y* |Y* |Y* |Y* |N |
PROMPT | FOR UPDATE OF ... | | | | | | |
PROMPT +--------------------------------+-------+---+---+---+---+---+
PROMPT
PROMPT * If no conflicting locks are held by another transaction.
PROMPT Otherwise, waits occur.
PROMPT
PROMPT ...
PROMPT
COLUMN username FORMAT A10 HEADING "Username" WORD_WRAPPED
COLUMN type HEADING "TY"
COLUMN opid FORMAT A10 HEADING "SID/SER"
COLUMN lmode FORMAT A5 HEADING "Lock"
COLUMN lrequest FORMAT A4 HEADING "Req?"
COLUMN obj FORMAT A20 HEADING "Object" WORD_WRAPPED
COLUMN block FORMAT A12 HEADING "Blocking?"
select
ORACLE_USERNAME username,
-- OS_USER_NAME osuser,
-- PROCESS as ospid,
l.SID||'/'||s.serial# opid,
DECODE(l.TYPE
-- Long locks
, 'TM', 'dml/data enq (TM)'
, 'TX', 'transac enq (TX) '
, 'UL', 'pls usr lock (UL)'
-- Short locks
, 'BL', 'buf hash tbl (BL)'
, 'CF', 'control file (CF)'
, 'CI', 'cross inst f (CI)'
, 'CU', 'cursor bind (CU) '
, 'DF', 'data file (CF) '
, 'DL', 'direct load (DL) '
, 'DM', 'mount/strtup (DM)'
, 'DR', 'reco lock (DR) '
, 'DX', 'distrib tran (DX)'
, 'FI', 'sga opn file (FI)'
, 'FS', 'file set (FS) '
, 'IN', 'instance num (IN)'
, 'IR', 'instce recvr (IR)'
, 'IS', 'get state (IS) '
, 'IV', 'libcache inv (IV)'
, 'JQ', 'job queue (JQ) '
, 'KK', 'log sw kick (KK) '
, 'LS', 'log switch (LS) '
, 'MM', 'mount def (MM) '
, 'MR', 'media recvry (MR)'
, 'PF', 'pwfile enq (PF) '
, 'PR', 'process strt (PR)'
, 'RW', 'row wait (RW) '
, 'RT', 'redo thread (RT) '
, 'SC', 'scn enq (SC) '
, 'SM', 'smon lock (SM) '
, 'SN', 'seqno instce (SN)'
, 'SQ', 'seqno enq (SQ) '
, 'ST', 'space transc (ST)'
, 'SV', 'seqno value (SV) '
, 'TA', 'generic enq (TA) '
, 'TD', 'dll enq (TD) '
, 'TE', 'extend seg (TE) '
, 'TS', 'temp segment (TS)'
, 'TT', 'temp table (TT) '
, 'UN', 'user name (UN) '
, 'WL', 'write redo (WL) '
, 'TYPE = ' || l.TYPE) AS type ,
DECODE(l.lmode, 0, 'none', 1, 'null', 2, 'RS', 3, 'RX', 4, 'S', 5, 'SRX', 6,
'EXCL', TO_CHAR(l.lmode)) lmode ,
-- DECODE(l.request, 0, 'none', 1, 'null', 2, 'RS', 3, 'RX', 4, 'S', 5, 'SRX', 6,
-- 'X', TO_CHAR(l.request)) AS lrequest,
decode(BLOCK,
0, 'Not Blocking',
1, 'Blocking',
2, 'Global', block) block,
OWNER||'.'||OBJECT_NAME obj
from sys.v_$locked_object lo, dba_objects do, sys.v_$lock l,
(SELECT a.sid, a.serial#
FROM v$session a, v$bgprocess b
WHERE a.paddr = b.paddr(+)) s
where lo.OBJECT_ID = do.OBJECT_ID
AND l.SID = lo.SESSION_ID
and s.sid = l.sid;
set pagesize 25
set heading on
prompt
prompt *** PL/SQL THAT MAY NEED TO BE CACHED
prompt
col owner for a19
col name for a30 heading "Name" WORD_WRAPPED
select owner, name, sharable_mem,kept
from v$db_object_cache
where sharable_mem >100000
and type in ('PACKAGE','PACKAGE BODY','FUNCTION','PROCEDURE')
and kept ='NO';
prompt
prompt + Use the DBMS_SHARED_POOL package to pin objects
prompt + EXEC SYS.DBMS_SHARED_POOL.KEEP('<owner>','<name>');
prompt
prompt
prompt *** SEGMENT USAGE STATISTICS
prompt
prompt *** Most i/o operation (TOP 5):
prompt
col object_type format a15 heading "Object Type"
col object_name format a27 heading "Object Name"
col statistic_name format a22 heading "Statistic Name"
col value format 99999999999 heading "Value"
SELECT * from (
SELECT object_type, object_name, statistic_name, VALUE
FROM v$segment_statistics
WHERE statistic_name LIKE '%phys%' AND VALUE > 50
ORDER BY 4 DESC
) where rownum < 6;
prompt
prompt *** Most buffer gets (TOP 5):
prompt
SELECT * from (
SELECT object_type, object_name, statistic_name,VALUE
FROM v$segment_statistics
WHERE statistic_name LIKE '%logi%' AND VALUE > 50
ORDER BY 4 DESC
) where rownum < 6;
prompt
prompt + Review the query causing high amount of buffer gets.
prompt + Use StatsPack to detect them.
prompt + Create additional indexes to avoid FULL table scans.
prompt
prompt
prompt *** Most Buffer Busy Waits (TOP 5):
prompt
SELECT * from (
SELECT object_type, object_name, statistic_name,VALUE
FROM v$segment_statistics
WHERE statistic_name LIKE '%buffer%' AND VALUE > 0
ORDER BY 4 DESC
) where rownum < 6;
prompt
set pagesize 0
select
'+ Buffer Busy Wait Ratio: '||round((bbc.total_waits*100/(cg.value+dbg.value)),4)
from
v$system_event bbc, v$sysstat cg, v$sysstat dbg
where
bbc.event='buffer busy waits'
and cg.name ='consistent gets'
and dbg.name ='db block gets';
set pagesize 100
-- prompt
-- prompt + As buffer busy waits are due to contention for particular blocks
-- prompt + then you cannot take any action until you know which blocks are
-- prompt + being competed for and why.
-- prompt + Eliminating the cause of the contention is the best option.
-- prompt + Note that "buffer busy waits" for data blocks are often due to
-- prompt + several processes repeatedly reading the same blocks
-- prompt + (eg: if lots of people scan the same index) -
-- prompt + the first session processes the blocks that are in the buffer cache
-- prompt + quickly but then a block has to be read from disk - the other sessions
-- prompt + (scanning the same index) quickly 'catch up' and want the block which
-- prompt + is currently being read from disk - they wait for the buffer as someone
-- prompt + is already reading the block in.
-- prompt
-- prompt + The following hints may be useful for particular types of contention -
-- prompt + these are things that MAY reduce contention for particular situations:
-- prompt
-- prompt + Block Type and Possible Actions
-- prompt
-- prompt + data blocks
-- prompt + Eliminate HOT blocks from the application. Check for repeatedly
-- prompt + scanned / unselective indexes. Change PCTFREE and/or PCTUSED. Check for
-- prompt + 'right- hand-indexes' (indexes that get inserted into at the same point
-- prompt + by many processes). Increase INITRANS. Reduce the number of rows per block
-- prompt + segment header
-- prompt + Increase of number of FREELISTs. Use FREELIST GROUPs (even in single
-- prompt + instance this can make a difference).
-- prompt + freelist blocks
-- prompt + Add more FREELISTS. In case of Parallel Server make sure that each instance
-- prompt + has its own FREELIST GROUP(s).
-- prompt + undo header
-- prompt + Add more rollback segments.
-- prompt
prompt
prompt *** Most Row Level Locks (TOP 5):
prompt
SELECT * from (
SELECT object_type,object_name, statistic_name,VALUE
FROM v$segment_statistics
WHERE statistic_name LIKE '%row%' AND VALUE > 0
ORDER BY 4 DESC
) where rownum < 6;
prompt
prompt *** Most ITL waits (TOP 5):
prompt
SELECT * from (
SELECT object_type,object_name, statistic_name,VALUE
FROM v$segment_statistics
WHERE statistic_name LIKE '%ITL%' AND VALUE > 0
ORDER BY 4 DESC
) where rownum < 6;
-- prompt
-- prompt + What Is an ITL Wait?
-- prompt + This can occur because either (i) the block is so packed that the ITL
-- prompt + cannot grow to create a free slot, or (ii) the MAXTRANS has already
-- prompt + been reached. In this case, the transaction that needs to lock a row
-- prompt + has to wait until a slot becomes available. This wait is termed as ITL
-- prompt + waits and can be seen from the view v$session_wait, in which the session
-- prompt + is waiting on an event named "enqueue."
-- prompt
-- prompt + The INITTRANS setting controls Initial Transaction Slots (ITLs).
-- prompt + A transaction slot is required for any session that needs to modify a
-- prompt + block in an object. For tables INITRANS defaults to 1 for indexes, 2.
-- prompt
-- prompt + The MAXTRANS setting controls the maximum number of ITLs that a block
-- prompt + can allocate (usually defaults to 255). If a block is sparsely populated
-- prompt + then Oracle will dynamically increase the number of ITLs up to MAXTRANS.
-- prompt
-- prompt + ITL's, INITRANS and Block Waits
-- prompt + However, if the block has little or no free space then transactions will
-- prompt + serialize waiting on a free ITL. This is one cause for data base block waits.
-- prompt + By setting INITRANS to the number of expected simultaneous DML transaction
-- prompt + for a single block, you can avoid serialization for ITL slots.
-- prompt
-- prompt + The maximum value suggested for INITRANS is 100 and settings over this
-- prompt + size rarely improve performance. Therefore a setting of INITRANS to the
-- prompt + average number of simultaneous DML users and setting MAXTRANS to 100 will
-- prompt + most likely result in the best utilization of resources and performance.
-- prompt + Remember, each ITL requires approximately 23 bytes in the block header.
-- prompt
prompt
prompt *** CROSSTAB of object and statistic
prompt
col obj format a20 heading "Object|Name" WORD_WRAP
col itl format 999999 heading "ITL|Waits"
col bbw format 999999999 heading "Buffer|Busy|Waits"
col phr format 999999999999 heading "Physical|Reads"
col lr format 999999999999 heading "Logical|Reads"
col rlw format 999999999 heading "Row|Lock|Waits"
set pages 100
select * from
(
select
DECODE
(GROUPING(a.object_name), 1, 'All Objects', a.object_name)
AS obj,
sum(case when
a.statistic_name = 'ITL waits'
then
a.value else null end) "itl",
sum(case when
a.statistic_name = 'buffer busy waits'
then
a.value else null end) "bbw",
sum(case when
a.statistic_name = 'row lock waits'
then
a.value else null end) "rlw",
sum(case when
a.statistic_name = 'physical reads'
then
a.value else null end) phr,
sum(case when
a.statistic_name = 'logical reads'
then
a.value else null end) lr
from
v$segment_statistics a
where
owner not like 'SYS%'
group by
rollup(a.object_name)) b
where (b."itl">0 or b."bbw">0)
order by 3 desc
;
prompt
prompt + The cross tab report generates a listing showing the statistics of concern
prompt + as headers across the page rather than listings going down the page and
prompt + summarizes them by object. This allows us to easily compare total buffer
prompt + busy waits to the number of ITL or row lock waits. This ability to compare
prompt + the ITL and row lock waits to buffer busy waits lets us see what objects may
prompt + be experiencing contention for ITL lists, which may be experiencing excessive
prompt + locking activity and through comparisons, which are highly contended for
prompt + without the row lock or ITL waits.
prompt
-- prompt + AN example of the output of the report, edited for length, is shown below.
-- prompt
-- prompt + Object ITL Buffer Row Lock Physical Logical
-- prompt + Waits Busy Waits Waits Reads Reads
-- prompt + ************* ********* ********** ********* *********** **********
-- prompt + BILLING 0 63636 38267 1316055 410219712
-- prompt + BILLING_INDX1 1 16510 55 151085 21776800
-- prompt + ...
-- prompt + DELIVER_INDX1 1963 36096 32962 1952600 60809744
-- prompt + DELIVER_INDX2 88 16250 9029 18839481 342857488
-- prompt + DELIVER_PK 2676 99748 29293 15256214 416206384
-- prompt + ...
-- prompt + All Objects 12613 20348859 1253057 1139977207 20947864752
-- prompt +
-- prompt + In the above report the BILLING_INDX1 index has a large amount of buffer
-- prompt + Reads busy waits but we can.t account for them from the ITL or Row lock
-- prompt + waits, this indicates that the index is being constantly read and the blocks
-- prompt + then aged out of memory forcing waits as they are re-read for the next
-- prompt + process. On the other hand, almost all of the buffer busy waits for the
-- prompt + DELIVER_INDX1 index can be attributed to ITL and Row Lock waits.
-- prompt + In situations where there are large numbers of ITL waits we need to consider
-- prompt + the increase of the INITRANS setting for the table to remove this source of
-- prompt + contention. If the predominant wait is row lock waits then we need to
-- prompt + determine if we are properly using locking and cursors in our application
-- prompt + (for example, we may be over using the SELECT.FOR UPDATE type code.)
-- prompt + If, on the other hand all the waits are un-accounted for buffer busy waits,
-- prompt + then we need to consider increasing the amount of database block buffers
-- prompt + we have in our SGA.
-- prompt
prompt
prompt *** SEGMENTS WITH MORE THAN 100 extents
prompt ...
col owner for a13
col segment_name for a22
col segment_type for a18
col segsize for 999999.90 heading "Segment|Size (Mb)"
col segcount for 999999 heading "# of extents"
select
OWNER,
SEGMENT_NAME,
SEGMENT_TYPE,
sum(bytes)/1024/1024 segsize,
count(*) segcount
from
dba_segments
where
owner not in ('SYS','SYSTEM')
group by
OWNER, SEGMENT_NAME, SEGMENT_TYPE
having
count(*) > 100;
prompt
prompt + Segments with high # of extents in dictionary manged tablespace might cause
prompt + performance isssues.
prompt + Use Locally managed tablespaces.
prompt
prompt
prompt *** TABLES with many indexes (>6)
prompt
set pagesize 0
select count(*)||' tables detected with more than 4 indexes'
from
(select owner, table_name, count(*)
from dba_indexes
where owner not in ('SYS','SYSTEM')
group by owner, table_name
having count(*) > 6
);
set pagesize 50
col owner for a30 heading Owner
col table_name for a30 heading "Table Name"
col no for 999 heading "# of indexes"
select
owner,
table_name,
count(*) no
from
dba_indexes
where
owner not in ('SYS','SYSTEM')
group by
owner, table_name
having
count(*) > 6
order by 3 desc;
prompt
prompt + Consider to use index monitoring feature
prompt + Monitoring an Index determine weather the particular index is used or not
prompt + used, so that you can drop un-necessary indexes.
prompt
-- prompt + Enable Monitoring By :
-- prompt + ALTER INDEX <INDEXNAME> MONITORING USAGE ;
-- prompt
--
--
-- prompt + use the following query to generate the script for INDEX MONITORING:
-- prompt
-- prompt + select
-- prompt + ' ALTER INDEX '||owner||'.'||index_name||' MONITORING USAGE;'
-- prompt + from dba_indexes i
-- prompt + where table_name in (select table_name from (
-- prompt + select owner, table_name, count(*)
-- prompt + from dba_indexes
-- prompt + where owner not in ('SYS','SYSTEM')
-- prompt + group by owner, table_name
-- prompt + having count(*) > 6));
-- prompt
-- prompt + Check the monitoring status using "V$OBJECT_USAGE" view (column "USED"):
-- prompt
-- prompt + select * from V$OBJECT_USAGE;
-- prompt
-- prompt + INDEX_NAME TABLE_NAME MON USED START_MONITORING END_MONITORING
-- prompt + *************** ************* *** **** ***************** **************
-- prompt + ....
-- prompt
-- prompt + Run the application for a sufficient (~1 week) time to use the table and
-- prompt + associated indexes. Work with application team to remove unused indexes.
-- prompt
-- prompt + Disable the monitoring By:
-- prompt + ALTER INDEX <INDEXNAME> NOMINTORING USAGE;
-- prompt
prompt
prompt *** SEQUENCE CACHE STATS (listing only non-cached)
prompt
col sequence_owner format a15 heading "Sequence|Owner"
col sequence_name format a30 heading "Sequence Name"
col cache_size format 999999999 heading "Sequnce|cache|size"
col comments format a21 heading "Comments"
select
sequence_owner,
sequence_name,
cache_size,
(case when cache_size < 20
then '- Set cache to min 20'
else 'Ok'
end) comments
from
dba_sequences
where
sequence_owner not in ('SYS','SYSTEM','DBSNMP')
and cache_size = 0
order by 1,2,3;
prompt
prompt On application with high amount of inserts set cache size to min. 40
prompt
prompt
prompt
prompt *** ROLLBACK / UNDO segments statistics
prompt
set pagesize 0
col msg format A50
prompt init parameters:
prompt
select
name||'='||value
from v$parameter
where name like 'undo%'
or name like 'rollback%';
prompt
select (case when value = 'MANUAL' then
'+ '||value ||' UNDO management is used.'||chr(10)||
'+ Recommendation: Enable Automatic UNDO management.' ||chr(10)||
'+ set undo_management=AUTO' ||chr(10)||
'+ undo_tablespace=<tablespace_name>' ||chr(10)||
'+ undo_retention=1800' ||chr(10)||
'+ Additionally the following parameter can be set to suppress '||
'errors while executing'||chr(10)||
'+ manual undo management mode operations such as '||
'.SET TRANSACTION USE ROLLBACK SEGMENT ...'
else '+ Automatic UNDO management is used.'
end) msg
from v$parameter
where name ='undo_management';
set pagesize 50
column "Rollback Segment" format a16
column "Size (Kb)" format 9,999,999
column "Gets" format 999,999,990
column "Waits" format 9,999,990
column "% Waits" format 90.00
column "# Shrinks" format 999,990
column "# Extends" format 999,990
select rn.Name "Rollback Segment", rs.RSSize/1024 "Size (KB)", rs.Gets "Gets",
rs.waits "Waits", (rs.Waits/rs.Gets)*100 "% Waits",
rs.Shrinks "# Shrinks", rs.Extends "# Extends"
from sys.v_$RollName rn, sys.v_$RollStat rs
where rn.usn = rs.usn;
prompt
prompt GETS - # of gets on the rollback segment header
prompt WAITS - # of waits for the rollback segment header
ptompt
set pagesize 0
select 'The average of waits/gets is '||
round((sum(waits) / sum(gets)) * 100,2)||'%'
From v$rollstat;
set pagesize 50
prompt
PROMPT
prompt *** ROLLBACK / UNDO SEGMENTS CONTENTION:
prompt
COLUMN class FORMAT A30 HEADING 'Header Type'
COLUMN count FORMAT 999,999,999,999 HEADING 'Number|of Waits'
COLUMN con_get FORMAT 999,999,999,999,999 HEADING 'Logical|Reads'
COLUMN pct FORMAT 990.99 HEADING 'Percent of|Contention'
SELECT
a.class,
count,
SUM(value) con_get,
((count / SUM(value)) * 100) pct
FROM
v$waitstat a,
v$sysstat b
WHERE
name IN ('db block gets', 'consistent gets')
AND a.class IN (
'system undo header',
'system undo block',
'undo header',
'undo block')
GROUP BY
a.class,
count;
prompt
prompt Another way to gauge rollback contention is:
prompt
column xn1 format 9999999
column xv1 new_value xxv1 noprint
set head on
select class, count
from v$waitstat
where class in ('system undo header', 'system undo block',
'undo header', 'undo block' );
set head off
select 'Total requests = '||sum(count) xn1, sum(count) xv1
from v$waitstat;
select 'Contention for system undo header = '||
(round(count/(&xxv1+0.00000000001),4)) * 100||'%'
from v$waitstat
where class = 'system undo header';
select 'Contention for system undo block = '||
(round(count/(&xxv1+0.00000000001),4)) * 100||'%'
from v$waitstat
where class = 'system undo block';
select 'Contention for undo header = '||
(round(count/(&xxv1+0.00000000001),4)) * 100||'%'
from v$waitstat
where class = 'undo header';
select 'Contention for undo block = '||
(round(count/(&xxv1+0.00000000001),4)) * 100||'%'
from v$waitstat
where class = 'undo block';
prompt
prompt + If the percentage for an area is more than 1% or 2%, consider
prompt + creating more rollback/undo segments.
prompt
set head on
prompt
prompt
prompt *** UNDO USAGE STATISTICS:
prompt
col BeginT format a6 heading "Begin|Time" WORD_WRAPPED
col EndT format a6 heading "End|Time" WORD_WRAPPED
col UDBPS format 9999 heading "UNDO|Blk|Per|Sec"
col T1 format 9999999999 heading "Total|Undo|Blocks|Used"
col T2 format 9999999999999 heading "Total|Num|Trans|Executed"
col T3 format 999999999 heading "Longest|Query|(in secs)"
col T4 format 999999 heading "Highest|Conc TX|Count"
col T5 format 99999 heading "# of|ORA|01555"
col T6 format 99999 heading "# of|Space|Err"
select TO_CHAR(MIN(Begin_Time),'DDMMYYHH24MISS') BeginT,
TO_CHAR(MAX(End_Time),'DDMMYYHH24MISS') EndT,
ceil(sum(undoblks)/((max(end_time)-min(begin_time))*24*60*60)) UDBPS,
SUM(Undoblks) T1,
SUM(Txncount) T2,
MAX(Maxquerylen) T3,
MAX(Maxconcurrency) T4,
SUM(Ssolderrcnt) T5,
SUM(Nospaceerrcnt) T6
from V$UNDOSTAT;
prompt
prompt
prompt *** UNDO TS size estimation:
prompt
prompt + Undo Space in Bytes = (UR * UDBPS * DB_Block Size) + Overhead
prompt + where
prompt + UR = Undo Retention Time in Seconds
prompt + UDBPS = Undo Blocks used Per Second
prompt + Overhead = One DB block for metadata
prompt
prompt Current UNDO tablespace(s):
prompt
set pagesize 0
select '+ UNDO tablespace: '||ts||'. Size: '||sz||'MB'
from
(
select
t.tablespace_name ts, ROUND(sum(bytes)/1024/1024,2) sz
from
dba_tablespaces t,
dba_data_files f
where
f.tablespace_name = t.tablespace_name
and t.contents= 'UNDO'
group by
t.tablespace_name
);
prompt
prompt The following size estimation is based on the current DB usage.
prompt
SELECT '+ Estimated UNDO TS size for undo_retention='||rd||' (current) is '||trunc((rd * (udbps * overhead) + overhead)/1024/1024)||'MB'
FROM
(SELECT value AS RD
FROM v$parameter
WHERE name = 'undo_retention'),
(SELECT (sum (undoblks) /
sum ( ((end_time - begin_time) * 86400))) as UDBPS
FROM v$undostat),
(SELECT value AS OVERHEAD
FROM v$parameter
WHERE name = 'db_block_size');
prompt
prompt
prompt + On Oracle 10g use Undo Advisor PL/SQL package to estimate the UNDO size:
prompt + set serveroutput on;
prompt + declare utbsize_in_MB number;
prompt + begin
prompt + utbsize_in_MB := DBMS_UNDO_ADV.RBU_MIGRATION;
prompt + dbms_output.put_line(TO_CHAR(utbsize_in_MB));
prompt + end;
prompt + /
prompt
prompt
prompt ROLLBACK SEGMENTS USAGE BY ACTIVE TRANSACTIONS
prompt ...
set pagesize 50
COLUMN name FORMAT A20 HEADING "Rollback Segment"
COLUMN sid FORMAT 990 HEADING "Session|ID"
COLUMN username FORMAT A16 HEADING "Oracle|Username"
COLUMN osuser FORMAT A16 HEADING "O/S|Username"
COLUMN program FORMAT A16 HEADING "Program"
SELECT
r.name,
l.sid,
s.username,
s.osuser,
s.program
FROM
v$rollname r,
v$session s,
v$lock l
WHERE
r.usn = TRUNC(l.id1 / 65536)
AND l.sid = s.sid
AND l.type = 'TX'
ORDER BY
r.name;
prompt
prompt *** MISSING STATISTICS ?
prompt
set pagesize 0
select 'optimizer_mode='||value from v$parameter where name = 'optimizer_mode';
prompt
select (case when value = 'RULE' then '+ '||value ||
' based optimizer is used. Ignore the statistics,'||chr(10)||
'+ ... unless you are not using SQL hints invoking COST based optimizer.'
else '+ Cost based optimizer is used. Make sure to keep the statistics '||
'uptodate.'
end)
from v$parameter where name = 'optimizer_mode';
set pagesize 50
column schema_name format a19
column clusters_with format 99999999 heading "CLUSTERS|WITH|STATS"
column tables_with format 99999999 heading "TABLES|WITH|STATS"
column indexes_with format 99999999 heading "INDEXES|WITH|STATS"
column clusters_without format 99999999 heading "CLUSTERS|WITHOUT|STATS"
column tables_without format 99999999 heading "TABLES|WITHOUT|STATS"
column indexes_without format 99999999 heading "INDEXES|WITHOUT|STATS"
select
u.name schema_name,
nvl(c.stats, 0) clusters_with,
t.stats tables_with,
nvl(i.stats, 0) indexes_with,
nvl(c.total - c.stats, 0) clusters_without,
t.total - t.stats tables_without,
nvl(i.total - i.stats, 0) indexes_without
from
( select
o1.owner#,
count(c1.avgchn) stats,
count(*) total
from
sys.clu$ c1,
sys.obj$ o1
where
c1.obj# = o1.obj#
group by
o1.owner#
) c,
( select
o2.owner#,
count(t2.rowcnt) stats,
count(*) total
from
sys.tab$ t2,
sys.obj$ o2
where
t2.obj# = o2.obj# and
t2.tab# is null
group by
o2.owner#
) t,
( select
o3.owner#,
count(i3.blevel) stats,
count(*) total
from
sys.ind$ i3,
sys.obj$ o3
where
i3.obj# = o3.obj# and
i3.type# between 1 and 7
group by
o3.owner#
) i,
sys.user$ u
where
u.user# = t.owner# and
u.user# = c.owner# (+) and
u.user# = i.owner# (+)
order by
t.total - t.stats
+ nvl(c.total - c.stats, 0)
+ nvl(i.total - i.stats, 0);
prompt
prompt Schemas detected with stats older than 30 days:
prompt
set pagesize 50
col owner for a20 heading "Schema"
col no for 999999 heading "# of objects"
select owner, sum(no) no
from
(
select owner, trunc(last_analyzed) dt, count(*) no
from dba_tables
where owner not in ('SYS','SYSTEM')
group by owner, trunc(last_analyzed)
)
where
dt < sysdate - 30
group by owner
order by 2 desc, 1 asc;
prompt
prompt + Use only DBMS_STATS package to gather statistics.
prompt
prompt + Ex.: exec DBMS_STATS.GATHER_schema_stats (ownname => '<schemaname>',
prompt + options => 'GATHER',
prompt + cascade => TRUE,
prompt + estimate_percent => 30,
prompt + degree => 4);
prompt
prompt + ! Avoid using (estimate_percent => dbms_stats.auto_sample_size)
prompt
prompt + Recommendation: Save database statistics on regularly basis (once a month)
prompt + using:
prompt + dbms_stats.create_stat_table('<owner>','<stats_tab_name>');
prompt + dbms_stats.export_schema_stats('<owner>','<stats_tab_name>','<owner>');
prompt
prompt
prompt *** TABLESPACE FREE SPACE report:
prompt
COLUMN ts HEADING "Tablespace Name" FORMAT A25
COLUMN msg HEADING "Tablespace Name" FORMAT A27
COLUMN megs_alloc HEADING "Alloc|(Mbytes)" FORMAT 999999
COLUMN megs_used HEADING "Used |(Mbytes)" FORMAT 999999
COLUMN Pct_Free HEADING "% Free" FORMAT 999.9
SELECT a.tablespace_name ts,
ROUND(a.bytes_alloc / 1024 / 1024, 2) megs_alloc,
-- ROUND(NVL(b.bytes_free, 0) / 1024 / 1024, 2) megs_free,
ROUND((a.bytes_alloc - NVL(b.bytes_free, 0)) / 1024 / 1024, 2) megs_used,
ROUND((NVL(b.bytes_free, 0) / a.bytes_alloc) * 100,2) Pct_Free,
(case when ROUND((NVL(b.bytes_free, 0) / a.bytes_alloc) * 100,2)<=0
then 'Immediate action required!'
when ROUND((NVL(b.bytes_free, 0) / a.bytes_alloc) * 100,2)<5
then 'Critical (<5% free)'
when ROUND((NVL(b.bytes_free, 0) / a.bytes_alloc) * 100,2)<15
then 'Warning (<15% free)'
when ROUND((NVL(b.bytes_free, 0) / a.bytes_alloc) * 100,2)<25
then 'Warning (<25% free)'
when ROUND((NVL(b.bytes_free, 0) / a.bytes_alloc) * 100,2)>60
then 'Waste of space? (>60% free)'
else 'OK'
end) msg
FROM ( SELECT f.tablespace_name,
SUM(f.bytes) bytes_alloc,
SUM(DECODE(f.autoextensible, 'YES',f.maxbytes,'NO', f.bytes)) maxbytes
FROM DBA_DATA_FILES f
GROUP BY tablespace_name) a,
( SELECT f.tablespace_name,
SUM(f.bytes) bytes_free
FROM DBA_FREE_SPACE f
GROUP BY tablespace_name) b
WHERE a.tablespace_name = b.tablespace_name (+)
UNION
SELECT h.tablespace_name,
ROUND(SUM(h.bytes_free + h.bytes_used) / 1048576, 2),
-- ROUND(SUM((h.bytes_free + h.bytes_used) - NVL(p.bytes_used, 0)) / 1048576, 2),
ROUND(SUM(NVL(p.bytes_used, 0))/ 1048576, 2),
ROUND((SUM((h.bytes_free + h.bytes_used) - NVL(p.bytes_used, 0)) / SUM(h.bytes_used + h.bytes_free)) * 100,2),
(case when ROUND((SUM((h.bytes_free + h.bytes_used) - NVL(p.bytes_used, 0)) / SUM(h.bytes_used + h.bytes_free)) * 100,2)<=0 then 'Immediate action required!'
when ROUND((SUM((h.bytes_free + h.bytes_used) - NVL(p.bytes_used, 0)) / SUM(h.bytes_used + h.bytes_free)) * 100,2)<5 then 'Critical (<5% free)'
when ROUND((SUM((h.bytes_free + h.bytes_used) - NVL(p.bytes_used, 0)) / SUM(h.bytes_used + h.bytes_free)) * 100,2)<15 then 'Warning (<15% free)'
when ROUND((SUM((h.bytes_free + h.bytes_used) - NVL(p.bytes_used, 0)) / SUM(h.bytes_used + h.bytes_free)) * 100,2)<25 then 'Warning (<25% free)'
when ROUND((SUM((h.bytes_free + h.bytes_used) - NVL(p.bytes_used, 0)) / SUM(h.bytes_used + h.bytes_free)) * 100,2)>60 then 'Waste of space? (>60% free)'
else 'OK'
end) msg
FROM sys.V_$TEMP_SPACE_HEADER h, sys.V_$TEMP_EXTENT_POOL p
WHERE p.file_id(+) = h.file_id
AND p.tablespace_name(+) = h.tablespace_name
GROUP BY h.tablespace_name
ORDER BY 1;
PROMPT
PROMPT *** TABLESPACE FRAGMENTATION REPORT (ts with more than 100 free chunks) (TOP 5)
PROMPT
col tsname format A31 heading "Tablespace Name"
col totsize format 999999.990 heading "Total|size (Mb)"
col smsize format 999999.990 heading "Smalles|chunk (Kb)"
col bigsize format 999999.990 heading "Biggest|chunk (Mb)"
col nochunk format 999999 heading "# of free|space chunks"
select * from
(
select
tablespace_name tsname,
round(sum(bytes)/1024/1024,2) totsize,
round(min(bytes)/1024,2) smsize,
round(max(bytes)/1024/0124,2) bigsize,
count(*) nochunk
from dba_free_space
where tablespace_name not in (select tablespace_name
from dba_tablespaces
where contents = 'UNDO')
group by tablespace_name
having count(*)>100
order by 3 desc
)
where rownum < 6;
prompt
prompt + Tablespace fragmentation is not a performance problem, but merely
prompt + a waste of space problem.
prompt
prompt
prompt *** TABLESPACE analysis
prompt
set pagesize 50
col tablespace_name for a21 heading "Tablespace Name"
col bs for a4 heading "Bl|Size"
col contents for a4 heading "Cont|ents" TRUNC
col log for a4 heading "Logg|ing"
col extent_management for a6 heading "Extent|Mgmt" TRUNC
col allocation_type for a7 heading "Alloc"
col ssm for A5 heading "ASSM?"
col status for A6 heading "Status" TRUNC
col fno for 99999 heading "Total|Files"
col fsize for 999.99 heading "Total|Size|(GB)"
select
t.tablespace_name,
block_size/1024||'k' bs,
t.status,
contents,
decode(logging,'LOGGING','Yes','No') log,
extent_management,
allocation_type,
decode(segment_space_management,'AUTO','Yes','No') ssm,
count(*) fno,
round(sum(bytes)/1024/1024/1024,2) fsize
from
dba_tablespaces t,
dba_data_files d
where t.tablespace_name = d.tablespace_name
group by
t.tablespace_name,
block_size/1024||'k',
t.status,
contents,
decode(logging,'LOGGING','Yes','No'),
extent_management,
allocation_type,
decode(segment_space_management,'AUTO','Yes','No')
order by
contents,
tablespace_name;
prompt
prompt + Localy Manged (9i and 10g) tablespaces and ASSM (10g) are recommended
prompt
prompt
set pagesize 0
select (case when count(*)=0 then '+ Temporary Tablespace not defined!'
else '+ Temporary tablespace: '||max(tablespace_name)
end)
from dba_tablespaces
where contents = 'TEMPORARY'
group by tablespace_name;
select (case when count(*)=0 then '+ No temp files defined! TEMP files have to be'||
' used for TEMP tablespaces.'
else '+ Tablespace '||max(tablespace_name)||' has '||count(*)||' temp files.'
end)
from dba_temp_files
group by tablespace_name;
prompt
prompt *** Datafile status
prompt
set pagesize 50
col message for a70
col status for a15 heading "File status"
col enabled for a15 heading "Enabled mode"
col bs for a10 heading "Block size"
col dfno for 99999 heading "# Files"
select status, enabled, block_size/1024||'k' bs, count(*) dfno
from v$datafile
group by status, enabled, block_size/1024||'k'
order by block_size/1024||'k', status, enabled;
prompt
prompt *** Tempfile status
prompt
select status, enabled, block_size/1024||'k' bs, count(*) dfno
from v$tempfile
group by status, enabled, block_size/1024||'k'
order by block_size/1024||'k', status, enabled;
set pagesize 0
PROMPT
PROMPT *** I/O DISTRIBUTION BY TABLESPACE (TOP 5)
PROMPT
VARIABLE n_rd NUMBER
VARIABLE n_wr NUMBER
SET TERMOUT OFF
BEGIN
SELECT
SUM(phyblkrd),
SUM(phyblkwrt)
INTO
:n_rd,
:n_wr
FROM
v$filestat
;
END;
/
DEFINE nameln = 30
DEFINE devsep = "GREATEST(0, -
INSTR(df.name, '/', -1), -
INSTR(df.name, ':', -1), -
INSTR(df.name, '\', -1))"
DEFINE device = "SUBSTR(df.name, 1, &&devsep)"
DEFINE filenm = "SUBSTR(df.name, &&devsep + 1)"
SET TERMOUT ON
set echo off
set pagesize 100
COLUMN name FORMAT A22 HEADING "Name"
COLUMN tsize FORMAT 9,990 HEADING "GBytes"
COLUMN reads FORMAT 999999,990 HEADING "Reads|(1000s)"
COLUMN readp FORMAT 999 HEADING "Read| %" JUSTIFY RIGHT
COLUMN writs FORMAT 999999,990 HEADING "Writes|(1000s)"
COLUMN writp FORMAT 990 HEADING "Wri-|te %" JUSTIFY RIGHT
COLUMN total FORMAT 999999,990 HEADING "Total|(1000s)"
COLUMN totlp FORMAT 990 HEADING "Tot-|al %" JUSTIFY RIGHT
SELECT * FROM
(
SELECT
MAX(ts.name) name,
SUM(df.bytes / (1024 * 1024 * 1024)) tsize,
SUM(st.phyblkrd) / 1000 reads,
SUM(st.phyblkrd) / (:n_rd) * 100 readp,
SUM(st.phyblkwrt) / 1000 writs,
SUM(st.phyblkwrt) / (:n_wr) * 100 writp,
SUM(st.phyblkrd + st.phyblkwrt) / 1000 total,
SUM(st.phyblkrd + st.phyblkwrt) / (:n_rd + :n_wr) * 100 totlp
FROM
v$datafile df,
v$filestat st,
sys.file$ sf,
sys.ts$ ts
WHERE
df.file# = st.file#
AND df.file# = sf.file#
AND sf.ts# = ts.ts#
GROUP BY
ts.name
ORDER BY 8 DESC
)
WHERE ROWNUM < 6;
PROMPT
PROMPT *** I/O DISTRIBUTION BY DEVICE (TOP 5)
PROMPT
SELECT * FROM
(
SELECT
MAX(&&device) name,
SUM(df.bytes / (1024 * 1024 * 1024)) tsize,
SUM(st.phyblkrd) / 1000 reads,
SUM(st.phyblkrd) / (:n_rd) * 100 readp,
SUM(st.phyblkwrt) / 1000 writs,
SUM(st.phyblkwrt) / (:n_wr) * 100 writp,
SUM(st.phyblkrd + st.phyblkwrt) / 1000 total,
SUM(st.phyblkrd + st.phyblkwrt) / (:n_rd + :n_wr) * 100 totlp
FROM
v$datafile df,
v$filestat st,
sys.file$ sf,
sys.ts$ ts
WHERE
df.file# = st.file#
AND df.file# = sf.file#
AND sf.ts# = ts.ts#
GROUP BY
&&device
ORDER BY 8 DESC
)
where rownum <6;
PROMPT
PROMPT *** I/O DISTRIBUTION BY FILE (TOP 5)
PROMPT
SELECT * FROM
(
SELECT
MAX(df.name) name,
SUM(df.bytes / (1024 * 1024 * 1024)) tsize,
SUM(st.phyblkrd) / 1000 reads,
SUM(st.phyblkrd) / (:n_rd) * 100 readp,
SUM(st.phyblkwrt) / 1000 writs,
SUM(st.phyblkwrt) / (:n_wr) * 100 writp,
SUM(st.phyblkrd + st.phyblkwrt) / 1000 total,
SUM(st.phyblkrd + st.phyblkwrt) / (:n_rd + :n_wr) * 100 totlp
FROM
v$datafile df,
v$filestat st,
sys.file$ sf,
sys.ts$ ts
WHERE
df.file# = st.file#
AND df.file# = sf.file#
AND sf.ts# = ts.ts#
GROUP BY
df.name
ORDER BY 8 DESC
)
where rownum <6;
set pagesize 0
col namevalue format a79 heading "Name" WORD_WRAPPED
set pagesize 0
col namevalue format a79 heading "Name" WORD_WRAPPED
prompt
prompt *** RMAN
prompt
select
'CONFIGURE '||name ||' '|| value namevalue
from
v$rman_configuration
order by
conf#;
set pagesize 50
prompt
prompt *** DATABASE ACCOUNTS report:
prompt
col USERNAME format a15 heading "Username" WORD_WRAPPED
col STATUS format a10 heading "Account|Status" WORD_WRAPPED
col PROFILE format a27 heading "Profile" WORD_WRAPPED
col CREATED format a8 heading "Created|Time"
col PWD_CHANGE_TIME format a8 heading "Pwd|Last|Time|Chg"
col DAYS_BEFORE_EXPIRE format 99999 heading "Days|Before|Expire"
select
u.name AS "USERNAME",
decode(d.account_status,
'EXPIRED(GRACE)','EXP(GRACE)',
d.account_status) AS "STATUS",
d.profile AS "PROFILE",
to_char(u.ctime,'DD/MM/YY') AS "CREATED",
to_char(u.ptime,'DD/MM/YY') AS "PWD_CHANGE_TIME",
(case when trunc(u.ptime + days - sysdate) < 0
then 0
else trunc(u.ptime + days - sysdate)
end) AS "DAYS_BEFORE_EXPIRE"
from
(select name,ptime,ctime from sys.user$) u,
(select username,profile,account_status from sys.dba_users) d,
(select profile, decode(limit,'UNLIMITED',99999,'DEFAULT',99999,limit) "DAYS"
from sys.dba_profiles where resource_name = 'PASSWORD_LIFE_TIME') p
where u.name=d.username
-- and u.name not in (select username from dba_users where profile like '%GDBA%')
and d.profile = p.profile
order by 6 desc,2,3,1;
prompt
prompt *** SECURITY (roles and privileges) checking:
prompt
set pagesize 50
set recsep off
col grantee for a20 heading "Grantee"
col granted_role for a20 heading "Granted Role"
col privilege for a20 heading "Granted Privilege"
col admin_option for a10 heading "Admin|Option?"
col default_role for a10 heading "Default|Role?"
select
grantee,
granted_role,
admin_option,
default_role
from
dba_role_privs
where
granted_role in ('DBA','EXP_FULL_DATABASE','IMP_FULL_DATABASE','OEM_MONITOR')
and
grantee not in ('SYS','SYSTEM','DBA')
order by 1;
select
grantee,
privilege,
admin_option
from
dba_sys_privs
where privilege in
('ALTER DATABASE',
'ALTER PROFILE',
'ALTER SYSTEM',
'ALTER TABLESPACE',
'BECOME USER',
'BACKUP ANY TABLE',
'ALTER USER',
'CREATE ANY DIRECTORY',
'CREATE DATABASE',
'CREATE ROLLBACK SEGMENT',
'CREATE TABLESPACE',
'DROP ANY DIRECTORY',
'DROP DATABASE',
'DROP PROFILE',
'DROP ROLLBACK SEGMENT',
'DROP TABLESPACE',
'DROP USER',
'EXECUTE ANY PROCEDURE',
'GRANT ANY OBJECT PRIVILEGE',
'GRANT ANY PRIVILEGE',
'GRANT ANY ROLE',
'INSERT ANY TABLE',
'MANAGE TABLESPACE',
'UNDER ANY TABLE',
'UNDER ANY TYPE',
'UNDER ANY VIEW',
'UPDATE ANY TABLE',
'ALTER ANY PROCEDURE',
'ALTER ANY ROLE',
'ALTER ANY TABLE',
'ALTER ANY TRIGGER',
'ALTER ANY TYPE',
'DELETE ANY TABLE',
'DROP ANY PROCEDURE',
'DROP ANY ROLE',
'DROP ANY TABLE',
'FORCE ANY TRANSACTION',
'FORCE TRANSACTION',
'ALTER ROLLBACK SEGMENT',
'CREATE USER',
'CREATE PUBLIC DATABASE LINK',
'RESTRICTED SESSION',
'SELECT ANY TABLE')
and grantee not in
('SYS',
'SYSTEM',
'DBA',
'IMP_FULL_DATABASE',
'EXP_FULL_DATABASE')
order by 1;
prompt
prompt
prompt *** PROFILES PASSWORD AGING SETTINGS:
prompt
set pagesize 100
col profile format a24 heading "Profile Name"
col resource_name format a24 heading "Resource Name"
col limit format a18 heading "Limit"
col com format a10 heading "Comment"
BREAK ON profile SKIP 1
SELECT
profile,
resource_name,
limit,
(case when limit = 'UNLIMITED' or limit = 'DEFAULT'
then 'Unsecured?'
end) com
FROM
sys.dba_profiles
WHERE
resource_name In ('FAILED_LOGIN_ATTEMPTS',
'PASSWORD_LIFE_TIME',
'PASSWORD_REUSE_TIME',
'PASSWORD_REUSE_MAX',
'PASSWORD_VERIFY_FUNCTION',
'PASSWORD_LOCK_TIME',
'PASSWORD_GRACE_TIME')
ORDER BY
profile,
resource_name;
-- prompt
-- prompt + Standard GDBA password aging profiles:
-- prompt +++++++++++++++++++++++++++++++++++++++++++++
-- prompt + create PROFILE GDBA_USER LIMIT
-- prompt + PASSWORD_LIFE_TIME 90
-- prompt + PASSWORD_GRACE_TIME 10
-- prompt + PASSWORD_REUSE_TIME 1800
-- prompt + PASSWORD_REUSE_MAX UNLIMITED
-- prompt + FAILED_LOGIN_ATTEMPTS 5
-- prompt + PASSWORD_LOCK_TIME 1/24
-- prompt + PASSWORD_VERIFY_FUNCTION verify_function;
-- prompt +++++++++++++++++++++++++++++++++++++++++++++
-- prompt + create PROFILE HP_GDBA_ADMIN LIMIT
-- prompt + PASSWORD_LIFE_TIME 45
-- prompt + PASSWORD_GRACE_TIME 10
-- prompt + PASSWORD_REUSE_TIME 1800
-- prompt + PASSWORD_REUSE_MAX UNLIMITED
-- prompt + FAILED_LOGIN_ATTEMPTS 5
-- prompt + PASSWORD_LOCK_TIME 1/24
-- prompt + PASSWORD_VERIFY_FUNCTION verify_function12;
-- prompt +++++++++++++++++++++++++++++++++++++++++++++
-- prompt + Create PROFILE HP_APP_SERVICE LIMIT
-- prompt + PASSWORD_LIFE_TIME 365
-- prompt + PASSWORD_GRACE_TIME 10
-- prompt + PASSWORD_REUSE_TIME 1800
-- prompt + PASSWORD_REUSE_MAX UNLIMITED
-- prompt + FAILED_LOGIN_ATTEMPTS 5
-- prompt + PASSWORD_LOCK_TIME 1/24
-- prompt + PASSWORD_VERIFY_FUNCTION verify_function12;
-- prompt +++++++++++++++++++++++++++++++++++++++++++++
-- prompt
-- prompt + More info in "Database Security - User Account Setup.doc" on GDBA share
-- prompt
prompt
prompt *** NLS settings
prompt
COLUMN "Parameter" FORMAT A25
COLUMN "Database Value" FORMAT A20
COLUMN "Instance Value" FORMAT A15
COLUMN "Session Value" FORMAT A15
SELECT
d.parameter "Parameter",
d.value "Database Value",
i.value "Instance Value",
s.value "Session Value"
FROM
nls_database_parameters d,
nls_instance_parameters i,
nls_session_parameters s
WHERE
d.parameter = i.parameter(+)
AND d.parameter = s.parameter(+);
prompt
prompt + Make sure you are using the correct NLS_LANG when using RMAN, exp/imp
prompt
set pagesize 0
prompt
prompt *** CURRENT SESSION DETAILS:
prompt
SELECT
'SID: ' || s.sid || CHR(10) ||
'Serial#: ' || s.serial# || CHR(10) ||
'Process: ' || s.process || CHR(10) ||
'SPID: ' || p.spid || CHR(10) ||
'OS User: ' || s.osuser || CHR(10) ||
'Oracle User: ' || s.username || CHR(10) ||
'Machine: ' || s.machine || CHR(10) ||
'Terminal: ' || s.terminal || CHR(10) ||
'Logon Time: ' || TO_CHAR(s.logon_time, 'DD-MON-RR HH24:MI')
FROM
v$session s,
v$process p
WHERE
audsid = USERENV('SESSIONID')
AND s.paddr = p.addr
and lower(substr(s.program,1,7)) = 'sqlplus';
prompt
select 'Timestamp: '||to_char(sysdate,'Mon dd yyyy hh24:mi:ss') from dual;
prompt
prompt
prompt *** END of healthcheck report
spool off
select
'+ Script Outp saved to: healthcheck_&&dbname×tamp&&suffix ' "Output script"
from dual;
prompt
prompt + If you run this script on your Windows PC the output will be saved into
prompt + %ORACLE_HOME%\bin
prompt