這一節主要講述索引的使用,首先介紹怎麼在查詢中避免使用索引,然後介紹優化器怎麼判斷是否使用索引,並介紹了強制使用索引的方法,最後介紹了Oracle的並行處理方法。
雖然你創建了索引,但有些查詢你可能需要避免使用這些索引,或者你為了做一些測試,希望看看各種情況下查詢的情況,也希望能夠避免使用一些索引或者索引掃描方式。Oracle提供了方式來達到這些目地,就是在查詢中使用hint信息,具體情況如下。
如果索引的選擇性很差,那麼也許使用其它索引或者使用全表掃描的效率會更加高效,這時你可以考慮避免使用該索引,方法如下:
/*+ no_index(table_name index_name)*/
表示查詢不使用table_name的index_name索引,下面是一個實例:
select /*+ no_index(historyalarm idx_historyalarm$clear)*/ position1 from historyalarm
優化器不使用指定索引後,仍然可以使用表上的其它索引,你也可以指定忽略一組索引:
select /*+ no_index(historyalarm idx_historyalarm$clear,idx_historyalarm$cleargmt)*/ position1 from historyalarm
如果你只是指定了no_index提示,而沒有列出任何索引,優化器將忽略指定表的所有索引:
select /*+ no_index(historyalarm)*/ position1 from historyalarm
語法如下:
/*+ no_index_ffs(table_name index_name)*/
表示查詢不使用table_name的index_name索引的索引快速掃描,下面是一個實例:
select /*+ no_index_ffs(historyalarm idx_historyalarm$clear)*/ position1 from historyalarm
強制表掃描也是避免使用索引的一個方法,如下:
/*+ full(table_name)*/
表示查詢表table_name時強制使用全表掃描,下面是一個實例:
select /*+ full(historyalarm)*/ position1 from historyalarm
查詢時優化器會計算使用索引的成本信息,以決定是否使用索引,表的選擇性(通過過濾)乘以索引聚蔟因子(clustering factor),決定了通過索引訪問表的成本。下面我們從聚蔟因子開始。
聚蔟因子對應user_indexes視圖的CLUSTERING_FACTOR列,用於表示索引的葉子項怎麼和表格的行對應關系,下面是聚蔟因子的計算方法:
為在索引中的每一個條目,Oracle比較條目所在表的數據塊和前一個的索引條目數據塊,如果是不同的數據塊,則聚蔟因子加1,否則繼續。
因此,聚蔟因子的最小值等於索引上的所有條目對應的數據塊數量(由於在單列或多列上的索引都不包含空數據,因此這將等於在表中包含了數據的數據塊的數量),而最大值等於索引上的條目的數量(每兩個相鄰的條目都在不同的數據塊塊上)。
下面看一個例子,我們使用表T1和T2(請看“Oracle性能分析4:數據訪問方法之全掃描”),下面是查找表T1上的索引IDX_T1_ID和T2上的索引IDX_T2_ID的聚蔟因子:
select i.INDEX_NAME, i.CLUSTERING_FACTOR from user_indexes i where index_name = 'IDX_T1_ID' or index_name = 'IDX_T2_ID' INDEX_NAME CLUSTERING_FACTOR IDX_T1_ID 17 IDX_T2_ID 1700
由於表T1中相同的數據都按順序存儲,因此索引條目的順序和數據的存儲順序基本一致,因此T1表的聚蔟因子達到了最低值;而T2表中的數據相同值都存放在不同的數據塊上,因此索引條目中任意兩個相鄰的條目對應的數據都在不同的塊上,因此聚蔟因子達到了最大值。因此T1表的IDX_T1_ID索引使用代價很低,而T2表的IDX_T2_ID索引使用的代價則很高。
需要注意的是聚蔟因子與表中數據而不是與索引有關,因此重建索引對它沒有任何影響。
當Oracle的優化器無法選擇正確的索引時(優化器並不是完美的,它並不總能准確地了解數據的性質和分布),我們可以考慮強制使用索引,下面介紹使用hint信息來強制使用索引的方法。
需要注意的是在使用提示之前,應先查看是否有對象的正確的統計信息,並檢查SQL語句的構造方法,提示應該是無路可走時的選擇。
index提示指示優化器對特定的表使用索引掃描,例如:
select /*+ index(historyalarm idx_historyalarm$clear)*/ position1 from historyalarm
這裡相當於強制優化器使用idx_historyalarm$clear,也可以不指定索引,這時優化器就會選擇它認為最優的索引:
select /*+ index(historyalarm)*/ position1 from historyalarm
這樣做的缺陷在於優化器可能會選擇錯誤的索引,因此通常這並不是好的選擇。
如果你需要在index提示中指定多個索引,可以使用index_combine提示,如下;
select /*+ index_combine(historyalarm idx_historyalarm$01 idx_historyalarm$02)*/ position1 from historyalarm
這時,優化器會使用兩個指定索引成本最低的組合,如果不提供索引列表,則優化器將根據其成本估計使用索引的最佳組合。
除了上面介紹的hint信息,還有下面的常用的跟索引相關的hint信息:
1)index_asc:指定按升序做索引范圍掃描;
2)index_desc:指定按降序順序掃描升序索引,或者按升序掃描降序索引;
3)index_join:當兩個索引包含返回查詢結果所需的所有列時,使用該提示連接索引,從而從索引直接獲取所有數據,語法類似index_combine;
4)index_ss:執行索引跳躍式掃描;
5)index_ffs:以索引快速全掃描的方式訪問數據。
導致hint失效的主要原因如下:
1)優化器認為使用hint會導致錯誤的結果。這往往由於數據中包含許多空值,因此有一些不會出現在索引中的行,導致索引掃描最後可能會導致錯誤的結果;
2)SQL語句中的表指定了別名,這時hint中也必須使用別名,否則hint會被忽略,如:
select /*+ index(h idx_historyalarm$raise)*/ position1 from historyalarm h
3)查詢涉及表連接,基於優化器選額的連接方法,無法使用索引。這時往往可以通過添加更多的提示信息來解決(如指定表的連接方式等)。
在Oracle中支持並行處理操作,下面介紹和並行處理相關的概念。
指定SQL執行的並行度,這個值會覆蓋表自身設定的並行度,如:
select /*+ parallel(h 4)*/ * from historyalarm h
parallel有以下幾種級別:
1)parallel:總是並行執行,數據庫計算並行度,值大於等於2;
2)parallel(default):和parallel相同;
3)parallel(auto):數據庫計算並行度,值大於等於1,當並行度為1時表示串行執行;
4)parallel(manual):優化器使用表的並行度;
5)parallel(integer):優化器使用指定的並行度。
在創建表時指定並行度
create table t1 parallel 4 as select trunc((rownum - 1) / 100) id, rownum value from dba_source where rownum <= 10000
查看表的並行度
select degree from user_tables where table_name = 'T1'
修改表的並行度
alter table t1 parallel(degree 3)
取消表的並行度
alter table t1 noparallel
上面的方法也同樣適用於索引。
在SQL中禁止使用並行。
select /*+ no_parallel(h)*/ * from historyalarm h
在處理分區索引時,可以指示優化器使用多個並發的並行服務器,來並行化劑中類型的索引操作。可以並行化的操作包括索引范圍掃描、全索引掃描和索引快速全掃描。
下面是通過parallel_index提示來指定分區索引的並行掃描操作:
select /*+ parallel_index(h idx_historyalarm$06 4)*/ alarmclearedtime, alarmkey from historyalarm h
整數4表示索引掃描的並行度。
禁止使用索引並行掃描。