Oracle Index 的三個問題

索引 ( Index ) 是常見的數據庫對象,它的設置好壞、使用是否得當,極大地影響數據庫應用程序和 Database 的性能。 html

1. 索引特色: sql

第一,經過建立惟一性索引,能夠保證數據庫表中每一行數據的惟一性。 數據庫

第二,能夠大大加快數據的檢索速度,這也是建立索引的最主要的緣由。 性能

第三,能夠加速表和表之間的鏈接,特別是在實現數據的參考完整性方面特別有意義。 測試

第四,在使用分組和排序子句進行數據檢索時,一樣能夠顯著減小查詢中分組和排序的時間。 優化

第五,經過使用索引,能夠在查詢的過程當中,使用優化隱藏器,提升系統的性能。 htm

2. 索引不足: 對象

第一,建立索引和維護索引要耗費時間,這種時間隨着數據量的增長而增長。 排序

第二,索引須要佔物理空間,除了數據表佔數據空間以外,每個索引還要佔必定的物理空間,若是要創建聚簇索引,那麼須要的空間就會更大。 索引

第三,當對錶中的數據進行增長、刪除和修改的時候,索引也要動態的維護,這樣就下降了數據的維護速度。

3. 應該建索引列的特色:

1)在常常須要搜索的列上,能夠加快搜索的速度;

2)在做爲主鍵的列上,強制該列的惟一性和組織表中數據的排列結構;

3)在常常用在鏈接的列上,這些列主要是一些外鍵,能夠加快鏈接的速度;

4)在常常須要根據範圍進行搜索的列上建立索引,由於索引已經排序,其指定的範圍是連續的;

5)在常常須要排序的列上建立索引,由於索引已經排序,這樣查詢能夠利用索引的排序,加快排序查詢時間;

6)在常用在WHERE子句中的列上面建立索引,加快條件的判斷速度。

 

第一講、索引並不是老是最佳選擇

  若是發現 Oracle 在有索引的狀況下,沒有使用索引,這並非 Oracle 的優化器出錯。在有些狀況下, Oracle 確實會選擇全表掃描( Full Table Scan ) , 而非索引掃描( Index Scan )。這些狀況一般有:

1. 表未作 statistics, 或者 statistics 陳舊,致使 Oracle 判斷失誤。

2. 根據該表擁有的記錄數和數據塊數,實際上全表掃描要比索引掃描更快。

  對第 1 種狀況,最多見的例子,是如下這句 sql 語句:

select count(*) from mytable;

在未做 statistics 以前,它使用全表掃描,須要讀取 6000 多個數據塊(一個數據塊是 8k ) , 作了 statistics 以後,使用的是 INDEX (FAST FULL SCAN) ,只須要讀取 450 個數據塊。可是, statistics 作得很差,也會致使 Oracle 不使用索引。

  第 2 種狀況就要複雜得多。通常概念上都認爲索引比錶快,比較難以理解什麼狀況下全表掃描要比索引掃描快。爲了講清楚這個問題,這裏先介紹一下 Oracle 在評估使用索引的代價( cost )時兩個重要的數據: CF(Clustering factor) 和 FF(Filtering factor).

CF: 所謂 CF, 通俗地講,就是每讀入一個索引塊,要對應讀入多少個數據塊。

FF: 所謂 FF, 就是該 sql 語句所選擇的結果集,佔總的數據量的百分比。

  大約的計算公式是: FF * (CF + 索引塊個數 ) ,由此估計出,一個查詢, 若是使用某個索引,會須要讀入的數據塊塊數。須要讀入的數據塊越多,則 cost 越大, Oracle 也就越可能不選擇使用 index. (全表掃描須要讀入的數據塊數等於該表的實際數據塊數)

其核心就是, CF 可能會比實際的數據塊數量大。 CF 受到索引中數據的排列方式影響,一般在索引剛創建時,索引中的記錄與表中的記錄有良好的對應關係, CF 都很小;在表通過大量的插入、修改後,這種對應關係愈來愈亂, CF 也愈來愈大。此時須要 DBA 從新創建或者組織該索引。

若是某個 sql 語句之前一直使用某索引,較長時間後再也不使用,一種可能就是 CF 已經變得太大,須要從新整理該索引了。

FF 則是 Oracle 根據 statistics 所作的估計。好比 , mytables 表有 32 萬行,其主鍵 myid 的最小值是 1 ,最大值是 409654 ,考慮如下 sql 語句:

Select * from mytables where myid>=1; 和

Select * from mytables where myid>=400000

  這兩句看似差很少的 sql 語句,對 Oracle 而言,卻有巨大的差異。由於前者的 FF 是 100% , 然後者的 FF 可能只有 1% 。若是它的 CF 大於實際的數據塊數,則 Oracle 可能會選擇徹底不一樣的優化方式。而實際上,在咱們的數據庫上的測試驗證了咱們的預測 . 如下是在 HP 上執行時它們的 explain plan:

  第一句:

SQL> select * from mytables where myid>=1;

  已選擇 325917 行。

Execution Plan

----------------------------------------------------------

0 SELECT STATEMENT Optimizer=CHOOSE (Cost=3132 Card=318474 Byt es=141402456)

1 0 TABLE ACCESS (FULL) OF 'MYTABLES' (Cost=3132 Card=318474 Byt es=141402456)

Statistics

----------------------------------------------------------

7 recursive calls

89 db block gets

41473 consistent gets

19828 physical reads

0 redo size

131489563 bytes sent via SQL*Net to client

1760245 bytes received via SQL*Net from client

21729 SQL*Net roundtrips to/from client

1 sorts (memory)

0 sorts (disk)

325917 rows processed

第二句:

Execution Plan

----------------------------------------------------------

0 SELECT STATEMENT Optimizer=CHOOSE (Cost=346 Card=663 Bytes=2 94372)

1 0 TABLE ACCESS (BY INDEX ROWID) OF 'MYTABLES' (Cost=346 Card=663

Bytes=294372)

2 1 INDEX (RANGE SCAN) OF 'PK_MYTABLES' (UNIQUE) (Cost=5 Card=663)

Statistics

----------------------------------------------------------

1278 recursive calls

0 db block gets

6647 consistent gets

292 physical reads

0 redo size

3544898 bytes sent via SQL*Net to client

42640 bytes received via SQL*Net from client

524 SQL*Net roundtrips to/from client

1 sorts (memory)

0 sorts (disk)

7838 rows processed

  顯而易見,第 1 句沒有使用索引,第 2 句使用了主鍵索引 pk_mytables. FF 的巨大影響因而可知一斑。由此想到,咱們在寫 sql 語句時,若是預先估計一下 FF, 你就幾乎能夠預見到 Oracle 會否使用索引。

第二講、索引也有好壞

索引有 B tree 索引, Bitmap 索引, Reverse b tree 索引, 等。最經常使用的是 B tree 索引。 B 的全稱是 Balanced , 其意義是,從 tree 的 root 到任何一個 leaf ,要通過一樣多的 level. 索引能夠只有一個字段( Single column ) , 也能夠有多個字段( Composite ) , 最多 32 個字段, 8I 還支持 Function-based index. 許多 developer 都傾向於使用單列 B 樹索引。

除此以外呢?咱們仍是來看一個例子吧:

  在 HP ( Oracle 8.1.7 ) 上執行如下語句:

select count(1) from mytabs where coid>=130000 and issuedate >= to_date ('2001-07-20', 'yyyy-mm-dd') 。

  一開始,咱們有兩個單列索引: I_mytabs1(coid), I_mytabs2(issuedate), 下面是執行狀況:

COUNT(1)

----------

6427

Execution Plan

----------------------------------------------------------

0 SELECT STATEMENT Optimizer=CHOOSE (Cost=384 Card=1 Bytes=11)

1 0 SORT (AGGREGATE)

2 1 TABLE ACCESS (BY INDEX ROWID) OF 'T_MYTABS' (Cost=384 Card

=126 Bytes=1386)

3 2 INDEX (RANGE SCAN) OF 'I_MYTABS2' (NON-UNIQUE) (Cost=11

Card=126)

Statistics

----------------------------------------------------------

172 recursive calls

1 db block gets

5054 consistent gets

2206 physical reads

