2011-11-17 47 views
5

我一直在學習Oracle SQL中的索引,並且我想用測試表進行一個小實驗,以查看索引是如何工作的。正如我在此處發佈的一篇文章中發現的,最好的方法是使用EXPLAIN PLAN。但是,我遇到了讓我困惑的事情。索引,EXPLAIN PLAN和Oracle SQL中的記錄訪問

我的示例表包含屬性(EmpID,Fname,Lname,Occupation,....等)。我使用我寫的java程序(隨機名稱,職業等)填充了500,000條記錄。現在,這裏有一些示例查詢使用和不使用索引:

NO INDEX:

SELECT Fname FROM EMPLOYEE WHERE Occupation = 'DOCTOR'; 

EXPLAIN PLAN說:

OPERATION       OPTIMIZER COST 
TABLE ACCESS(FULL) TEST.EMPLOYEE ANALYZED 1169 

現在我創建索引:

CREATE INDEX occupation_idx 
    ON EMPLOYEE (Occupation); 

WITH INDEX「occupation_idx」:

SELECT Fname FROM EMPLOYEE WHERE Occupation = 'DOCTOR'; 

EXPLAIN PLAN說:

OPERATION       OPTIMIZER COST 
TABLE ACCESS(FULL) TEST.EMPLOYEE ANALYZED 1169 

所以...成本還是一樣,1169?我現在試試這個:

WITH INDEX 「occupation_idx」:

SELECT Occupation FROM EMPLOYEE WHERE Occupation = 'DOCTOR'; 

EXPLAIN PLAN說:

OPERATION        OPTIMIZER COST 
INDEX(RANGE SCAN) TEST.OCCUPATION_IDX ANALYZED 67 

所以,看來當該列是唯一一個索引只使用我從中拉取價值。但我認爲索引的要點是使用索引列作爲關鍵字解鎖整個記錄?上面的搜索是一個毫無意義的搜索...它搜索你已經知道的值。我能想到的唯一有價值的查詢只涉及索引列的值(而不是記錄的其餘部分),它將是諸如COUNT之類的集合。

我錯過了什麼?

+0

有趣的問題,我會好奇,看看爲什麼會發生這種情況。 – ChandlerPelhams

+0

是爲EMPLOYEE表定義的主鍵嗎? –

+0

索引是否可能尚未完成?如果您重新運行查詢SELECT FNAME FROM EMPLOYEE WHERE Occupation ='DOCTOR';它的成本下降了嗎? – xQbert

回答

5

即使使用您的索引,Oracle也決定對第二個查詢進行全面掃描。

它爲什麼這樣做?甲骨文將創建了兩個計劃,並拿出爲每個成本: -

1)全掃描

2)索引訪問

甲骨文選擇具有較低成本的計劃。很顯然,它將全面掃描作爲較低的成本。

如果你想看到的指標計劃的成本,你可以做一個像這樣帶有一絲的解釋計劃來強制使用索引:

SELECT /*+ INDEX(EMPLOYEE occupation_idx) */ Fname 
FROM EMPLOYEE WHERE Occupation = 'DOCTOR'; 

如果一個以上解釋計劃,您會看到成本高於全掃描成本。這就是爲什麼Oracle沒有選擇使用索引。

一個簡單的方法來考慮指標計劃的成本是: -

  • 指數(多少塊必須從上到下讀取)
  • 表塊數的blevel必須隨後讀取索引中匹配的記錄。這依賴於甲骨文對擁有「醫生」職位的員工人數的估計。在簡單的例子中,這將是:不同值的

    行數/數

更復雜的考慮因素包括聚類工廠和索引費用調整這兩者反映所讀取的塊的情形產生已經在內存中,因此不需要從磁盤讀取。

也許你可以從你的查詢的結果與索引提示,也該查詢的結果更新你的問題: -

SELECT COUNT(*), COUNT(DISTINCT(Occupation)) 
FROM EMPLOYEE; 

這將使人們對指數計劃的成本發表評論。

+0

感謝您的回覆。我從來不知道「提示」。但是,它似乎並沒有工作。即使有了這個提示,它仍然會進行全面掃描,就像沒有提示一樣。 – The111

+0

@約翰遜:我有錯誤的提示。請參閱編輯。 –

+0

感謝您的修正提示。當我創建我的表格時,我故意讓我的隨機器腳本對每個字段都有不同的大小域。例如,lname字段從500個lnames中選擇,而fname字段僅從100箇中選擇。我知道這些變體最後很有趣。使用提示,我現在能夠精確地指出索引變得有用的大小域(順便說一下,當域有大約450個值時可能......或換句話說:當索引帳戶找到的行不到表格的0.2%)。 – The111

0

作爲一個WAG。分析表格和索引,然後查看計劃是否更改。

當您僅選擇職業時,可以從索引中滿足整個查詢。索引字面上有一份職業的副本。在您爲select添加額外列的那一刻,Oracle必須轉到數據記錄才能得到它。優化器選擇讀取所有數據行而不是所有索引行和數據行。這個更便宜。

+0

沒有運氣。不過謝謝。 – The111

+0

那麼,全表掃描是更便宜的方式來做到這一點。我們在這裏討論的是什麼樣的數據量? – EvilTeach

