程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 數據庫知識 >> Oracle數據庫 >> Oracle教程 >> Oracle性能分析6:數據訪問方式之索引掃描

Oracle性能分析6:數據訪問方式之索引掃描

編輯:Oracle教程

Oracle性能分析6:數據訪問方式之索引掃描


這節將介紹各種索引掃描方式,在了解了各種索引掃描方式的特點後,你就可以判斷你的執行計劃中使用的掃描方式是否正確,並可以針對獲取的信息作出改進。

索引唯一掃描

在下面的場景中使用相等條件時,數據庫使用索引唯一掃描。
1)查詢條件中包含唯一索引中的所有列時;
2)查詢條件使用主鍵約束列時。
下面是一個實際的例子,在表historyalarm中創建如下唯一索引:

create unique index idx_historyalarm$queryid on historyalarm(queryid) tablespace uep4x_fm_index

然後在表上執行查詢:

select * from historyalarm where queryid = 3

該查詢符合上面的第一種情況,會使用索引唯一掃描,該查詢的執行計劃如下:

	DESCRIPTION							OBJECT NAME
-----------------------------------------------------------------------
SELECT STATEMENT, GOAL = ALL_ROWS
 TABLE ACCESS BY GLOBAL INDEX ROWID		HISTORYALARM
  INDEX UNIQUE SCAN						IDX_HISTORYALARM$QUERYID

這裡Oracle首先通過唯一索引掃描找到索引節點,然後使用索引節點中包含的rowid來訪問表中的數據。

索引范圍掃描

當查詢條件可能會返回一定范圍的數據時就會選用索引范圍掃描,索引可以是唯一索引或者不唯一索引,但如果查詢條件包含的數據范圍太大,也有可能導致全表掃描。查詢條件中使用<、>、LIKE、BETWEEN、=等都可能使用索引范圍掃描,需要注意單個=條件在唯一索引或者主鍵上將導致索引唯一掃描。
下面是一個索引范圍掃描的例子,在上面的histroyalarm中執行一個范圍查詢:

select * from caffm4x.historyalarm where queryid < 10

這裡查詢的數據是一個范圍,且使用了queryid列,在queryid列上有唯一索引,但任然會導致索引范圍掃描:

	DESCRIPTION							OBJECT NAME
-----------------------------------------------------------------------
SELECT STATEMENT, GOAL = ALL_ROWS
 TABLE ACCESS BY GLOBAL INDEX ROWID		HISTORYALARM
  INDEX RANGE SCAN						IDX_HISTORYALARM$QUERYID

索引范圍掃描從索引的根節點出發,找到第一個匹配的條目所在的葉子數據塊開始遍歷索引結構,首先從索引條目中取出rowid然後取出對應的表數據塊(通過rowid訪問數據表),接下來葉子索引塊會被再次訪問並讀取下一個索引條目並獲取rowid,這樣反復直到整個葉子索引塊被的索引條目全部被讀出。因此排除索引根節點和中間節點,每行數據讀取需要讀取兩個數據塊,我們可以通過blevel來得到索引高度,通過索引高度和獲取的數據行數就能得到需要讀取的數據塊數,例如:如果blevel為3,讀取5行數據,則總的需要訪問的數據塊次數將是(5*2) + 3 = 13(注意只有根節點時blevel為0)。
如果在讀取了整個葉子索引塊之後,還需要訪問下一個葉子索引塊,在當前的葉子索引塊中有指向下一個葉子索引塊的指針(也含有指向上一個葉子索引塊的指針)。
使用索引范圍掃描的另一個優勢就在於排序,由於索引的節點是有序的,因此如果查詢的結果需要按照索引列排序(升序或者降序),那麼使用索引范圍掃描則可以很好的避免排序操作,例如:

select * from historyalarm where queryid > 10

由於queryid大於10的數據量占總數據量的99%,因此Oracle的優化器選擇了全表掃描:

	DESCRIPTION							OBJECT NAME
-----------------------------------------------------------------------
SELECT STATEMENT, GOAL = ALL_ROWS		
 PARTITION RANGE ALL			
  TABLE ACCESS FULL						HISTORYALARM

如果我們在查詢時對數據指定排序,如下:

select * from historyalarm where queryid > 10 order by queryid

執行計劃如下:

	DESCRIPTION							OBJECT NAME
-----------------------------------------------------------------------
SELECT STATEMENT, GOAL = ALL_ROWS			
 TABLE ACCESS BY GLOBAL INDEX ROWID		HISTORYALARM	
  INDEX RANGE SCAN						IDX_HISTORYALARM$QUERYID

優化器改為使用了索引范圍掃描。由於當數據量很大時,排序的代價是很大的(可能導致物理排序),這時使用索引范圍掃描將是一個很好的選擇,特別是當你排序後選擇部分數據的情況下(rownum < n)。

索引全掃描

索引全掃描會讀取索引上的所有條目,下面幾種情況可能導致索引全掃描:

1)沒有條件但是所需獲取列的列表可以通過其中一列的索引來獲得;

select id from t3

	DESCRIPTION							OBJECT NAME
-----------------------------------------------------------------------
SELECT STATEMENT, GOAL = ALL_ROWS		
 INDEX FULL SCAN						IDX_T3_ID

由於id列帶有索引,因此這裡優化器選擇了索引全掃描。

2)查詢條件中包含排序操作

select * from historyalarm order by queryid

	DESCRIPTION							OBJECT NAME
-----------------------------------------------------------------------
SELECT STATEMENT, GOAL = ALL_ROWS
 INDEX FULL SCAN						IDX_HISTORYALARM$QUERYID

索引全掃描讀取單個數據塊,讀取每個條目的rowid,再通過rowid取出數據行,由於索引已經排序,所以不必執行排序操作。如果查詢只請求了索引列,數據庫將跳過表訪問,只通過訪問索引得到數據。
索引全掃描的另一個優勢在計算最大、最小值時:

select min(queryid) from historyalarm 

	DESCRIPTION							OBJECT NAME
-----------------------------------------------------------------------
SELECT STATEMENT, GOAL = ALL_ROWS	
 SORT AGGREGATE		
  INDEX FULL SCAN (MIN/MAX)				IDX_HISTORYALARM$QUERYID

由於索引本身已經排序,因此在計算最大最小值時只需要很小的代價。

索引跳躍掃描

當查詢條件中帶有符合索引中的列,但是不包含前導列時,就可能導致索引跳躍掃描。數據庫將一個復合索引拆分為多個邏輯子索引,符合索引前導列的不同值決定邏輯子索引的數量,即前導列的不同值越少,索引跳躍式掃描的性能就越好。

select value from t3 where value = 'test'

	DESCRIPTION							OBJECT NAME
-----------------------------------------------------------------------
SELECT STATEMENT, GOAL = ALL_ROWS
 INDEX SKIP SCAN						IDX_T3_COMBINE

索引快速全掃描

當索引本身包含查詢中指定的所有列時,Oracle執行索引快速全掃描。索引快速全掃描和索引全掃描的區別在於:索引全掃描使用單塊讀操作,而索引快速全掃描使用多塊讀。這種掃描不能用於避免排序,因為數據塊是通過無序的多塊讀取來讀取的。

select queryid from historyalarm

	DESCRIPTION							OBJECT NAME
-----------------------------------------------------------------------
SELECT STATEMENT, GOAL = ALL_ROWS
 INDEX FAST FULL SCAN					IDX_HISTORYALARM$QUERYID

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved