数据库

位置:IT落伍者 >> 数据库 >> 浏览文章

Oracle索引问题诊断与优化


发布日期:2018年08月28日
 
Oracle索引问题诊断与优化

实验

create table s as select * from SHSALES;

create table s as select * from SHSALES;

s表没有建立索引

s表有建立索引

set timing on;

select * from s where prod_id=;

s

select * from s where prod_id=;

s

可见索引对于表查询速度的重要性

索引性能测试与诊断

查看数据库Index信息

SELECT AOWNER ATABLE_OWNER ATABLE_NAME AINDEX_NAME AINDEX_TYPE

BCOLUMN_POSITION BCOLUMN_NAME CTABLESPACE_NAME

ATABLESPACE_NAME AUNIQUENESS

FROM DBA_INDEXES A DBA_IND_COLUMNS B DBA_TABLES C

WHERE AOWNER = UPPER (hr)

AND AOWNER = BINDEX_OWNER

AND AOWNER = COWNER

AND ATABLE_NAME LIKE UPPER (DEPARTMENTS)

AND ATABLE_NAME = BTABLE_NAME

AND ATABLE_NAME = CTABLE_NAME

AND AINDEX_NAME = BINDEX_NAME

ORDER BY AOWNER ATABLE_OWNER ATABLE_NAME AINDEX_NAME BCOLUMN_POSITION

查出没有建立index的表

SELECT OWNER TABLE_NAME

FROM ALL_TABLES

WHERE OWNER NOT IN (SYSSYSTEMOUTLNDBSNMP) AND OWNER = UPPER (scott)

MINUS

SELECT OWNER TABLE_NAME

FROM ALL_INDEXES

WHERE OWNER NOT IN (SYSSYSTEMOUTLNDBSNMP)

查出建立了过量index的表

SELECT OWNER TABLE_NAME COUNT (*) count

FROM ALL_INDEXES

WHERE OWNER NOT IN (SYSSYSTEMOUTLNDBSNMP) AND OWNER = UPPER (hr)

GROUP BY OWNER TABLE_NAME

HAVING COUNT (*) > ()

一个表可以有几百个索引但是对于频繁插入和更新表索引越多系统CPUI/O负担就越重建议每张表不超过个索引

实验

create table table as select * from SHSALES;

create table table as select * from SHSALES;

table只在prod_id列建索引

table在所有列建索引

SELECT count(*) FROM table where prod_id=;

set timing on;

update table set cust_id= where prod_id=;

s

update table set cust_id= where prod_id=;

s

找出全表扫描(Full Scan)的Sid和SQL

A full table scan occurs when every block is read from a table Full table scans are often a preferred performance option in batchstyle applications such as decision support We have seen some excellent runtime improvements in decision support systems that use the parallel query option which relies on full table scans to operate However full table scans at an OLTP site during prime online usage times can create havoc with response times Full table scans even on small tables can degrade response times particularly when the small table drives the query and this table is not always the most efficient access path

The following query reports how many full table scans are taking place:

SELECT name value

FROM v$sysstat

WHERE name LIKE %table %

ORDER BY name;

The values relating to the full table scans are:

table scans (long tables) a scan of a table that has more than five database blocks

table scans (short tables) a count of full table scans with five or fewer blocks

If the number of long table scans is significant there is a strong possibility that SQL statements in your application need tuning or indexes need to be added

To get an appreciation of how many rows and blocks are being accessed on average for the long full table scans use this calculation (the sample data comes from an OLTP application):

Average Long Table Scan Blocks

= (table scan blocks gotten (short table scans * ))

/ long table scans

= ( ( * )) /

= ( ()) /

= blocks read per full table scan

In our example average disk reads performed on an OLTP application times in the space of a few short hours is not a healthy situation

If you can identify the users who are experiencing the full table scans you can find out what they were running to cause these scans Below is a script that allows you to do this:

REM FILE NAME: fullscansql

REM LOCATION: Database Tuning\File I/O Reports

REM FUNCTION: Identifies users of full table scans

REM TESTED ON:

REM PLATFORM: nonspecific

REM REQUIRES: v$session v$sesstat v$statname

REM This view is used by the fscanavgsql script

REM

REM This is a part of the Knowledge Xpert for Oracle Administration REM library

REM Copyright (C) Quest Software

REM All rights reserved

REM

REM************ Knowledge Xpert for Oracle Administration *************

DROP VIEW full_table_scans;

CREATE VIEW full_table_scans

AS

SELECT ssusername

|| (

|| sesid

|| ) User Process

SUM (DECODE (NAME table scans (short tables) VALUE)) Short Scans