+0

給你的箭袋添加另一個箭頭。學習使用AUTOTRACE和SQLPLUS。 http://www.adp-gmbh.ch/ora/sqlplus/autotrace.html – EvilTeach

2

索引是其中僅存儲以下數據中的表的副本:

  • 索引字段(一個或多個)
  • 的指針原始行(rowid)。

假設你有一個表是這樣的:

rowid id name occupation 
[1]  1 John clerk 
[2]  2 Jim manager 
[3]  3 Jane boss 

然後在occupation的指數應該是這樣的:

occupation rowid 
boss  [3] 
manager  [2] 
clerk  [1] 

,與記錄在B-Tree上排序occupation。如你所見,如果你只選擇索引字段,你只需要索引(第二個表)。

如果選擇除occupation其他任何東西:

SELECT * 
FROM mytable 
WHERE occupation = 'clerk' 

此時發動機應該讓兩件事情:首先找到在索引中的相關記錄,第二,通過rowid發現在原表中的記錄。這就像是你加入了rowid的兩張桌子。

由於索引中的rowid未按順序排列,因此對原始表的讀取不是順序的,可能會很慢。按順序讀取原始表可能會更快,只需使用occupation = 'clerk'過濾記錄即可。

引擎不「解鎖」記錄:它只是在索引中找到rowid,並且如果索引本身沒有足夠的數據,它將通過找到的rowid查找原始表中的數據。

3

我想我看到這裏發生了什麼。

當您準備好索引,你這樣做:

SELECT Occupation FROM EMPLOYEE WHERE Occupation = 'DOCTOR'; 

執行計劃將使用索引。這是一件容易的事情,因爲索引中所有滿足查詢要求的數據都在索引中,Oracle甚至根本無需引用表。

但是,當你這樣做:

SELECT Fname FROM EMPLOYEE WHERE Occupation = 'DOCTOR'; 

那麼,如果Oracle使用的指數,它會做一個索引範圍掃描,然後一個TABLE ACCESS BY ROWID查找對應於該職業FName參數。現在,根據具有DOCTOR for Occupation的行數,Oracle必須進行一次或多次到表的查找,以查找Fname。例如,如果您有一個表格,並且所有員工都將「職業」設置爲「DOCTOR」,那麼索引用處不大,Oracle將僅執行表格的FULL TABLE SCAN。如果有10,000名員工,並且只有一名是DOCTOR,那麼再說一次,這是毫不費力的,Oracle將使用該索引。

但有一些微妙之處,當你在這兩個極端之間的某處。在討論是否使用索引時,人們喜歡談論「選擇性」,即索引標識了多少行,與表的大小。但是,這不是真的是真的。什麼Oracle 真的關心的是塊選擇性。也就是說,有多少區塊是否必須訪問以滿足查詢?所以,首先,RANGE SCAN的「寬」是多少?謂詞值指定的值範圍越有限,越好。其次,當您的查詢需要執行表查找時,需要訪問多少個不同的塊才能找到所需的所有數據。也就是說,相對於索引順序,中的數據如何「隨機」?這被稱爲CLUSTERING_FACTOR。如果您分析索引以收集統計信息,然後查看USER_INDEXES,則會看到現在已經填充了CLUSTERING_FACTOR。

那麼,什麼是CLUSTERING_FACTOR? CLUSTERING_FACTOR是關於索引的關鍵列的表的「有序性」。 CLUSTERING_FACTOR的值將始終位於表中的塊數和表中的行數之間。 A low CLUSTERING_FACTOR,即非常接近區塊中的一個,表示相對於索引非常有序的表格。 A CLUSTERING_FACTOR,即表中非常接近的一個,相對於索引非常無序。

理解CLUSTERING_FACTOR描述中的數據相對於索引的順序是一個重要概念。所以,重建一個索引,例如而不是更改CLUSTERING_FACTOR。瞭解同一個表可以有兩個索引,一個可以有一個出色的CLUSTERING_FACTOR,另一個可能有一個非常差的CLUSTERING_FACTOR也很重要。表格本身只能以一種方式訂購。

那麼,爲什麼我花了這麼多時間來描述CLUSTERING_FACTOR?因爲當你有一個執行計劃執行INDEX RANGE SCAN和TABLE ACCESS BY ROWID時,你可以確定Oracle的優化器已經考慮CLUSTERING_FACTOR來提出執行計劃。例如,假設您有一個10,000行的表,並且假設有100個行擁有Occupation ='DOCTOR'。你寫上面的查詢,詢問職業是醫生的僱員的Fname。那麼,Oracle可以非常輕鬆高效地確定職業是DOCTOR的行的rowid。但是,Oracle需要訪問多少塊塊才能執行Fname查找?如果數據按表中的職業進行聚類(排序),則可能只有1個或2個表格塊。但是,如果數據在表格中非常無序,則可能多達100個!因此,我們再次假設10,000行表,並且假設(出於說明和簡單的數學目的)該表具有100行/塊,以及100個塊。根據表格順序(即CLUSTERING_FACTOR),表格訪問次數可能少至1次,或多達100次。

所以,我希望這可以幫助您理解爲什麼優化器可能不願意使用索引在某些情況下。

+0

+1有用的細節! –