| My OracleGuide |
Performance Optimization - Concepts |
About | Products |
|
Designing Indexes - Concepts Index Access Statistics ProActive and Reactive Indexing Using v$sql view to get Get Expensive queries Monitoring Index Usage Segment Level Statistics Multiple Block Sizes |
Designing Indexes - Concepts
|
|
Index Access Statistics
--- To select TOP 10 Indexes by usage select * FROM( select b.object_owner, b.object_name, sum(a.executions) FROM v$sql a, v$sql_plan b where a.address=b.address and a.hash_value=b.hash_value and b.operation = 'INDEX' GROUP BY b.object_owner, b.object_name ORDER BY sum(a.executions) DESC) WHERE rownum<11; --- To select TOP 10 Indexes with UNIQUE SCANS by usage select * FROM( select b.object_owner, b.object_name FROM v$sql a, v$sql_plan b where a.address=b.address and a.hash_value=b.hash_value and b.operation = 'INDEX' and b.options = 'UNIQUE SCAN' GROUP BY b.object_owner, b.object_name ORDER BY sum(a.executions) DESC) WHERE rownum<11; --- To select TOP 10 Indexes with RANGE SCANS select * FROM( select b.object_owner, b.object_name, sum(a.executions) FROM v$sql a, v$sql_plan b where a.address=b.address and a.hash_value=b.hash_value and b.operation = 'INDEX' and b.options = 'RANGE SCAN' GROUP BY b.object_owner, b.object_name ORDER BY sum(a.executions) DESC) WHERE rownum<11; --- To select TOP 10 Indexes with FULL SCANS select * FROM( select b.object_owner, b.object_name, sum(a.executions) FROM v$sql a, v$sql_plan b where a.address=b.address and a.hash_value=b.hash_value and b.operation = 'INDEX' and b.options = 'FULL SCAN' GROUP BY b.object_owner, b.object_name ORDER BY sum(a.executions) DESC) WHERE rownum<11; --- To select TOP 10 TABLES with ACCESS BY ROWID select * FROM( select b.object_owner, b.object_name, sum(a.executions) FROM v$sql a, v$sql_plan b where a.address=b.address and a.hash_value=b.hash_value and b.operation = 'TABLE ACCESS' and b.options = 'BY ROWID' GROUP BY b.object_owner, b.object_name ORDER BY sum(a.executions) DESC) WHERE rownum<11; --- To select TOP 10 TABLES with FULL SCAN select * FROM( select b.object_owner, b.object_name, sum(a.executions) FROM v$sql a, v$sql_plan b where a.address=b.address and a.hash_value=b.hash_value and b.operation = 'TABLE ACCESS' and b.options = 'FULL' GROUP BY b.object_owner, b.object_name ORDER BY sum(a.executions) DESC) WHERE rownum<11; |
ProActive Approach
It is possible to anticipate the necessary indexes While designing the database tables and relations. Based the nature of data and cardinality an expert data architect can design the correct indexes.
The types of indexes required for an Online Transaction systems ( OLTP Systems ) will be different from those required for Reporting Applications or Decision support Systems ( DSS Systems).
The number of records fetched or number of blocks required to be scanned in an OLTP System is very few. Normally OLTP applications are based on the user input to either query or update the record for a single key value. So fetching the table record based on the index scan will have least block reads and CPU time.
The primary key will allow the application developers to use a unique number to represent data record. The concept of primary key is required to implement referential integrity constraints or foreign keys.
The reporting queries of DSS Systems has to read the entire table in order to do sorting or aggregate function or perform join operations. If more than 25% of data in a table is to be selected then a full table scan can be chosen instead of an index scan.
Based on the percent of table data scanned either index scan or full table sacn can be used. The performance optimizer can choose the best plan if the table statistics are updated and the optimizer mode is set to either cost based or choose. If the queries contain either sorting or aggregate functions, then all the required data needs to be read either using a full table scan or an index range scan.
For tables that contain large text columns or if the average row length is high, then scanning the table invloves fetching large number of blocks and this may involve more disk reads. If all columns required for the query is present in an index then the table fetch can be avoided. This may reduce the amount of physical reads considerably.
The following information about the database will be useful in performance optimization
Information from Static Data Dictionary views
Information from Dynamic Performace views
-- TOP 10 UNIQUE SCAN indexes ordered by physical reads select * from( select OPERATION,OPTIONS,OBJECT_OWNER,OBJECT_NAME, sum(EXECUTIONS) EXECUTIONS, sum(OUTPUT_ROWS) OUTPUT_ROWS, sum(CR_BUFFER_GETS+CU_BUFFER_GETS) BUFFER_GETS, sum(DISK_READS) DISK_READS, sum(DISK_WRITES) DISK_WRITES, sum(ELAPSED_TIME) ELAPSED_TIME from v$sql_plan_statistics_all where operation = 'INDEX' and options = 'UNIQUE SCAN' GROUP BY OPERATION,OPTIONS,OBJECT_OWNER,OBJECT_NAME ORDER BY DISK_READS DESC) WHERE rownum<11;
|
Information regarding index usage and performance statistics can be obtained from dynamic performance views. By looking at the queries it will be possible to find the queries that are doing full table scan and the ones that can benefit from new indexes.
The following data dictionary views are useful for the data analysis. The shared pool keeps the most recent SQL statements, plan and statistics in the cache.
To list SQL statements in Shared pool
Queries having full table scan of the employee table in scott schema
Get Expensive queries
Find the expensive plan hash value
For the above plan_hash_value, say p1, get addres and hash_value of one SQL
Using the above address and hash_value query the v$sql_plan to get the execution plan for the above query Monitoring Index UsageThere are cases where the existing indexes are not used. This can increase the database writes for tables with heavy transactions. You can use the view v$object_usage to monitor index usage. The view displays statistics about index usage gathered from the database. Monitor for sufficient number of days. All indexes that have been used at least once can be displayed in this view.
Set index monitoring
To turn off monitoring
To generate monitoring scripts for all indexes
To turn off monitoring for all indexes
Find unused indexes
Choosing the index typeA bitmap index is more suitable where there are very few distinct values.The following query can be used to get a list of indexes where the leading column has very few distinct values. Before running this query the tables or schema has to be analyzed with "COMPUTE STATISTICS" and the option 'for all columns'
select * from(
select a.index_name,
Partitiond global indexesAs the number rows in the table increases the height of the index tree and also the index maintenance cost increases. Partitioning helps to reduce the size of the index tree. Even for a table that is partitioned, partitiond global indexes can be created for the indexes that are not based on the table partition key.Consider the sales table with millions of rows. A partitioned index has to be created based on customer_id. It is decided to have one million rows per index partition. The following query can be used to select range boundaries for index partition
select * from(
select customer_id, rownum r1 from
Create the partitioned index
|
|
Detecting which tables or indexes are being accessed the most
will allow you to fine tune memory structures and access methodologies. Without being able to determine how much and how often particular
data objects are being accessed, you will be unable to tune the database
engine. The ability to pinpoint objects that are degrading system performance and
zeroing in on what areas the performance hit is impacting is vital to your
ability to tune Oracle. If you knew nothing about the applications running, you may want to know the details of the transaction mix in the database. Oracle has come out with an advisory that will tell us the performance on individual objects within the database. So we can determine where the hot spots for particular objects reside and take appropriate action. The performance statistics associated with segments are available in the view V$SEGMENT_STATISTICS |
How to Ensure Segment Level Statistics are Being CollectedUsing the new Oracle initialization parameter called STATISTICS_LEVEL, you can start collecting segment level statistics. This parameter has three settings BASIC, TYPICAL, and ALL and you must set STATISTICS_LEVEL to TYPICAL or ALL if you want to take advantage of segment level statistics. BASIC - which does nothing, basically turned off. TYPICAL/ALL - will cause Segment Level Statistics to be collected. Check Your Current SettingIssue the following command to determine what your setting if for STATISTICS_LEVEL. SQL> SHOW PARAMETER statistics_level NAME TYPE VALUE ------------------------------------ ----------- ----------- statistics_level string TYPICAL |
To change your settingYou may change the current setting of STATISTICS_LEVEL by issuing the following ALTER statement. SQL> ALTER SYSTEM SET statistics_level=TYPICAL SCOPE=SPFILE; |
Use of Segment Level StatisticsThe main purpose of segment level statistics is to further investigate system level performance problems. It is thus recommended that you first take a look at what performance problems from the V$SYSTEM_EVENT view. If the problem is in Disk IO or buffer activity, then it is better to look at the segment level statistics to obtain the statistics related to objects. The waits associated with events "db file scattered read", "db file sequential read" and "buffer busy waits" has to be considered. System Wait Events
SQL> select event,total_waits,total_timeouts,time_waited
from v$system_event
order by time_waited desc;
Determination of which objects are producing read/write performance problems SQL> select object_name, reads,writes, physical_reads, buffer_busy, block_changes, round(100*(1-physical_reads/reads)) hits FROM( select object_name, max(case when STATISTIC_NAME like '%reads%' THEN value else null end) reads, max(case when STATISTIC_NAME like '%writes%' THEN value else null end) writes, max(case when STATISTIC_NAME='physical reads' THEN value else null end) physical_reads, max(case when STATISTIC_NAME='buffer busy waits' THEN value else null end) buffer_busy, max(case when STATISTIC_NAME='db block changes' THEN value else null end) block_changes FROM v$segment_statistics GROUP BY object_name) where reads>0; |
What to do NowAfter determining which objects are causing performance problems choose a methodology to reduce the bottleneck. If you have access to the code and developers, see if there is any ill-formed logic in the code. The other option is to modify the memory structures for objects |
|
The management of disk I/O is a very critical component and anything that can be done to reduce the amount of disk I/O is going to have a positive impact on the throughput of the Oracle database system. Access for RAM is measured in nanoseconds, while access from disk is generally measured in milliseconds. So there will be an order of magnitude improvement in performance if the Oracle data block is brought to RAM buffer. There are three sources of delay for disk IO.
For an OLTP database, there are many transactions happening constantly where small amount of data is read each time. So if the data is stored in tablespace with smaller block size then the amount of RAM memory required for caching the data and the disk IO will be less. If your rows are SMALL and you use a large blocksize,when you fetch a block you may get lots of rows that you are not interested in. For those databases that fetch small rows randomly from the disk, the Oracle DBA can segregate these types of tables into 2K Tablespaces. In a data warehousing system the data is loaded on a periodic interval via a batch job. Most of the activity in an OLAP database comes from complex queries that aggregate large sets of data. Here there are no transactions dealing with small amount of data and hence larger block size will be beneficial. For those tables that contain raw, long raw, or in-line LOBs, moving the table rows to large block size will have an extremely beneficial effect on disk I/O since the entire row may fit in a single DB block. This reduces the overhead associated with chained rows. Larger oracle block sizes typically give fewer index levels and hence improved index access times to data . This is one of the major benefits of a larger block size. Tables that experience large-table full-table scans will benefit from the larger block size. The initialization parameter db_file_multiblock_read_count specifies the maximum number of blocks read in one I/O operation during a full table scan. Data warehouse environments tend to benefit most from maximizing the value of this parameter. The optimizer is more likely to choose a full table scan over an index if the value of this parameter is very high. The DB_CACHE_SIZE parameter is used to define the size of the buffer cache for the standard block size. An individual buffer cache must be defined for each non-standard block size used.
DB_2K_CACHE_SIZE = xM DB_4K_CACHE_SIZE = xM DB_8K_CACHE_SIZE = xM DB_16K_CACHE_SIZE = xM DB_32K_CACHE_SIZE = xMOnce the caches are defined the tablespaces with appropriate blocksizes can be created.
CREATE TABLESPACE <tablespace-name> BLOCKSIZE 2048; The KEEP and RECYCLE buffer caches can only be defined for the standard block size. In 9i these are defined using DB_KEEP_CACHE_SIZE and DB_RECYCLE_CACHE_SIZE, with their memory allocations being totally separate to the main buffer cache. A number of rules apply to the use of variable block sizes:
If the table contains several millions of rows and if there are lots of DML happening then index in a larger block size will be better. If the number of rows are less than a million then normal block size will be enough. How to choose the correct block size
The following queries can be used to detemine the correct block size for a table Some of the parameters are 1) Amount of reads via FULL TABLE SCANS 2) Amount of reads per transaction via TABLE ACCESS BY ROWID 3) Amount of writes per transaction 4) The average row length 5) Number of concurrent DML's The following SQL's can be used to obtain some read/write statistics The first query is used to get total amount of reads happening via FUll table Scans The second query calculates the amount of reads per transaction for acces by rowid's Calculate the cost of FULL Table Scans -------------------------------------- select OBJECT_OWNER||'.'||OBJECT_NAME, sum(CR_BUFFER_GETS+CU_BUFFER_GETS) from v$sql_plan_statistics_all where operation='TABLE ACCESS' and options='FULL' group by OBJECT_OWNER||'.'||OBJECT_NAME order by 2 / Calculate the cost of Index by rowid ------------------------------------ select OBJECT_OWNER||'.'||OBJECT_NAME, sum((CR_BUFFER_GETS+CU_BUFFER_GETS))/sum(EXECUTIONS), from v$sql_plan_statistics_all where EXECUTIONS>0 and operation='TABLE ACCESS' and options<>'FULL' group by OBJECT_OWNER||'.'||OBJECT_NAME order by 2 / Write statistics ---------------- select OBJECT_OWNER||'.'||OBJECT_NAME, sum(disk_writes)/sum(EXECUTIONS), from v$sql_plan_statistics_all where EXECUTIONS>0 and group by OBJECT_OWNER||'.'||OBJECT_NAME order by 2 /Also see these links Multiple Block Sizes
|