本文只討論Oracle中最常見的索引,即是B-tree索引。本文中涉及的資料庫版本是Oracle8i。
一. 查看系統資料表中的使用者索引
在Oracle中,SYSTEM表是安裝資料庫時自動建立的,它包含資料庫的全部資料字典,預存程序、包、函數和觸發器的定義以及系統復原段。
一般來說,應該盡量避免在SYSTEM表中儲存非SYSTEM使用者的對象。因為這樣會帶來資料庫維護和管理的很多問題。一旦SYSTEM表損壞了,只能重建資料庫。我們可以用下面的語句來檢查在SYSTEM表內有沒有其他使用者的索引存在。
select count(*) from dba_indexes where tablespace_name = 'SYSTEM' and owner not in ('SYS','SYSTEM') / |
二. 索引的儲存情況檢查
Oracle為資料庫中的所有資料分配邏輯結構空間。資料庫空間的單位是資料區塊(block)、範圍(extent)和段(segment)。
Oracle資料區塊(block)是Oracle使用和分配的最小儲存單位。它是由資料庫建立時設定的DB_BLOCK_SIZE決定的。一旦資料庫產生了,資料區塊的大小不能改變。要想改變只能重建立立資料庫。(在Oracle9i中有一些不同,不過這不在本文討論的範圍內。)
Extent是由一組連續的block組成的。一個或多個extent組成一個segment。當一個segment中的所有空間被用完時,Oracle為它分配一個新的extent。
Segment是由一個或多個extent組成的。它包含某資料表空間中特定邏輯儲存結構的所有資料。一個段中的extent可以是不連續的,甚至可以在不同的資料檔案中。
一個object只能對應於一個邏輯儲存的segment,我們通過查看該segment中的extent,可以看出相應object的儲存情況。
(1)查看索引段中extent的數量:
select segment_name, count(*) from dba_extents where segment_type='INDEX' and owner=UPPER('&owner') group by segment_name / |
(2)查看錶空間內的索引的擴充情況:
select substr(segment_name,1,20) "SEGMENT NAME", bytes, count(bytes) from dba_extents where segment_name in ( select index_name from dba_indexes where tablespace_name=UPPER('&資料表空間')) group by segment_name,bytes order by segment_name / |
三. 索引的選擇性
索引的選擇性是指索引列中不同值的數目與表中記錄數的比。如果一個表中有2000條記錄,表索引列有1980個不同的值,那麼這個索引的選擇性就是1980/2000=0.99。
一個索引的選擇性越接近於1,這個索引的效率就越高。
如果是使用基於cost的最佳化,最佳化器不應該使用選擇性不好的索引。如果是使用基於rule的最佳化,最佳化器在確定執行路徑時不會考慮索引的選擇性(除非是唯一性索引),並且不得不手工最佳化查詢以避免使用非選擇性的索引。
確定索引的選擇性,可以有兩種方法:手工測量和自動測量。
(1)手工測量索引的選擇性
如果要根據一個表的兩列建立兩列共置索引,可以用以下方法測量索引的選擇性:
列的選擇性=不同值的數目/行的總數 /* 越接近1越好 */
select count(distinct 第一列||'%'||第二列)/count(*) from 表名 / |
如果我們知道其中一列索引的選擇性(例如其中一列是主鍵),那麼我們就可以知道另一列索引的選擇性。
手工方法的優點是在建立索引前就能評估索引的選擇性。
(2)自動測量索引的選擇性
如果分析一個表,也會自動分析所有表的索引。
第一,為了確定一個表的確定性,就要分析表。
analyze table 表名 compute statistics / |
第二,確定索引裡不同關鍵字的數目:
select distinct_keys from user_indexes where table_name='表名' and index_name='索引名' / |
第三,確定表中行的總數:
select num_rows from user_tables where table_name='表名' / |
第四,索引的選擇性=索引裡不同關鍵字的數目/表中行的總數:
select i.distinct_keys/t.num_rows from user_indexes i, user_tables t where i.table_name='表名' and i.index_name='索引名' and i.table_name=t.table_name / |
第五,可以查詢USER_TAB_COLUMNS以瞭解每個列的選擇性。
表中所有行在該列的不同值的數目:
select column_name, num_distinct from user_tab_columns where table_name='表名' / |
列的選擇性=NUM_DISTINCT/表中所有行的總數,查詢USER_TAB_COLUMNS有助測量每個列的選擇性,但它並不能精確地測量列的共置組合的選擇性。要想測量一組列的選擇性,需要採用手工方法或者根據這組列建立一個索引並重新分析表。
四. 確定索引的實際片段
隨著資料庫的使用,不可避免地對基本表進行插入,更新和刪除,這樣導致葉子行在索引中被刪除,使該索引產生片段。插入刪除越頻繁的表,索引片段的程度也越高。片段的產生使訪問和使用該索引的I/O成本增加。片段較高的索引必須重建以保持最佳效能。
(1)利用驗證索引命令對索引進行驗證。
這將有價值的索引資訊填入index_stats表。
validate index 使用者名稱.索引名 / |
(2)查詢index_stats表以確定索引中刪除的、未填滿的葉子行的百分比。
select name, del_lf_rows, lf_rows, round((del_lf_rows/(lf_rows+0.0000000001))*100) "Frag Percent" from index_stats / |
(3)如果索引的葉子行的片段超過10%,考慮對索引進行重建。
alter index 使用者名稱.索引名 rebuild tablespace 資料表空間名 storage(initial 初始值 next 擴充值) nologging / |
(4)如果出於空間或其他考慮,不能重建索引,可以整理索引。
alter index使用者名稱.索引名 coalesce / |
(5)清除分析資訊
analyze index 使用者名稱.索引名 delete statistics / |
三. 索引的選擇性
索引的選擇性是指索引列中不同值的數目與表中記錄數的比。如果一個表中有2000條記錄,表索引列有1980個不同的值,那麼這個索引的選擇性就是1980/2000=0.99。
一個索引的選擇性越接近於1,這個索引的效率就越高。
如果是使用基於cost的最佳化,最佳化器不應該使用選擇性不好的索引。如果是使用基於rule的最佳化,最佳化器在確定執行路徑時不會考慮索引的選擇性(除非是唯一性索引),並且不得不手工最佳化查詢以避免使用非選擇性的索引。
確定索引的選擇性,可以有兩種方法:手工測量和自動測量。
(1)手工測量索引的選擇性
如果要根據一個表的兩列建立兩列共置索引,可以用以下方法測量索引的選擇性:
列的選擇性=不同值的數目/行的總數 /* 越接近1越好 */
select count(distinct 第一列||'%'||第二列)/count(*) from 表名 / |
如果我們知道其中一列索引的選擇性(例如其中一列是主鍵),那麼我們就可以知道另一列索引的選擇性。
手工方法的優點是在建立索引前就能評估索引的選擇性。
(2)自動測量索引的選擇性
如果分析一個表,也會自動分析所有表的索引。
第一,為了確定一個表的確定性,就要分析表。
analyze table 表名 compute statistics / |
第二,確定索引裡不同關鍵字的數目:
select distinct_keys from user_indexes where table_name='表名' and index_name='索引名' / |
第三,確定表中行的總數:
select num_rows from user_tables where table_name='表名' / |
第四,索引的選擇性=索引裡不同關鍵字的數目/表中行的總數:
select i.distinct_keys/t.num_rows from user_indexes i, user_tables t where i.table_name='表名' and i.index_name='索引名' and i.table_name=t.table_name / |
第五,可以查詢USER_TAB_COLUMNS以瞭解每個列的選擇性。
表中所有行在該列的不同值的數目:
select column_name, num_distinct from user_tab_columns where table_name='表名' / |
列的選擇性=NUM_DISTINCT/表中所有行的總數,查詢USER_TAB_COLUMNS有助測量每個列的選擇性,但它並不能精確地測量列的共置組合的選擇性。要想測量一組列的選擇性,需要採用手工方法或者根據這組列建立一個索引並重新分析表。
四. 確定索引的實際片段
隨著資料庫的使用,不可避免地對基本表進行插入,更新和刪除,這樣導致葉子行在索引中被刪除,使該索引產生片段。插入刪除越頻繁的表,索引片段的程度也越高。片段的產生使訪問和使用該索引的I/O成本增加。片段較高的索引必須重建以保持最佳效能。
(1)利用驗證索引命令對索引進行驗證。
這將有價值的索引資訊填入index_stats表。
validate index 使用者名稱.索引名 / |
(2)查詢index_stats表以確定索引中刪除的、未填滿的葉子行的百分比。
select name, del_lf_rows, lf_rows, round((del_lf_rows/(lf_rows+0.0000000001))*100) "Frag Percent" from index_stats / |
(3)如果索引的葉子行的片段超過10%,考慮對索引進行重建。
alter index 使用者名稱.索引名 rebuild tablespace 資料表空間名 storage(initial 初始值 next 擴充值) nologging / |
(4)如果出於空間或其他考慮,不能重建索引,可以整理索引。
alter index使用者名稱.索引名 coalesce / |
(5)清除分析資訊
analyze index 使用者名稱.索引名 delete statistics / |
五. 重建索引
(1)檢查需要重建的索引。
根據以下幾方面進行檢查,確定需要重建的索引。
第一,查看SYSTEM資料表空間中的使用者索引。
為了避免資料字典的片段出現,要盡量避免在SYSTEM資料表空間出現使用者的表和索引。
select index_name
from dba_indexes
where tablespace_name='SYSTEM'
and owner not in ('SYS','SYSTEM')
/
第二,確保使用者的表和索引不在同一資料表空間內。
表和索引對象的第一個規則是把表和索引分離。把表和相應的索引建立在不同的資料表空間中,最好在不同的磁碟上。這樣可以避免在資料管理和查詢時出現的許多I/O衝突。
set linesize 120
col "OWNER" format a20
col "INDEX" format a30
col "TABLE" format a30
col "TABLESPACE" format a30
select
i.owner "OWNER",
i.index_name "INDEX",
t.table_name "TABLE",
i.tablespace_name "TABLESPACE"
from
dba_indexes i,
dba_tables t
where i.owner=t.owner
and i.table_name=t.table_name
and i.tablespace_name=t.tablespace_name
and i.owner not in ('SYS','SYSTEM')
/
第三,查看資料資料表空間裡有哪些索引
使用者的預設資料表空間應該不是SYSTEM資料表空間,而是資料資料表空間。在建立索引時,如果不指定相應的索引資料表空間名,那麼,該索引就會建立在資料資料表空間中。這是程式員經常忽略的一個問題。應該在建索引時,明確的指明相應的索引資料表空間。
col segment_name format a30
select
owner,
segment_name,
sum(bytes)
from dba_segments
where tablespace_name='資料資料表空間名'
and segment_type='INDEX'
group by owner,segment_name
/
第四,查看哪個索引被擴充了超過10次
隨著表記錄的增加,相應的索引也要增加。如果一個索引的next extent值設定不合理(太小),索引段的擴充變得很頻繁。索引的extent太多,檢索時的速度和效率就會降低。
set linesize 100
col owner format a10
col segment_name format a30
col tablespace_name format a30
select
count(*),
owner,
segment_name,
tablespace_name
from dba_extents
where segment_type='INDEX'
and owner not in ('SYS','SYSTEM')
group by owner,segment_name,tablespace_name
having count(*) >10
order by count(*) desc
/
(2)找出需要重建的索引後,需要確定索引的大小,以設定合理的索引儲存參數。
set linesize 120
col "INDEX" format a30
col "TABLESPACE" format a20
select
owner "OWNER",
segment_name "INDEX",
tablespace_name "TABLESPACE",
bytes "BYTES/COUNT",
sum(bytes) "TOTAL BYTES",
round(sum(bytes)/(1024*1024),0) "TOTAL M",
count(bytes) "TOTAL COUNT"
from dba_extents
where segment_type='INDEX'
and segment_name in
(
'索引名1',
'索引名2',
......
)
group by owner,segment_name,segment_type,tablespace_name,bytes
order by owner,segment_name
/
(3)確定索引資料表空間還有足夠的剩餘空間。
確定要把索引重建到哪個索引資料表空間中。要保證相應的索引資料表空間有足夠的剩餘空間。
select round(bytes/(1024*1024),2) free(M)
from sm$ts_free
where tablespace_name='資料表空間名'
/
(4)重建索引。
重建索引時要注意以下幾點:
a.如果不指定tablespace名,索引將建在使用者的預設資料表空間。
b.如果不指定nologging,將會寫日誌,導致速度變慢。由於索引的重建沒有恢複的必要,所以,可以不寫日誌。
c.如果出現資源忙,表明有進程正在使用該索引,等待一會再提交。
alter index 索引名
rebuild
tablespace 索引資料表空間名
storage(initial 初始值 next 擴充值)
nologging
/
(5)檢查索引。
對重建好的索引進行檢查。
select *
from dba_extents
where segment_name='索引名'
/
(6)根據索引進行查詢,檢查索引是否有效
使用相應的where條件進行查詢,確保使用該索引。看看使用索引後的效果如何。
select *
from dba_ind_columns
where index_name like '表名%'
/
然後,根據相應的索引項目進行查詢。
select *
from '表名%'
where ......
/
(6)找出有片段的資料表空間,並收集其片段。
重建索引後,原有的索引被刪除,這樣會造成資料表空間的片段。
select 'alter tablespace '||tablespace_name||' coalesce;'
from dba_free_space_coalesced
where percent_blocks_coalesced!=100
/
整理資料表空間的片段。
alter tablespace 資料表空間名 coalesce
/