0 redo size

293 bytes sent via SQL*Net to client

359 bytes received via SQL*Net from client

2 SQL*Net roundtrips to/from client

5 sorts (memory)

0 sorts (disk)

1 rows processed

  能夠看到,它讀取了 7000 個數據塊來得到所查詢的 6000 多行。

  如今,去掉這兩個單列索引,增長一個複合索引 I_mytabs_test ( coid, issuedate), 從新執行,結果以下:

COUNT(1)

----------

6436

Execution Plan

----------------------------------------------------------

0 SELECT STATEMENT Optimizer=CHOOSE (Cost=3 Card=1 Bytes=11)

1 0 SORT (AGGREGATE)

2 1 INDEX (RANGE SCAN) OF 'I_MYTABS_TEST' (NON-UNIQUE) (Cost=3 Card=126 Bytes=1386)

Statistics

----------------------------------------------------------

806 recursive calls

5 db block gets

283 consistent gets

76 physical reads

0 redo size

293 bytes sent via SQL*Net to client

359 bytes received via SQL*Net from client

2 SQL*Net roundtrips to/from client

3 sorts (memory)

0 sorts (disk)

1 rows processed

  能夠看到,此次只讀取了 300 個數據塊。

7000 塊對 300 塊,這就是在這個例子中,單列索引與複合索引的代價之比。這個例子提示咱們, 在許多狀況下,單列索引不如複合索引有效率。

  能夠說,在索引的設置問題上,其實有許多工做能夠作。正確地設置索引,須要對應用進行整體的分析。

第三講、索引再好,不用也是白搭

拋開前面所說的,假設你設置了一個很是好的索引,任何傻瓜都知道應該使用它,可是 Oracle 卻恰恰不用,那麼,須要作的第一件事情,是審視你的 sql 語句。

Oracle 要使用一個索引,有一些最基本的條件:

1 , where 子句中的這個字段,必須是複合索引的第一個字段;

2 , where 子句中的這個字段,不該該參與任何形式的計算

  具體來說,假設一個索引是按 f1, f2, f3 的次序創建的,如今有一個 sql 語句 , where 子句是 f2 = : var2, 則由於 f2 不是索引的第 1 個字段,沒法使用該索引。

  第 2 個問題,則在咱們之中很是嚴重。如下是從 實際系統上面抓到的幾個例子:

Select jobid from mytabs where isReq='0' and to_date (updatedate) >= to_Date ( '2001-7-18', 'YYYY-MM-DD') ;

………

以上的例子能很容易地進行改進。請注意這樣的語句天天都在咱們的系統中運行,消耗咱們有限的 cpu 和 內存資源。

除了 1 , 2 這兩個咱們必須牢記於心的原則外,還應儘可能熟悉各類操做符對 Oracle 是否使用索引的影響。這裏我只講哪些操做或者操做符會顯式( explicitly )地阻止 Oracle 使用索引。如下是一些基本規則:

1 , 若是 f1 和 f2 是同一個表的兩個字段,則 f1>f2, f1>=f2, f1

2 , f1 is null, f1 is not null, f1 not in, f1 !=, f1 like ‘ %pattern% ' ;

3 , Not exist

4 , 某些狀況下, f1 in 也會不用索引;

對於這些操做,別無辦法,只有儘可能避免。好比,若是發現你的 sql 中的 in 操做沒有使用索引,也許能夠將 in 操做改爲 比較操做 + union all 。筆者在實踐中發現不少時候這頗有效。

可是, Oracle 是否真正使用索引,使用索引是否真正有效,仍是必須進行實地的測驗。合理的作法是,對所寫的複雜的 sql, 在將它寫入應用程序以前,先在產品數據庫上作一次 explain . explain 會得到 Oracle 對該 sql 的解析( plan ) , 能夠明確地看到 Oracle 是如何優化該 sql 的。

若是常常作 explain, 就會發現,喜好寫複雜的 sql 並非個好習慣,由於過度複雜的 sql 其解析計劃每每不盡如人意。事實上,將複雜的 sql 拆開,有時候會極大地提升效率,由於能得到很好的優化。固然這已是題外話了。

相關文章
相關標籤/搜索