Performance Tuning
Last modification: 18-Dec-08
Includes:
- Installation
and Top Init.ora Parameters
- Oracle Performance Checklist
- Instance
Tuning
- Application and SQL Tuning
- Distribution
of Disk I/O
- ANALYZE and DBMS_STATS Package
- Define UNDO Parameters
- Indexes on Foreign Keys
(FK)
- Rebuild Indexes
- Hints
- Nologging
- CBO Options
- Connect using IPC to Local Databases
- Space used per block
Memory Tuning
The total available memory on a system should be configured in such
a manner, that all components of the system function at optimum levels.
The following is a rule-of-thumb breakdown to help assist in memory
allocation for the various components in a system with an Oracle
back-end.
|
SYSTEM COMPONENT |
ALLOCATED % OF MEMORY |
|
Oracle SGA Components |
~ 50% |
|
Operating System +Related Components |
~15% |
|
User Memory |
~ 35% |
The following is a rule-of-thumb breakdown of the ~50% of memory
that is allocated for an Oracle SGA. These are good starting numbers
and
will potentially require fine-tuning, when the nature and access
patterns
of the application is determined.
|
ORACLE SGA COMPONENT |
ALLOCATED % OF MEMORY |
|
Database Buffer Cache |
~80% |
|
Shared Pool Area |
~12% |
|
Fixed Size + Misc |
~1% |
|
Redo Log Buffer |
~0.1% |
The following is an example to illustrate the above guidelines. In the
following example, it is assumed that the system is configured with 2
GB of memory, with an average of 100 concurrent sessions at any given
time. The application requires response times within a few seconds and
is mainly transactional. But it does support batch reports at regular
intervals.
|
SYSTEM COMPONENT |
ALLOCATED MEMORY(IN MB) |
|
Oracle SGA Components |
~1024 |
|
Operating System +Related Components |
~306 |
|
User Memory |
~694 |
In the aforementioned breakdown, approximately 694MB of memory will
be available for Program Global Areas (PGA) of all Oracle Server
processes. Again, assuming 100 concurrent sessions, the average memory
consumption for a given PGA should not exceed ~7MB. It should be noted
that SORT_AREA_SIZE is part of the PGA.
|
ORACLE SGA COMPONENT |
ALLOCATED MEMORY(IN MB) |
|
Database Buffer Cache |
~800 |
|
Shared Pool Area |
~128 - 188 |
|
Fixed Size + Misc |
~ 8 |
|
Redo Log Buffer |
~ 1 (average size 512K) |
Another Example
Let's assume that we have a high water mark of 100 connects
sessions to our Oracle database server. We multiply 100 by the total
area for each PGA memory region, and we can now determine the maximum
size of our SGA:
The total RAM demands for Oracle is 20 percent of total RAM for
MS-Windows, 10% of RAM for UNIX
Here we can see the values for sort_area_size and hash_area_size for
our Oracle database. To compute the value for the size of each PGA RAM
region, we can write a quick data dictionary query against the
v$parameter
view :
set pages 999;
column pga_size format 999,999,999
select
2048576 + a.value + b.value pga_size
from v$parameter a, v$parameter b
where a.name = 'sort_area_size'
and b.name = 'hash_area_size';
PGA_SIZE
------------
3,621,440
The output from this data dictionary query shows that every connected
Oracle session will use 3.6 megabytes of RAM memory for the Oracle PGA.
Now, if we were to multiply the number of connected users by the total
PGA demands for each connected user, we will know exactly how much RAM
memory in order to reserve for connected sessions.
Total RAM on Windows Server
1250 MB
Less:
Total PGA regions for 100 users: 362 MB
RAM reserved for Windows (20 percent) 500 MB
----------
862 MB
Hence, we would want to adjust the RAM to the data buffers in order to
make the SGA size less than 388 MB (that is 1250MB - 862 MB). Any SGA
size greater than 388 MB, and the server will start RAM paging,
adversely affecting the performance of the entire server. The final
task is to size the Oracle SGA such that the total memory involved does
not exceed 388 MB.
Examples
for UNIX Environments
0) for super machines with 4 GB of ram & swap 12 GB
set shmsys:shminfo_shmmax=3221225471
set shmsys:shminfo_shmmin=1
set shmsys:shminfo_shmmni=1024
set shmsys:shminfo_shmseg=100
set semsys:seminfo_semmni=1024
set semsys:seminfo_semmns=163840
set semsys:seminfo_semmsl=160
set semsys:seminfo_semmap=163840
set semsys:seminfo_semmnu=163840
set msgsys:msginfo_msgmap=163840
set msgsys:msginfo_msgmax=6144
set msgsys:msginfo_msgmni=640
set msgsys:msginfo_msgssz=64
set msgsys:msginfo_msgtql=640
set msgsys:msginfo_msgseg=32768
1) For high end machines with 2 GB of RAM & 6 GB of swap, we
recommend the following:
set shmsys:shminfo_shmmax=1073741824
set shmsys:shminfo_shmmin=1
set shmsys:shminfo_shmmni=250
set shmsys:shminfo_shmseg=100
set semsys:seminfo_semmni=750
set semsys:seminfo_semmns=75000
set semsys:seminfo_semmsl=100
set semsys:seminfo_semmap=75000
set semsys:seminfo_semmnu=75000
set msgsys:msginfo_msgmap=75000
set msgsys:msginfo_msgmax=6144
set msgsys:msginfo_msgmni=640
set msgsys:msginfo_msgssz=64
set msgsys:msginfo_msgtql=640
set msgsys:msginfo_msgseg=32768
2) For medium end machines with 1 GB of RAM & 3 GB of swap we
recommend the following:
set shmsys:shminfo_shmmax=536870912
set shmsys:shminfo_shmmin=1
set shmsys:shminfo_shmmni=150
set shmsys:shminfo_shmseg=50
set semsys:seminfo_semmni=500
set semsys:seminfo_semmns=50000
set semsys:seminfo_semmsl=100
set semsys:seminfo_semmap=50000
set semsys:seminfo_semmnu=50000
set msgsys:msginfo_msgmap=50000
set msgsys:msginfo_msgmax=2048
set msgsys:msginfo_msgmni=512
set msgsys:msginfo_msgssz=32
set msgsys:msginfo_msgtql=512
set msgsys:msginfo_msgseg=16384
3) For small end machines with 512 MB of RAM * 1.5 - 2 GB swap we
recommend the following:
set shmsys:shminfo_shmmax=134217728
set shmsys:shminfo_shmmin=1
set shmsys:shminfo_shmmni=100
set shmsys:shminfo_shmseg=50
set semsys:seminfo_semmni=250
set semsys:seminfo_semmns=25000
set semsys:seminfo_semmsl=100
set semsys:seminfo_semmap=25000
set semsys:seminfo_semmnu=25000
set msgsys:msginfo_msgmap=25000
set msgsys:msginfo_msgmax=1024
set msgsys:msginfo_msgmni=224
set msgsys:msginfo_msgssz=16
set msgsys:msginfo_msgtql=224
set msgsys:msginfo_msgseg=8192
Top Oracle Init.ora's Parameters
DB_BLOCK_SIZE - Size of the blocks (db_block_size x
db_cache_size=bytes for data). Setup on database creation. Generally
8K, for DW 16K
Reference for setting DB_BLOCK_LRU_LATCHES parameter
Default value: 1/2 the # of CPU's
MAX Value: Min 1, Max about 6 * max(#cpu's,#processor groups)
1)Oracle has found that a optimal value for this would be 2 X #CPU's and would recommend testing at this level.
2)Also setting this parameter to a multiple of #CPU's is important for Oracle to properly allocate and utilize working sets.
3)This value is hard coded in 9i
**IMPORTANT**
Increasing this parameter greater than 2 X #CPU's may have a negative impact on the system.
FREQUENTLY ASKED QUESTIONS
You have just upgraded to 8.0 or 8.1 and have found that there are 2 new parameters regarding DBWR. You are wondering what the differences are and which one you should use.
DBWR_IO_SLAVES
In Oracle7, the multiple DBWR processes were simple slave processes; i.e., unable to perform async I/O calls. In Oracle80, true asynchronous I/O is provided to the slave processes, if available. This feature is implemented via the init.ora parameter dbwr_io_slaves. With dbwr_io_slaves, there is still a master DBWR process and its slave processes. This feature is very similar to the db_writers in Oracle7, except the IO slaves are now capable of asynchronous I/O on systems that provide native async I/O, thus allowing for much better throughput as slaves are not blocked after the I/O call. I/O slaves for DBWR are allocated immediately following database open when the first I/O request is made.
DB_WRITER_PROCESSES
Multiple database writers is implemented via the init.ora parameter db_writer_processes. This feature was enabled in Oracle8.0.4, and allows true database writers; i.e., no master-slave relationship. With Oracle8 db_writer_processes, each writer process is assigned to a LRU latch set. Thus, it is recommended to set db_writer_processes equal to the number of LRU latches (db_block_lru_latches) and not exceed the number of CPUs on the system. For example, if db_writer_processes was set to four and db_lru_latches=4, then each writer process will manage its corresponding set.
Things to know and watch out for....
1. Multiple DBWRs and DBWR IO slaves cannot coexist. If both are enabled, then the following error message is produced: ksdwra("Cannot start multiple dbwrs when using I/O slaves.\n"); Moreover, if both parameters are enabled, dbwr_io_slaves will take precedence.
2. The number of DBWRs cannot exceed the number of db_block_lru_latches. If it does, then the number of DBWRs will be minimized to equal the number of db_block_lru_latches and the following message is produced in the alert.log during startup: ("Cannot start more dbwrs than db_block_lru_latches.\n"); However, the number of lru latches can exceed the number of DBWRs.
3. dbwr_io_slaves are not restricted to the db_block_lru_latches; i.e., dbwr_io_slaves >= db_block_lru_latches.
Should you use DB_WRITER_PROCESSES or DBWR_IO_SLAVES?
Although both implementations of DBWR processes may be beneficial, the general rule, on which option to use, depends on the following :
1) the amount write activity;
2) the number of CPUs (the number of CPUs is also indirectly related to the number LRU latch sets);
3) the size of the buffer cache;
4) the availability of asynchronous I/O (from the OS).
There is NOT a definite answer to this question but here are some considerations to have when making your choice. Please note that it is recommended to try BOTH (not simultaneously) against your system to determine which best fits the environment.
-- If the buffer cache is very large (100,000 buffers and up) and the application is write intensive, then db_writer_processes may be beneficial. Note, the number of writer processes should not exceed the number of CPUs.
-- If the application is not very write intensive (or even a DSS system) and async I/O is available, then consider a single DBWR writer process; If async I/O is not available then use dbwr_io_slaves.
-- If the system is a uniprocessor(1 CPU) then implement may want to use dbwr_io_slaves.
Implementing db_io_slaves or db_writer_processes comes with some overhead cost. Multiple writer processes and IO slaves are advanced features, meant for high IO throughput. Implement this feature only if the database environment requires such IO throughput. In some cases, it may be acceptable to disable I/O slaves and run with a single DBWR process.
Other Ways to Tune DBWR Processes
It can be easily seen that reducing buffer operations will be a direct benefit to DBWR and also help overall database performance. Buffer operations can be reduced by:
1) using dedicated temporary tablespaces
2) direct sort reads
3) direct Sqlloads
4) performing direct exports.
In addition, keeping a high buffer cache hit ratio will be extremely beneficial not only to the response time of applications, but the DBWR as well.
*DATA DICTIONARY cache miss ratio (Goal > 90%,
increase SHARED_POOL)
Contains:
Preparsed database procedures
Preparsed database triggers
Recently parsed SQL & PL/SQL requests
This is the memory allocated for the library and data dictionary cache
select sum(gets) Gets, sum(getmisses) Misses,
(1 - (sum(getmisses) /
(sum(gets) +
sum(getmisses))))*100 HitRatio
from v$rowcache;
* El HIT RATIO del SHARED_POOL_SIZE (LIBRARY CACHE hit
ratio) debe ser superior al 99%
column
namespace
heading "Library Object"
column
gets
format 9,999,999 heading "Gets"
column gethitratio format
999.99 heading "Get Hit%"
column
pins
format 9,999,999 heading "Pins"
column pinhitratio format
999.99 heading "Pin Hit%"
column
reloads format
99,999 heading "Reloads"
column invalidations format
99,999 heading "Invalid"
column db format a10
set pages 58 lines 80
select namespace, gets, gethitratio*100 gethitratio,
pins, pinhitratio*100 pinhitratio, RELOADS, INVALIDATIONS
from v$librarycache
/
If all Get Hit% (gethitratio in the view) except for indexes are greater than 80-90 percent, this is the desired state; the value for indexes is low because of the few accesses of that type of object. Notice that the Pin Hit% should ve also greater than 90% (except for indexes). The other goals of tuning this area are to reduce reloads to as small a value as possible (this is done by proper sizing and pinning) and to reduce invalidations. Invalidations happen when for one reason or another an object becomes unusable.
Guideline: In a system where there is no flushing increase the shared pool size in 20% increments to reduce reloads and invalidations and increase hit ratios.
select sum(pins) Executions, sum(pinhits) Execution_Hits,
((sum(pinhits) / sum(pins))
* 100) phitrat,
sum(reloads) Misses,
((sum(pins) / (sum(pins) +
sum(reloads))) * 100) RELOAD_hitrat
from v$librarycache;
* How much memory is left for SHARED_POOL_SIZE
col value for 999,999,999,999 heading "Shared Pool Size"
col bytes for 999,999,999,999 heading "Free Bytes"
select to_number(v$parameter.value) value, v$sgastat.bytes,
(v$sgastat.bytes/v$parameter.value)*100 "Percent Free"
from v$sgastat, v$parameter
where v$sgastat.name = 'free memory'
and v$parameter .name = 'shared_pool_size';
A better query:
select sum(ksmchsiz) Bytes, ksmchcls Status
from SYS.x$ksmsp
group by ksmchcls;
If there is free memory then there is no need to increase this parameter.
* Identifying objects reloaded into the SHARED POOL
again and again
select substr(owner,1,10) owner,substr(name,1,25) name,
substr(type,1,15) type, loads, sharable_mem
from v$db_object_cache
-- where owner not in ('SYS','SYSTEM') and
where loads > 1 and type in ('PACKAGE','PACKAGE
BODY','FUNCTION','PROCEDURE')
order by loads DESC;
* Large Objects NOT 'pinned' in Shared
Pool
To determine what large PL/SQL objects are currently loaded in the
shared pool and are not marked 'kept' (NOT pinned) and therefore may
causing a problem, execute the following query:
select name, sharable_mem
from v$db_object_cache
where sharable_mem > 10000
and (type = 'PACKAGE' or type = 'PACKAGE BODY' or type =
'FUNCTION'
or type = 'PROCEDURE')
and kept = 'NO';
SORT_AREA_SIZE = Indica la cantidad de memoria
reservada para sorts en bytes. Deberia haber pocos valores
(especialmente en disco), si no es asi, entonces incrementar
SORT_AREA_SIZE. Para saber si debo incrementar o no el parametro uso:
select name, value from v$sysstat where name like
'%sort%';
SORT_AREA_RETAINED_SIZE = is the size that the
SORT_AREA_SIZE is actually reduced to once the sort is complete.
This parameter should be set less than or equal to SORT_AREA_SIZE. If
we are going to
make a big import or use several batch processes, increase it. Just use
ALTER SESSION (for batch) or ALTER SYSTEM DEFERRED
(for imports). Remember to put back to its original value. Sorts
(memory) tells you the number of sorts done entirely in memory. Sorts
(disk) indicates
the number of sorts that required access to disk. The recommended
setting
for this parameter and SORT_AREA_SIZE is 65K-1MB.
Oracle Performance Checklist
As a consultant, I follow a standard procedure when I come into a new
shop with a database that I have never seen before. My goal is to
quickly identify and correct performance problems. Here is a summary of
the things that I look at first:
1 - Install STATSPACK first, and get hourly snaps working.
2 - Get an SQL access report (or plan9i.sql), a spreport during peak
times, and statspack_alert.sql output.
3 - Look for silver bullets:
- partial schema stats
- missing indexes
-optimizer_index_cost_adj=15
#10-15 for OLTP systems, 50 for DW #This adjusts the optimizer to
favor index access
-optimizer_index_caching=85
(depending on RAM for index caching,
around 85)
- optimizer_mode=first_rows (for
OLTP)
- hash_area_size too small (too
many nested loop joins)
-
parallel_automatic_tuning=TRUE When set to "on", this parameter
parallelizes full-table scans . Because parallel full-table scans are
very fast, the CBO will give a higher cost to index access and be
friendlier to full-table scans.
4 - Fully utilize server RAM - On a dedicated Oracle server, use all
extra RAM for db_cache_size less PGA's and 20% RAM
reserve for OS.
5 - Get the bottlenecks - See STATSPACK top 5 wait events - OEM
performance pack reports - TOAD reports
6 - Look for Buffer Busy Waits resulting from table/index freelist
shortages
7 - See if large-table full-table scans can be removed with well-placed
indexes
8 - If tables are low volatility, seek an MV that can
pre-join/pre-aggregate common queries. Turn-on automatic query rewrite
9 - Look for non-reentrant SQL - (literals values inside SQL from
v$sql) - If so, set cursor_sharing=force
10 - Monitor over time - The ongoing STATSPACK reports should show any
new performance problems.
INSTANCE TUNING
1) Library Cache Hit Ratio:
In the most basic terms, the library cache is a memory structure that
holds the parsed (ie. already examined to determine syntax correctness,
security privileges, execution plan, etc.) versions of SQL statements
that have been executed at least once. As new SQL statements arrive,
older
SQL statements will be pushed from the memory structure to provide
space
for the new statements. If the older SQL statements need to be
re-executed,
they will now have to be re-parsed. Also, a SQL statement that is not
exactly the same as an already parsed statement (including even
capitalization)
will be reparsed even though it may perform the exact same operation.
Parsing is an expensive operation, so the objective is to make the
memory structure large enough to hold enough parsed SQL statements to
avoid a large percentage of re-parsing.
Target: 99% or greater.
Value: SELECT (1 - SUM(reloads)/SUM(pins)) FROM v$librarycache;
Correction: Increase the SHARED_POOL_SIZE parameter (in bytes)
in the INIT.ORA file.
2) Dictionary Cache Hit Ratio:
The dictionary cache is the memory structure that holds the most
recently used contents of ORACLE's data dictionary, such as security
privileges, table structures, column data types, etc. This data
dictionary information is necessary for each and every parsing of a SQL
statement. Recalling that memory is around 300 times faster than disk,
it is needless to say that performance is improved by holding enough
data dictionary information in memory to significantly minimize disk
accesses.
Target: 90%
Value: SELECT (1 - SUM(getmisses)/SUM(gets)) FROM v$rowcache;
Correction: Increase the SHARED_POOL_SIZE parameter (in bytes)
in the INIT.ORA file.
3) Buffer Cache Hit Ratio:
The buffer cache is the memory structure that holds the most recently
used blocks read from disk, whether table, index, or other segment
type. As new data is read into the buffer cache, data that hasn't been
recently used is pushed out. Again recalling that memory is
approximately 300 times faster than disk, the objective is to hold
enough data in memory to minimize disk accesses. Note that data read
from tables through the use of indexes is held in the buffer cache much
longer than data read via full-table scans.
Target: 90% (although some shops find 80% or even 70% acceptable)
Value:
SELECT value FROM v$sysstat WHERE name = 'consistent gets';
SELECT value FROM v$sysstat WHERE name = 'db block gets';
SELECT value FROM v$sysstat WHERE name = 'physical reads';
Buffer cache hit ratio = 1 - physical reads/(consistent gets + db block
gets)
Correction: Increase the DB_CACHE_SIZE parameter in the INIT.ORA
file.
Other notes:
- Compare the values for "table scans" and "table access by rowid" in
the v$sysstat table to gain general insight into whether additional
indexing is needed. Tuning specific applications via indexing will
increase the "table access by rowid" value (ie. tables read through the
use of indexes) and decrease the "table scans" values. This effect
tends to improve the buffer cache hit ratio since a smaller volume of
data is read into the buffer cache from disk, so less previously cached
data is pushed out. (See the article on application tuning for more
details regarding indexing.)
- A low buffer cache hit ratio can very quickly lead to an I/O bound
situation, as more reads are required per period of time to provide the
requested data. When the reads/time period exceed the workload
supported by the disk subsystem, exponential performance degradations
can occur. (Please see the section on Operating System tuning.)
- Since the buffer cache will typically be the largest memory structure
allocated in the ORACLE instance, it is the structure most likely to
contribute
to O/S paging. If the buffer cache is sized such that the hit ratio is
90%, but excessive paging occurs at this setting, performance may be
better if the buffer cache were sized to achieve an 85% hit ratio.
Careful
analysis is necessary to balance the buffer cache hit ratio with the
O/S paging rate.
4) Sort Area Hit Ratio:
Sorts that are too large to be performed in memory are written to disk.
Once again, memory is about 300 times faster than disk, so for
instances where a large volume of sorting occurs (such as decision
support systems or data warehouses), sorting on disk can degrade
performance. The objective, of course, is to allow a significant
percentage of sorts to occur in memory.
Target: 90% (although many shops find 80% or less acceptable)
Value:
SELECT value FROM v$sysstat WHERE name = 'sorts (memory)';
SELECT value FROM v$sysstat WHERE name = 'sorts (disk)';
Sort area hit ratio = 1 - disk sorts/(memory sorts + disk sorts);
Correction: Increase the SORT_AREA_SIZE parameter (in
bytes) in the INIT.ORA file.
Other notes:
- With release 7.3 and above, setting the SORT_DIRECT_WRITES = TRUE
initialization parameter causes sorts to disk to bypass the buffer
cache, thus improving the buffer cache hit ratio.
- As with buffer cache hit ratio, examine the values for "table scans"
and "table access by rowid" in the v$sysstat table to determine
if additional indexing is needed. In some cases, the optimizer will
choose
to retrieve the rows in the correct order by using the index, thus
avoiding
a sort. In other cases, retrieval by index rather than full-table scan
tends to collect a smaller quantity of rows to be sorted, thus
increasing
the probability that the sort can occur in memory, which also tends to
improve the sort area hit ratio.
- Also, as with buffer cache hit ratio, sort area size (if very large)
can contribute to O/S paging. In general, sorting on disk should
be favored over excessive paging, as paging effects all memory
structures
(ORACLE and non-ORACLE) while sorting on disk only effects sorts
performed
by the ORACLE instance.
5) Redo Log Space Requests:
Redo logs (and archive logs if the ORACLE instance is run in ARCHIVELOG
mode) are transaction logs involving a variety of structures. The redo
log buffer is a memory structure into which changes are recorded as
they are applied to blocks in the buffer cache (including data, index,
rollback segments, etc.). Committed changes are synchronously flushed
to redo log
file members on disk, while uncommited changes are asynchronously
written
to redo log files. (This approach makes perfect sense on inspection. If
an instance crash occurs, commited changes are already written to the
redo logs on disk and are applied during instance recovery. Uncommited
changes in the redo log buffer not yet written to disk are lost, and
any
uncommited changes that have been written to disk are rolled-back
during
instance recovery.) A session performing an update and an immediate
commit
will not return until the committed change has been written to the redo
log buffer and flushed to the redo log files on disk. Redo log groups
are written to in a round-robin manner. When the mirrored members of
a redo log group become full, a log switch occurs, thus archiving one
member of the redo log group (if ARCHIVELOG mode is TRUE), then
clearing the members of that redo log group. Note that a checkpoint
also occurs at least on each redo log switch. In most basic form, the
redo log buffer should be large enough that no waits for available
space in the memory structure occur while changes are written to redo
log files. The redo log file size should be large enough that the redo
log buffer does not fill during a redo log switch. Finally, there
should be enough redo log groups that the archiving and clearing of
filled redo logs does not cause waits for redo log switches, thus
causing the redo log buffer to fill. The inability to write changes to
the redo log buffer because it is full is reported as redo log space
requests in the v$sysstat table.
Target: 0
Value: SELECT value FROM v$sysstat WHERE name = 'redo log space
requests';
Correction:
- Increase the LOG_BUFFER parameter (in bytes) in the INIT.ORA file.
- Increase the redo log size.
- Increase the number of redo log groups.
Other notes:
- The default configuration of small redo log size and two redo log
groups is seldom sufficient. Between 4 and 10 groups typically yields
adequate results, depending on the particular archive log destination
(whether a single disk, RAID array, or tape). Size will be very
dependent upon the specific application characteristics and throughput
requirements, and can range from less than 10 Mb to 500 Mb or greater.
- Since redo log sizes and groups can be changed without a
shutdown/restart of the instance, increasing the redo log size and
number of groups is typically the best area to start tuning for
reduction of redo log space requests. If increasing the redo log size
and number of groups appears to have little impact on redo log space
requests, then increase the LOG_BUFFER initialization parameter.
6) Redo Buffer Latch Miss Ratio:
One of the two types of memory structure locking mechanisms used by an
ORACLE instance is the latch. A latch is a locking mechanism that is
implemented entirely within the executable code of the instance (as
opposed to an enqueue, see below). Latch mechanisms most likely to
suffer from contention involve requests to write data into the redo
log buffer. To serve the intended purpose, writes to the redo log
buffer must be serialized (ie. one process locks the buffer, writes to
it, then unlocks it, a second process locks, writes, and unlocks, etc.,
while other
processes wait for their chance to acquire these same locks). There are
four different groupings applicable to redo buffer latches: redo
allocation
latches and redo copy latches, each with immediate and willing-to-wait
priorities. Redo allocation latches are acquired by small redo entries
(having an entry size smaller than or equal to the
LOG_SMALL_ENTRY_MAX_SIZE initialization parameter) and utilize only a
single CPU's resources for execution. Redo copy latches are requested
by larger redo entries (entry size larger than the
LOG_SMALL_ENTRY_MAX_SIZE), and take advantage of multiple CPU's for
execution. Recall from above that committed changes are synchronously
written to redo logs on disk: these entries require an immediate latch
of the appropriate type. Uncommitted changes are asynchronously written
to redo log files, thus they attempt to acquire a willing-to-wait latch
of the appropriate type. Below, each category of redo buffer latch will
be considered seperately.
- Redo allocation immediate and willing-to-wait latches:
Target: 1% or less
Value (immediate):
SELECT a.immediate_misses/(a.immediate_gets + a.immediate_misses +
0.000001)
FROM v$latch a, v$latchname b
WHERE b.name = 'redo allocation' AND b.latch# = a.latch#;
Value (willing-to-wait):
SELECT a.misses/(a.gets + 0.000001)
FROM v$latch a, v$latchname b
WHERE b.name = 'redo allocation' AND b.latch# = a.latch#;
Correction: Decrease the LOG_SMALL_ENTRY_MAX_SIZE parameter in
the INIT.ORA file.
Other notes:
- By making the max size for a redo allocation latch smaller, more redo
log buffer writes qualify for a redo copy latch instead, thus better
utilizing multiple CPU's for the redo log buffer writes. Even though
memory structure manipulation times are measured in nanoseconds, a
larger write still takes longer than a smaller write. If the size for
remaining writes done via redo allocation latches is small enough, they
can be completed with little or no redo allocation latch contention.
- On a single CPU node, all log buffer writes are done via redo
allocation latches. If log buffer latches are a significant bottleneck,
performance can benefit from additional CPU's (thus enabling redo copy
latches) even if the CPU utilization is not an O/S level bottleneck.
- In the SELECT statements above, an extremely small value is added to
the divisor to eliminate potential divide-by-zero errors.
- Redo copy immediate and willing-to-wait latches:
Target: 1% or less
Value (immediate):
SELECT a.immediate_misses/(a.immediate_gets + a.immediate_misses +
0.000001)
FROM v$latch a, v$latchname b
WHERE b.name = 'redo copy' AND b.latch# = a.latch#;
Value (willing-to-wait):
SELECT a.misses/(a.gets + 0.000001)
FROM v$latch a, v$latchname b
WHERE b.name = 'redo copy' AND b.latch# = a.latch#;
Correction: Increase the LOG_SIMULTANEOUS_COPIES parameter in
the INIT.ORA file.
Other Notes:
- Essentially, this initialization parameter is the number of redo copy
latches available. It defaults to the number of CPU's (assuming a
multiple CPU node). Oracle Corporation recommends setting it as large
as 2 times the number of CPU's on the particular node, although quite a
bit of experimentation may be required to get the value adjusted
in a suitable manner for any particular instance's workload. Depending
on CPU capability and utilization, it may be beneficial to set this
initialization parameter smaller or larger than 2 X #CPU's.
- Recall that the assignment of log buffer writes to either redo
allocation latches or redo copy latches is controlled by the maximum
log buffer write size allowed for a redo allocation latch, and is
specified in the LOG_SMALL_ENTRY_MAX_SIZE initialization parameter.
Recall also that redo copy latches apply only to multiple CPU hosts.
7) Enqueue Waits:
The second of the two types of memory structure locking mechanisms used
by an ORACLE instance is the enqueue. As opposed to a latch, an enqueue
is a lock implemented through the use of an operating system call,
rather than entirely within the Instance's executable code. Exactly
what operations use locks via enqueues is not made sufficiently
clear from any Oracle documentation (or at least none that the author
has seen), but the fact that enqueues waits do degrade instance
performance
is reasonably clear. Luckily, tuning enqueues is very straight-forward.
Target: 0
Value: SELECT value FROM v$sysstat WHERE name = 'enqueue waits';
Correction: Increase the ENQUEUE_RESOURCES parameter in the
INIT.ORA file.
8) Checkpoint Contention:
A checkpoint is the process of flushing all changed data blocks (table,
index, rollback segments, etc.) held in the buffer cache to their
corresponding datafiles on disk. This process occurs during each redo
log switch, each time the number of database blocks specified in the
LOG_CHECKPOINT_INTERVAL initialization parameter is reached, and each
time the number of seconds specified in the LOG_CHECKPOINT_TIMEOUT is
reached. (Also, checkpoints occur during a NORMAL or IMMEDIATE
SHUTDOWN,
when a tablespace is placed in BACKUP mode, or when an ALTER SYSTEM
CHECKPOINT
is manually issued, but these occurrences are usually outside the scope
of normal daytime operation.) Depending on the number of changed blocks
in the buffer cache, a checkpoint can take considerable time to
complete.
Since this process is essentially done asynchronously, user sessions
performing work will typically not have to wait for a checkpoint to
complete. However checkpoints can effect overall system performance
since they are fairly resource intensive operations, even though they
occur in the background. Checkpoints are, of course, absolutely
necessary,
but it is quite possible for one checkpoint to begin (because of
LOG_CHECKPOINT_INTERVAL
or LOG_CHECKPOINT_TIMEOUT settings) and partially complete, then be
rolled-back
because another checkpoint was issued (perhaps because of a redo log
switch). It is desirable to avoid this checkpoint contention because
it wastes considerable resources that can be used by other processes.
Checkpointing statistics are readily available in the v$sysstat table,
and the contention is fairly simple to determine.
Target: 1 or less
Value:
SELECT value FROM v$sysstat WHERE name = 'background checkpoints
started';
SELECT value FROM v$sysstat WHERE name = 'background checkpoints
completed';
Checkpoints rolled-back = checkpoints started - checkpoints completed;
Correction:
- Increase the LOG_CHECKPOINT_TIMEOUT parameter (in seconds) in the
INIT.ORA file, or set it to 0 to disable time-based checkpointing. If
time-based checkpointing is not disabled, set it to checkpoint once per
hour or more.
- Increase the LOG_CHECKPOINT_INTERVAL parameter (in db blocks) in the
INIT.ORA file, or set it to an arbitrarily large value so that
change-based checkpoints will only occur during a redo log switch.
- Examine the redo log size and the resulting frequency of redo log
switches.
Other notes: Note that regardless of the checkpoint frequency,
no data is lost in the event of an instance crash. All changes are
recorded to the redo logs and would be applied during instance recovery
on the next startup, so checkpoint frequency will impact the time
required for instance recovery. Presented below is a typical scenario:
- Set the LOG_CHECKPOINT_INTERVAL to an arbitrarily large value, set
the LOG_CHECKPOINT_TIMEOUT to 2 hours, and size the redo logs so that a
log switch will normally occur once per hour. During times of heavy
OLTP activity, a change-based log switch will occur approximately once
per hour, and no time-based checkpoints will occur. During periods of
light OLTP activity, a time-based checkpoint will occur at least once
every two hours, regardless of the number of changes. Setting the
LOG_CHECKPOINT_INTERVAL arbitrarily large allows change-based
checkpoint frequency to be adjusted during periods of heavy use by
re-sizing the redo logs on-line rather than adjusting the
initialization parameter and performing an instance shutdown/restart.
9) Rollback Segment Contention:
Rollback segments are the structures into which undo information for
uncommited changes are temporarily stored. This behavior serves two
purposes. First, a session can remove a change that was just issued by
simply issuing a ROLLBACK rather than a COMMIT. Second, read
consistency is established because a long-running SELECT statement
against a table that is constantly being updated (for example) will get
data that is consistent
with the start time of the SELECT statement by reading undo information
from the appropriate rollback segment. (Otherwise, the answer returned
by the long-running SELECT would vary depending on whether that
particular
block was read before the update occurred, or after.) Rollback segments
become a bottleneck when there are not enough to handle the load of
concurrent
activity, in which case, sessions will wait for write access to an
available rollback segment. Some waits for rollback segment data blocks
or header blocks (usually header blocks) will always occur, so criteria
for tuning is to limit the waits to a very small percentage of the
total
number of all data blocks requested. Note that rollback segments
function
exactly like table segments or index segments: they are cached in the
buffer cache, and periodically checkpointed to disk.
Target: 1% or less
Value:
Rollback waits = SELECT max(count) FROM v$waitstat
WHERE class IN ('system undo header', 'system undo block','undo
header', 'undo block')
GROUP BY class;
Block gets = SELECT sum(value) FROM v$sysstat WHERE name IN
('consistent gets','db block gets');
Rollback segment contention ratio = rollback waits / block gets
Correction: Create additional rollback segments.
10) Freelist contention:
In each table, index, or other segment type, the first one or more
blocks contain one or more freelists. The freelist(s) identify the
blocks in that segment that have free space available and can accept
more data. Any INSERT, UPDATE, or DELETE activity will cause the
freelist(s) to be accessed. Change activity with a high level of
concurrency may cause
waits to access to these freelist(s). This is seldom a problem in
decision
support systems or data warehouses (where updates are processed as
nightly
single-session batch jobs, for example), but can become a bottleneck
with OLTP systems supporting large numbers of users. Unfortunately,
there
are no initialization parameters or other instance-wide settings to
correct freelist contention: this must be corrected on a table by table
basis by re-creating the table with additional freelists and/or by
modifying
the PCT_USED parameter. (Please see the article on storage management.)
However, freelist contention can be measured at the instance level.
Some
freelist waits will always occur; the objective is to limit the
freelist
waits to a small percentage of the total blocks requested.
Target: 1% or less
Value:
Freelist waits = SELECT count FROM v$waitstat WHERE class = 'free list';
Block gets = SELECT sum(value) FROM v$sysstat WHERE name IN
('consistent gets','db block gets');
Freelist contention ratio = Freelist waits / block gets
Correction: No method for instance-level correction. Please see
the article on storage management.
11) Oracle Session hogs
If the complaint of poor performance is current, then the connected
sessions are one of the first things to check to see which users are
impacting the system in undesirable ways. There are a couple of
different avenues to take here. First, you can get an idea of the
percentage that each session is/has taken up with respect to I/O. One
rule of thumb is that if any session is currently consuming 50% or more
of the total I/O, then that session and its SQL need to be investigated
further to determine what activity it is engaged in. If you are a DBA
that is just concerned with physical I/O, then the physpctio.sql query
will provide the information you need:
This script queries the sys.v_$statname, sys.v_$sesstat,
sys.v_$session, and sys.v_$bgprocess views.
select sid, username,
round(100 *
total_user_io/total_io,2) tot_io_pct
from (select b.sid sid,nvl(b.username,p.name) username,
sum(value) total_user_io
from sys.v_$statname c,
sys.v_$sesstat a,
sys.v_$session b, sys.v_$bgprocess p
where
a.statistic#=c.statistic# and
p.paddr (+) = b.paddr and
b.sid=a.sid and
c.name in ('physical reads',
'physical writes',
'physical writes direct',
'physical reads direct',
'physical writes direct (lob)',
'physical reads direct (lob)')
group by b.sid, nvl(b.username,p.name)),
(select sum(value)
total_io
from
sys.v_$statname c, sys.v_$sesstat a
where
a.statistic#=c.statistic# and
c.name in ('physical reads',
'physical writes',
'physical writes direct',
'physical reads direct',
'physical writes direct (lob)',
'physical reads direct (lob)'))
order by 3 desc;
Regardless of which query you use, the output might resemble something
like the following:
SID USERNAME TOT_IO_PCT
---- -------- -------------------
9 USR1 71.26
20 SYS 15.76
5 SMON 7.11
2 DBWR 4.28
12 SYS 1.42
6 RECO .12
7 SNP0 .01
10 SNP3 .01
11 SNP4 .01
8 SNP1 .01
1 PMON 0
3 ARCH 0
4 LGWR 0
In the above example, a DBA would be prudent to examine the USR1
session to see what SQL calls they are making. You can see that the
above queries are excellent weapons that you can use to quickly
pinpoint
problem I/O sessions.
* Check DB Parameters
select substr(name,1,20), substr(value,1,40), isdefault,
isses_modifiable, issys_modifiable
from v$parameter
where issys_modifiable <> 'FALSE'
or isses_modifiable <> 'FALSE'
order by name;
* The SQL sentences must be the same in order to re-use them in memory.
* Size of Database
compute sum of bytes on report
break on report
Select tablespace_name, sum(bytes) bytes
From dba_data_files
Group by tablespace_name;
* How much Space is Left?
compute sum of bytes on report
Select tablespace_name, sum(bytes) bytes
From dba_free_space
Group by tablespace_name;
* Memory Values.
select substr(name,1,35) name, substr(value,1,25) value
from v$parameter
where name in
('db_cache_size','db_block_size','shared_pool_size','sort_area_size');
* Identify the SQL responsible for the most BUFFER HITS and/or
DISK READS. If I want to see what is on SQL AREA:
SELECT SUBSTR(sql_text,1,80)
Text, disk_reads, buffer_gets, executions
FROM v$sqlarea
WHERE executions >
0
AND buffer_gets >
100000
and DISK_READS >
100000
ORDER BY (DISK_READS * 100) +
BUFFER_GETS desc;
The column BUFFER_GETS is the total number of times the SQL statement
read a database block from the buffer cache in the SGA. Since almost
every SQL operation passes through the buffer cache, this value
represents the best metric for determining how much work is being
performed. It is
not perfect, as there are many direct-read operations in Oracle that
completely
bypass the buffer cache. So, supplementing this information, the column
DISK_READS is the total number times the SQL statement read database
blocks from disk, either to satisfy a logical read or to satisfy a
direct-read.
Thus, the formula:
(DISK_READS * 100) + BUFFER_GETS
is a very adequate metric of the amount of work being performed by a
SQL statement. The weighting factor of 100 is completely arbitrary, but
it reflects the fact that DISK_READS are inherently more expensive than
BUFFER_GETS to shared memory.
Patterns to look for
DISK_READS close to or equal to BUFFER_GETS This indicates that most
(if not all) of the gets or logical reads of database blocks are
becoming
physical reads against the disk drives. This generally indicates a
full-table
scan, which is usually not desirable but which usually can be quite
easy
to fix.
* Finding the top 25 SQL
declare
top25 number;
text1 varchar2(4000);
x number;
len1 number;
cursor c1 is
select buffer_gets,
substr(sql_text,1,4000)
from v$sqlarea
order by buffer_gets desc;
begin
dbms_output.put_line('Gets'||'
'||'Text');
dbms_output.put_line('----------'||
'
'||'----------------------');
open c1;
for i in 1..25 loop
fetch c1 into top25, text1;
dbms_output.put_line(rpad(to_char(top25),9)||
'
'||substr(text1,1,66));
len1:=length(text1);
x:=66;
while len1 > x-1 loop
dbms_output.put_line('"
'||substr(text1,x,66));
x:=x+66;
end loop;
end loop;
end;
/
* Displays the porcentage of SQL executed that did NOT incur an
expensive hard parse. So a low number may indicate a literal
SQL or other sharing problem.
Ratio success is dependant on your development environment. OLTP
should be 90 percent.
select 100 *
(1-a.hard_parses/b.executions) noparse_hitratio
from (select value hard_parses
from v$sysstat
where name = 'parse count (hard)'
) a
,(select value
executions
from v$sysstat
where name = 'execute count') b;
* HIT RATIO BY SESSION:
column HitRatio format 999.99
select substr(Username,1,15)
username, Consistent_Gets,
Block_Gets, Physical_Reads, 100*(Consistent_Gets+Block_Gets-Physical_Reads)/(Consistent_Gets+Block_Gets) HitRatio
from V$SESSION, V$SESS_IO
where V$SESSION.SID = V$SESS_IO.SID
and (Consistent_Gets+Block_Gets)>0
and Username is not null;
* IO PER DATAFILE:
select substr(DF.Name,1,40)
File_Name,
FS.Phyblkrd Blocks_Read,
FS.Phyblkwrt Blocks_Written,
FS.Phyblkrd+FS.Phyblkwrt Total_IOs
from V$FILESTAT FS, V$DATAFILE DF
where DF.File#=FS.File#
order by FS.Phyblkrd+FS.Phyblkwrt
desc;
* Schema's Report
select substr(username,1,10)
"Username", created "Created",
substr(granted_role,1,25) "Roles",
substr(default_tablespace,1,15) "Default TS",
substr(temporary_tablespace,1,15) "Temporary TS"
from sys.dba_users,
sys.dba_role_privs
where username = grantee (+)
order by username;
* Free space on TABLESPACES:
select
substr(a.tablespace_name,1,10) tablespace,
round(sum(a.total1)/1024/1024, 1) Total,
round(sum(a.total1)/1024/1024,
1)-
round(sum(a.sum1)/1024/1024,
1) used,
round(sum(a.sum1)/1024/1024,
1) Free,
round(sum(a.sum1)/1024/1024,1)*100/round(sum(a.total1)/1024/1024,1)
porciento_fr,
round(sum(a.maxb)/1024/1024,
1) Largest,
max(a.cnt) Fragment
from (select tablespace_name, 0
total1, sum(bytes) sum1,
max(bytes)
MAXB,count(bytes) cnt
from dba_free_space
group by tablespace_name
union
select tablespace_name,
sum(bytes) total1, 0, 0, 0
from dba_data_files
group by tablespace_name) a
group by a.tablespace_name;
* Segments whose next extent can't fit
select substr(owner,1,10) owner,
substr(segment_name,1,40)
segment_name, substr(segment_type,1,10) segment_type, next_extent
from dba_segments
where next_extent>
(select sum(bytes) from
dba_free_space
where tablespace_name =
dba_segments.tablespace_name);
* Find Tables/Indexes fragmented into > 15 pieces
Select substr(owner,1,8) owner,
substr(segment_name,1,42) segment_name,
segment_type, extents
From dba_segments
Where extents > 15;
* COALESCING FREE SPACE = Los distintos bloques libres
(chunks)
que sean adjuntos se pueden juntar en uno mas grande. Inspecciono con:
select file_id, block_id, blocks,
bytes from dba_free_space
where tablespace_name = 'xxx'
order by 1,2;
Esto me devuelve una lista de resultados. Si file_id de 2 filas es
igual y el block_id + blocks = Block_id de la fila siguiente, entonces
los puedo juntar.
Se hace con ALTER TABLESPACE XX COALESCE;
* Quick Script to coalesce all the tablespaces tablespaces
set echo off pages 0 trimsp off
feed off
spool coalesce.sql
select 'alter tablespace
'||tablespace_name||' coalesce;'
from sys.dba_tablespaces
where tablespace_name not in
('TEMP','ROLLBACK');
spool off
@coalesce.sql
host rm coalesce.sql
* Information about a Table
Select Table_Name,
Initial_Extent, Next_Extent,
Pct_Free, Pct_Increase
From dba_tables
Where Table_Name =
upper('&Table_name');
* Information about an Index:
Select Index_name,
Initial_Extent, Next_Extent
From Dba_indexes
Where Index_Name =
upper('&Index_name');
* Fixing Table Fragmentation
Example: CUSTOMER Table is fragmented
Currently in 22 Extents of 1M each.
(Can be found by querying DBA_EXTENTS)
CREATE TABLE CUSTOMER1
TABLESPACE NEW
STORAGE (INITIAL 23M NEXT 2M PCTINCREASE 0)
AS SELECT * FROM CUSTOMER;
DROP TABLE CUSTOMER;
RENAME CUSTOMER1 TO CUSTOMER;
(Create all necessary privileges,grants, etc.)
* PINS and UNPIN objects:
execute
dbms_shared_pool_keep('object_name','P o R o Q');
Use 'P' for procedure (or funcion), 'R' for trigger and 'Q' for
sequence.
Previously I should run the package dbmspool.sql y prvtpool.plb located
on
$ORACLE_HOME/rdbms/admin as sys or internal and grant
execute on dbms_shared_pool.
exec
dbms_shared_pool.unkeep('SCOTT.TEMP','P');
If you want to have a table in memory, add the CACHE word at the end of
the creation script. You can also use the /*+ cache(table) */
hint.
To Load the code automatically on each startup::
1- Create the following Trigger
create or replace trigger
pin_packs
after startup on database
begin
--You can interrogate the v$db_object_cache view to see the most
frequently used packages
--
Application-specific packages
--
Oracle-supplied software packages
dbms_shared_pool.keep('DBMS_ALERT');
dbms_shared_pool.keep('DBMS_DDL');
dbms_shared_pool.keep('DBMS_DESCRIBE');
dbms_shared_pool.keep('DBMS_LOCK');
dbms_shared_pool.keep('DBMS_OUTPUT');
dbms_shared_pool.keep('DBMS_PIPE');
dbms_shared_pool.keep('DBMS_SESSION');
dbms_shared_pool.keep('DBMS_STANDARD');
dbms_shared_pool.keep('DBMS_UTILITY');
dbms_shared_pool.keep('STANDARD');
-- Son usados estos?
dbms_shared_pool.keep('DBMS_SYS_SQL');
dbms_shared_pool.keep('DBMS_SQL');
dbms_shared_pool.keep('DBMS_JOB');
end;
2- Run the following Script to check pinned/unpinned packages
SELECT
substr(owner,1,10)||'.'||substr(name,1,35) "Object Name",
' Type: '||substr(type,1,12)||
' size: '||sharable_mem ||
' execs: '||executions||
' loads: '||loads||
' Kept: '||kept
FROM v$db_object_cache
WHERE type in
('TRIGGER','PROCEDURE','PACKAGE BODY','PACKAGE')
-- AND
executions > 0
ORDER BY executions desc,
loads desc,
sharable_mem desc;
* ROW_CHAINING
* To find out chained rows
ANALYZE TABLE TEST ESTIMATE STATISTICS;
Then from DBA_TABLES,
SELECT (CHAIN_CNT / NUM_ROWS) * 100 FROM DBA_TABLES WHERE TABLE_NAME
= upper('&Table_name');
This will give us the chained rows as a percentage of the total number of rows in that table. If this percentage is high near 5% and the row doe not contain LONG or similar datatype or the row can be contained inside one single data block then PCTFREE should definitely be decreased.
2 DISKS:
1- exec, index, redo logs, export files, control files
2- data, rollback segments, temp, archive log files, control files
3 DISKS
Disk 1: SYSTEM tablespace, control file, redo log
Disk 2: INDEX tablespace, control file, redo log, ROLLBACK tablespace
Disk 3: DATA tablespace, control file, redo log
or
Disk 1: SYSTEM tablespace, control file, redo log
Disk 2: INDEX tablespace, control file, redo log
Disk 3: DATA tablespace, control file, redo log, ROLLBACK tablespace
4 DISKS
1- exec, redo logs, export files, control files
2- data, temp, control files
3- indexes, control files
4- archive logs, rollback segs, control files
5 DISKS
1- exec, redo logs, system tablespace, control files
2- data, temp, control files
3- indexes, control files
4- rollback segments, export, control files
5- archive, control files
| Oracle Corporation strongly recommends that you use the DBMS_STATS package rather than ANALYZE to collect optimizer statistics. That package lets you collect statistics in parallel, collect global statistics for partitioned objects, and fine tune your statistics collection in other ways. Further, the cost-based optimizer will eventually use only statistics that have been collected by DBMS_STATS. |
Full documentation of CBO and how
it works
However, you must use the ANALYZE statement rather than DBMS_STATS for
statistics collection not related to the cost-based optimizer, such as:
* To use the VALIDATE or LIST CHAINED ROWS clauses
* To collect information on freelist blocks
The DBMS_STATS package can gather statistics on
indexes, tables,
columns, and partitions, as well as statistics on all schema objects in
a schema or database. The statistics-gathering operations can run
either serially or in parallel (DATABASE/SCHEMA/TABLE only)
Previous to 8i, you would be using the ANALYZE ... methods. However 8i onwards, using ANALYZE for this purpose is not recommended because of various restrictions; for example:
Example:
execute dbms_stats.gather_table_stats
(ownname => 'SCOTT'
, tabname => 'DEPT'
, partname=> null
, estimate_percent => 20
, degree => 5
, cascade => true
, options => 'GATHER AUTO');
execute dbms_stats.gather_schema_stats
(ownname => 'SCOTT'
, estimate_percent => 10
, degree => 5
, cascade => true);
execute dbms_stats.gather_database_stats
(estimate_percent => 20
, degree => 5
, cascade => true);
There are several
values for the options parameter that we
need to know about:
- gather - re-analyzes the whole
schema.
- gather empty - Only analyze tables that
have no existing statistics.
- gather stale - Only re-analyze tables with
more than 10% modifications (inserts, updates, deletes). The table
should be in monitor status first.
- gather auto - This will re-analyze objects
which currently have no statistics and objects with stale
statistics.The table should be in monitor status first.
Using gather auto is like
combining gather stale and gather empty.
Note that both gather stale and gather auto require
monitoring. If you issue the "alter table xxx monitoring" command,
Oracle tracks changed tables with the dba_tab_modifications view. Below
we see that the exact number of inserts, updates and deletes are
tracked since the last analysis of statistics.
The most interesting of these options is the gather stale
option. Because all statistics will become stale quickly in a robust
OLTP database, we must remember the rule for gather stale is
> 10% row change (based on num_rows at statistics collection time).
Hence, almost every table except read-only tables will be re-analyzed
with the gather stale option. Hence, the gather stale option is
best for systems that are largely read-only. For example, if only 5% of
the database tables get significant updates, then only 5% of the tables
will be re-analyzed with the "gather stale" option.
The CASCADE => TRUE option causes all indexes for the tables to also
be analyzed. In Oracle 10g, set CASCADE to AUTO_CASCADE to let
Oracle
decide whether or not new index statistics are needed.
The DEGREE Option
Note that you can also parallelize the collection of statistics because
the CBO does full-table and full-index scans. When you set degree=x,
Oracle will invoke parallel query slave processes to speed up table
access. Degree is usually about equal to the number of CPUs, minus 1
(for the OPQ query coordinator).
In Oracle 10g, set DEGREE to
DBMS_STATS.AUTO_DEGREE to let Oracle select the appropriate degree of
parallelism.
STATISTICS
FOR THE DATA DICTIONARY
New in Oracle Database 10g is the ability to gather statistics for the
data dictionary. The objective is to enhance the performance of
queries. There are two basic types of dictionary base
tables.
The statistics for normal base tables are gathered using
GATHER_DICTIONARY STATISTICS. They may also be gathered using
GATHER_SCHEMA_STATS for the SYS schema. Oracle recommends
gathering
these statistics at a similar frequency as your other database objects.
Statistics for fixed objects (the V$ views on the X$ tables) are
gathered using the GATHER_FIXED_OBJECT_STATS procedure. The
initial
collection of these statistics is normally sufficient. Repeat
only if
workload characteristics have changed dramatically. The SYSDBA
privilege or ANALYZE ANY DICTIONARY and ANALYZE ANY privileges are
required to execute the procedures for gathering data dictionary
statistics.
SQL Source - Dynamic
Method
DECLARE
sql_stmt VARCHAR2(1024);
BEGIN
FOR tab_rec IN (SELECT owner,table_name
FROM all_tables WHERE owner like UPPER('&1') ) LOOP
sql_stmt := 'BEGIN
dbms_stats.gather_table_stats
(ownname => :1,
tabname => :2,
partname => null,
estimate_percent => 10,
degree => 3 ,
cascade => true); END;' ;
EXECUTE IMMEDIATE sql_stmt USING tab_rec.owner, tab_rec.table_name ;
END LOOP;
END;
/
Analyze
Option
- Estimate over all rows
DBMS_UTILITY.ANALYZE_SCHEMA('userid', 'COMPUTE');
- Estimate 20% of all rows for a specific Schema
DBMS_UTILITY.ANALYZE_SCHEMA('userid', 'ESTIMATE',NULL,20);
- Estimate 20% of a table
DBMS_UTILITY.ANALYZE_SCHEMA('TABLE' , 'schema', 't_name',
'ESTIMATE',null,20);
or
ANALYZE TABLE table ESTIMATE
STATISTICS sample 20 percent;
- Estimate 20% of an index
DBMS_UTILITY.ANALYZE_SCHEMA('INDEX' , 'schema', 'i_name', 'COMPUTE';
- Estimate 1000 rows of all the tables for a schema
DBMS_UTILITY.ANALYZE_SCHEMA
('userid', 'ESTIMATE', 100000);
or
ANALYZE TABLE table ESTIMATE
STATISTICS sample 5000 rows;
- Delete all stats
DBMS_UTILITY.ANALYZE_SCHEMA
('userid', 'DELETE');
or
ANALYZE TABLE table DELETE
STATISTICS;
Define Undo
Parameters
When you are working with UNDO (instead of ROLLBACK), there are two
important things to consider:
To get information of your current settings you can use the
following query:
set serveroutput on
DECLARE
tsn
VARCHAR2(40);
tss
NUMBER(10);
aex
BOOLEAN;
unr
NUMBER(5);
rgt
BOOLEAN;
retval BOOLEAN;
BEGIN
retval :=
dbms_undo_adv.undo_info(tsn, tss, aex, unr, rgt);
dbms_output.put_line('UNDO
Tablespace is: ' || tsn);
dbms_output.put_line('UNDO
Tablespace size is: ' || TO_CHAR(tss));
IF aex THEN
dbms_output.put_line('Undo Autoextend is set to: TRUE');
ELSE
dbms_output.put_line('Undo Autoextend is set to: FALSE');
END IF;
dbms_output.put_line('Undo
Retention is: ' || TO_CHAR(unr));
IF rgt THEN
dbms_output.put_line('Undo Guarantee is set to: TRUE');
ELSE
dbms_output.put_line('Undo Guarantee is set to: FALSE');
END IF;
END;
/
You can choose to allocate a specific size for the UNDO tablespace
and
then set the UNDO_RETENTION parameter to an optimal value according to
the UNDO size and the database activity. If your disk space is limited
and you do not want to allocate more space than necessary to the UNDO
tablespace, this is the way to proceed. If you are not limited by
disk
space, then it would be better to choose the UNDO_RETENTION time that
is best for you (for FLASHBACK, etc.). Allocate the appropriate size to
the UNDO tablespace according to the database activity.
This tip help you get the information you need whatever the method you
choose.
Creating Indexes on Foreign Keys
Problem
Creating foreign keys constraints on tables increases the integrity of
your data by preventing rows from being inserted into detail (sometimes
called child tables) table that do not have a matching row in a master
(also called the parent table) table.
The following code creates two tables: "EMP" and "DEPT". Both tables
declare a primary key and the table "EMP" declares a foreign key
constraint between "EMP" and "DEPT".
CREATE TABLE dept (
deptno NUMBER(2)
CONSTRAINT PK_DEPT PRIMARY KEY,
dname
VARCHAR2(14),
loc VARCHAR2(13)
);
CREATE TABLE emp (
empno NUMBER(4) CONSTRAINT PK_EMP PRIMARY KEY,
ename VARCHAR2(10),
job VARCHAR2(9),
mgr NUMBER(4),
hiredate
DATE,
sal NUMBER(7,2),
comm NUMBER(7,2),
deptno NUMBER(2)
);
ALTER TABLE EMP ADD CONSTRAINT FK_EMP_DEPT
FOREIGN KEY (deptno)
REFERENCES dept (deptno);
Once this constraint is enabled, attempting to insert an "EMP" record
with an invalid DEPTNO, or trying to delete a DEPTNO row that has
matching "EMP" records, will generate an error. However, in order to
preserve integrity during the operation, Oracle needs to apply a full
"table-level" lock (as opposed to the usual row-level locks) to the
child table when the parent table is modified.
Solution
By creating an index on the foreign key of the child table, these
"table-level" locks can be avoided. (for instance, creating a foreign
key on "EMP.DEPTNO").
CREATE INDEX FK_EMP_DEPT
ON emp(deptno)
TABLESPACE indx;
Keep in mind that you will often be creating an index on the
foreign keys in order to optimize join and queries. However, if you
fail to create such a foreign key index and if the parent table is
subject to updates, you may see heavy lock contention. If ever in
doubt, it's often safer to create indexes on ALL foreign keys, despite
the possible overhead of maintaining unneeded indexes.
Having Unindexed foreign keys can be a performance issue. There
are two issues associated with unindexed foreign keys. The first is the
fact that a table lock will result if you update the parent records
primary key (very very unusual) or if you delete the parent record and
the child's foreign key is not indexed.
The second issue has to do with performance in general of a parent
child relationship. Consider that if you have an on delete cascade and
have not indexed the child table (eg: EMP is child of DEPT. Delete
deptno = 10 should cascade to EMP. If deptno in emp is not indexed --
full table scan). This full scan is probably undesirable and if you
delete many rows from the parent table, the child table will be scanned
once for each parent row deleted.
Also consider that for most (not all, most) parent child
relationships, we query the objects from the 'master' table to the
'detail' table. The glaring exception to this is a code table (short
code to long description). For master/detail relationships, if you do
not index the foreign key, a full scan of the child table will result.
So, how do you easily discover if you have unindexed foreign keys
in your schema? This script can help. When you run it, it will generate
a report such as:
SQL> @unindex
STAT
TABLE_NAME
COLUMNS
COLUMNS
----
------------------------------ -------------------- --------------------
****
APPLICATION_INSTANCES
AI_APP_CODE
ok
EMP
DEPTNO
DEPTNO
The **** in the first row shows me that I have an unindexed foreign key
in the table APPLICATION_INSTANCES. The ok in the second row shows me I
have a table EMP with an indexed foreign key.
The
script
column columns format a20
word_wrapped
column table_name f