SUM (DECODE (NAME table scans (long tables) VALUE)) Long Scans

SUM (DECODE (NAME table scan rows gotten VALUE)) Rows Retrieved

FROM v$session ss v$sesstat se v$statname sn

WHERE sestatistic# = snstatistic#

AND ( NAME LIKE %table scans (short tables)%

OR NAME LIKE %table scans (long tables)%

OR NAME LIKE %table scan rows gotten%

)

AND sesid = sssid

AND ssusername IS NOT NULL

GROUP BY ssusername

|| (

|| sesid

|| ) ;

COLUMN User Process FORMAT a;

COLUMN Long Scans FORMAT ;

COLUMN Short Scans FORMAT ;

COLUMN Rows Retreived FORMAT ;

COLUMN Average Long Scan Length FORMAT ;

TTITLE Table Access Activity By User

SELECT User Process Long Scans Short Scans Rows Retrieved

FROM full_table_scans

ORDER BY Long Scans DESC;

找出可能有全表扫描的sql语句

select sidsql_text

From v$session sv$sql q

Where sid in()

And (qsql_id=ssql_id or qsql_id=sprev_sql_id);

可借助Knowledge Xpert for Oracle Administration的Active Analysis协助分析Index

Indexes exist primarily to improve the performance of SQL statements In many cases establishing good indexes is the best path to optimal performance

Active Analysis Show indexes by owner & table

Active Analysis Show index statistics

Active Analysis Show tables without indexes

Active Analysis Show tables with excessive indexes

Active Analysis Show similar indexes

Active Analysis Show foreign keys missing indexes

Active Analysis Show partitioned indexes

Knowing When to Rebuild Indexes

Index调优建议

索引设计优化

The way columns are indexed effect their efficiency The order columns are specified should reflect the way a select will retrieve them The first column should be the one that will be accessed most often

Oracle recommends that you do not explicitly define UNIQUE indexes on tables When the unique constraint is created a unique index is created to maintain it Uniqueness is strictly a logical concept and should be associated with the definition of a table As such uniqueness should be defined by using UNIQUE integrity constraints on the desired columns

Oracle recommends that you do not explicitly define UNIQUE indexes on tables (CREATE UNIQUE INDEX) In general it is better to create constraints to enforce uniqueness than it is to use the CREATE UNIQUE INDEX syntax A constraints associated index always assumes the name of the constraint; you cannot specify a specific name for a constraint index

经常一起使用多个字段检索记录组合索引比单索引更有效

把最常用的列放在最前面dx_groupid_serv_id(groupidserv_id)在where条件中使用groupid或groupidserv_id查询将使用索引若仅用到serv_id字段则索引无效

试验(组合索引比单索引更有效)

create table as select * from SHSALES;

在table上建立一个prod_id和cust_id的组合索引

create table as select * from SHSALES;

在table上建立一个索引(prod_id)

set autotrace on;

SQL> select count(*) from table where prod_id = or cust_id = ;

COUNT(*)

Elapsed: ::

Execution Plan

SELECT STATEMENT Optimizer=ALL_ROWS (Cost= Card= Bytes=

)

SORT (AGGREGATE)

INDEX (FAST FULL SCAN) OF IDX_PROD_ID_CUST_ID (INDEX)

(Cost= Card= Bytes=)

Statistics

recursive calls

db block gets

consistent gets

physical reads

redo size

bytes sent via SQL*Net to client

bytes received via SQL*Net from client

SQL*Net roundtrips to/from client

sorts (memory)

sorts (disk)

rows processed

SQL> select count(*) from table where prod_id = or cust_id = ;

COUNT(*)

Elapsed: ::

Execution Plan

SELECT STATEMENT Optimizer=ALL_ROWS (Cost= Card= Bytes=

)

SORT (AGGREGATE)

TABLE ACCESS (FULL) OF TABLE (TABLE) (Cost= Card=

Bytes=)

Statistics

recursive calls

db block gets

consistent gets

physical reads

redo size

bytes sent via SQL*Net to client

bytes received via SQL*Net from client

SQL*Net roundtrips to/from client

sorts (memory)

sorts (disk)

rows processed

试验(组合索引在Where子句中的使用)

set autotrace on;

仅仅使用prod_id

SQL> select count(*) from table where prod_id=;

COUNT(*)

Elapsed: ::

Execution Plan

SELECT STATEMENT Optimizer=ALL_ROWS (Cost= Card= Bytes=

)

SORT (AGGREGATE)

INDEX (RANGE SCAN) OF IDX_PROD_ID_CUST_ID (INDEX) (Cos

t= Card= Bytes=)

Statistics

recursive calls

db block gets

consistent gets

physical reads

redo size

bytes sent via SQL*Net to client

bytes received via SQL*Net from client

SQL*Net roundtrips to/from client

sorts (memory)

sorts (disk)

rows processed

prod_id和cust_id一起使用

SQL> select count(*) from table where prod_id = and cust_id = ;

COUNT(*)

Elapsed: ::

Execution Plan

SELECT STATEMENT Optimizer=ALL_ROWS (Cost= Card= Bytes=)

SORT (AGGREGATE)

INDEX (RANGE SCAN) OF IDX_PROD_ID_CUST_ID (INDEX) (Cos

t= Card= Bytes=)

Statistics

recursive calls

db block gets

consistent gets

physical reads

redo size

bytes sent via SQL*Net to client

bytes received via SQL*Net from client

SQL*Net roundtrips to/from client

sorts (memory)

sorts (disk)

rows processed

仅仅使用cust_id

SQL> select count(*) from table where cust_id = ;

COUNT(*)

Elapsed: ::

Execution Plan

SELECT STATEMENT Optimizer=ALL_ROWS (Cost= Card= Bytes=

)

SORT (AGGREGATE)

INDEX (FAST FULL SCAN) OF IDX_PROD_ID_CUST_ID (INDEX)

(Cost= Card= Bytes=)

Statistics

recursive calls

db block gets

consistent gets

physical reads

redo size

bytes sent via SQL*Net to client

bytes received via SQL*Net from client

SQL*Net roundtrips to/from client

sorts (memory)

sorts (disk)

rows processed

Oracle索引扫描的四种类型

索引使用优化

避免意外的表扫描

Avoid accidental table scans

One of the most fundamental SQL tuning problems is the accidental table scan Accidental table scans usually occur when the SQL programmer tries to perform a search on an indexed column that cant be supported by an index This can occur when:

Using != (not equals to) Even if the not equals condition satisfies only a small number of rows Oracle does not use an index to satisfy such a condition Often you can recode these queries using > or IN conditions which can be supported by index lookups

Searching for NULLS Oracle wont use an index to find null values since null values are not usually stored in an index (the exception is a concatenated index entry where only some of the values are NULL) If youre planning to search for values that are logically missing consider changing the column to NOT NULL with a DEFAULT clause For example you could set a default value of UNKNOWN and use the index to find these values Interestingly recent versions of Oracle can index to find values that are NOT NULL if the costbased optimizer determines that such an approach is costeffective

Using functions on indexed columns Any function or operation on an indexed column prevents Oracle from using an index on that column For instance Oracle cant use an index to find SUBSTR(SURNAME)=SMIT Instead of manipulating the column try to manipulate the search condition In the previous example a better formulation would be SURNAME LIKE SMIT%

实验(慎用!=)

!=使用的是Fast Full Scan

SQL> select count(*) from table where prod_id != ;

COUNT(*)

Elapsed: ::

Execution Plan

SELECT STATEMENT Optimizer=ALL_ROWS (Cost= Card= Bytes=

)

SORT (AGGREGATE)

INDEX (FAST FULL SCAN) OF IDX_TABLE (INDEX) (Cost=

Card= Bytes=)

而<>IN用的是Range Scan

SQL> select count(*) from table where prod_id < ;

COUNT(*)

Elapsed: ::

Execution Plan

SELECT STATEMENT Optimizer=ALL_ROWS (Cost= Card= Bytes=

)

SORT (AGGREGATE)

INDEX (RANGE SCAN) OF IDX_TABLE (INDEX) (Cost= Ca

rd= Bytes=)

实验(慎用函数)

create table emp as select * from scottemp;

在emp的empno和ename字段上分别建立index

SQL> select Count(*) from emp where substr(ename) = smit;

COUNT(*)

Elapsed: ::

Execution Plan

SELECT STATEMENT Optimizer=ALL_ROWS (Cost= Card= Bytes=)

SORT (AGGREGATE)

TABLE ACCESS (FULL) OF EMP (TABLE) (Cost= Card= Byte

s=)

SQL> select Count(*) from emp where ename like smit%;

COUNT(*)

Elapsed: ::

Execution Plan

SELECT STATEMENT Optimizer=ALL_ROWS (Cost= Card= Bytes=)

SORT (AGGREGATE)

INDEX (RANGE SCAN) OF IDX_EMP (INDEX)

上一篇:Oracle数据库的在空间管理三个技巧介绍

下一篇:ORACLE Virtual Private Database 全新体验