在SQL Server 查詢性能優化——覆寫索引(一) 中講了覆寫索引的一些理論。
本文将具體講一下使用不同索引對查詢性能的影響。
下面通過執行個體,來檢視不同的索引結構,如聚集索引、非聚集索引、組合索引等來檢視相同的SQL語句查詢的不同性能
例一:沒有任何索引的查詢通路
1.表的碎片情況:
2.SQL查詢語句與查詢執行計劃成本--要求傳回IO統計,也就是資料頁通路的數量SETSTATISTICS IO ON--沒有任何索引情況下的資料頁通路數量SELECT[WBOOK_NO] ,[COP_G_NO] ,[G_NO] ,[CODE_T] ,[QTY_1] ,[UNIT_1] ,[TRADE_TOTAL] ,[GROSS_WT]FROM[WBK_PDE_LIST_ORG_HISTROY]where qty_1 between50and500--表'WBK_PDE_LIST_ORG_HISTROY'。掃描計數1,邏輯讀取1568 次,實體讀取54 次,預讀1568 次,lob 邏輯讀取0 次,lob 實體讀取0 次,lob 預讀 0 次。SETSTATISTICS IO OFF
例二:通過聚集索引查詢通路
1.聚集索引的碎片情況:
2.SQL查詢語句與查詢執行計劃成本
--要求傳回IO統計,也就是資料分頁通路的數量SETSTATISTICS IO ON---通過聚集索引查詢通路的資料頁數量createclusteredindex idx_WBK_PDE_LIST_ORG_HISTROY on[WBK_PDE_LIST_ORG_HISTROY](QTY_1)SELECT[WBOOK_NO] ,[COP_G_NO] ,[G_NO] ,[CODE_T] ,[QTY_1] ,[UNIT_1] ,[TRADE_TOTAL] ,[GROSS_WT]FROM[WBK_PDE_LIST_ORG_HISTROY]where qty_1 between50and500--表'WBK_PDE_LIST_ORG_HISTROY'。掃描計數1,邏輯讀取351 次,實體讀取4 次,預讀345 次,lob 邏輯讀取0 次,lob 實體讀取0 次,lob 預讀0 次。SETSTATISTICS IO OFF---dropindex[WBK_PDE_LIST_ORG_HISTROY].idx_WBK_PDE_LIST_ORG_HISTROY---
例三:強制通過非聚集索引查詢通路
1.非聚集索引的碎片情況:
--要求傳回IO統計,也就是資料頁通路的數目SETSTATISTICS IO ON--強制通過非聚集索引查詢通路的資料頁數量,用錯索引比不用索引更糟糕createindex idx_WBK_PDE_LIST_ORG_HISTROY on[WBK_PDE_LIST_ORG_HISTROY](WBOOK_NO)SELECT[WBOOK_NO] ,[COP_G_NO] ,[G_NO] ,[CODE_T] ,[QTY_1] ,[UNIT_1] ,[TRADE_TOTAL] ,[GROSS_WT]FROM[WBK_PDE_LIST_ORG_HISTROY]with (index(idx_WBK_PDE_LIST_ORG_HISTROY)) where qty_1 between50and500--表'WBK_PDE_LIST_ORG_HISTROY'。掃描計數1,邏輯讀取61065 次,實體讀取864 次,預讀727 次,lob 邏輯讀取0 次,lob 實體讀取0 次,lob 預讀0 次。SETSTATISTICS IO OFF---dropindex[WBK_PDE_LIST_ORG_HISTROY].idx_WBK_PDE_LIST_ORG_HISTROY
例四:通過字段順序不适用的覆寫索引查詢通路
--要求傳回IO統計,也就是資料頁通路的數量SETSTATISTICS IO ON--通過字段順序不适用的覆寫索引查詢通路的資料頁數量createindex idx_WBK_PDE_LIST_ORG_HISTROY on[WBK_PDE_LIST_ORG_HISTROY]([WBOOK_NO] ,[COP_G_NO] ,[G_NO] ,[CODE_T] ,[QTY_1] ,[UNIT_1] ,[TRADE_TOTAL] ,[GROSS_WT])SELECT[WBOOK_NO] ,[COP_G_NO] ,[G_NO] ,[CODE_T] ,[QTY_1] ,[UNIT_1] ,[TRADE_TOTAL] ,[GROSS_WT]FROM[WBK_PDE_LIST_ORG_HISTROY]where qty_1 between50and500--表'WBK_PDE_LIST_ORG_HISTROY'。掃描計數1,邏輯讀取687 次,實體讀取9 次,預讀683 次,lob 邏輯讀取0 次,lob 實體讀取0 次,lob 預讀0 次。SETSTATISTICS IO OFF---dropindex[WBK_PDE_LIST_ORG_HISTROY].idx_WBK_PDE_LIST_ORG_HISTROY
例五:通過覆寫索引查詢通路
--要求傳回IO統計,也就是資料頁通路的數量SETSTATISTICS IO ON--通過覆寫索引查詢通路的資料頁數量createindex idx_WBK_PDE_LIST_ORG_HISTROY on[WBK_PDE_LIST_ORG_HISTROY]([QTY_1] ,[COP_G_NO] ,[G_NO] ,[CODE_T] ,[WBOOK_NO] ,[UNIT_1] ,[TRADE_TOTAL] ,[GROSS_WT])SELECT[WBOOK_NO] ,[COP_G_NO] ,[G_NO] ,[CODE_T] ,[QTY_1] ,[UNIT_1] ,[TRADE_TOTAL] ,[GROSS_WT]FROM[WBK_PDE_LIST_ORG_HISTROY]where qty_1 between50and500--表'WBK_PDE_LIST_ORG_HISTROY'。掃描計數1,邏輯讀取178 次,實體讀取5 次,預讀175 次,lob 邏輯讀取0 次,lob 實體讀取0 次,lob 預讀0 次。SETSTATISTICS IO OFF---dropindex[WBK_PDE_LIST_ORG_HISTROY].idx_WBK_PDE_LIST_ORG_HISTROY
例六:通過字段順序不适用的覆寫索引查詢通路
--要求傳回IO統計,也就資料頁通路的數量SETSTATISTICS IO ON---通過字段順序不适用的覆寫索引查詢通路的資料頁數量createindex idx_WBK_PDE_LIST_ORG_HISTROY on[WBK_PDE_LIST_ORG_HISTROY]([WBOOK_NO]) include(qty_1 ,[COP_G_NO] ,[G_NO] ,[CODE_T] ,[UNIT_1] ,[TRADE_TOTAL] ,[GROSS_WT])SELECT[WBOOK_NO] ,[COP_G_NO] ,[G_NO] ,[CODE_T] ,[QTY_1] ,[UNIT_1] ,[TRADE_TOTAL] ,[GROSS_WT]FROM[WBK_PDE_LIST_ORG_HISTROY]where qty_1 between50and500--表'WBK_PDE_LIST_ORG_HISTROY'。掃描計數1,邏輯讀取682 次,實體讀取1 次,預讀492 次,lob 邏輯讀取0 次,lob 實體讀取0 次,lob 預讀0 次。SETSTATISTICS IO OFF---dropindex[WBK_PDE_LIST_ORG_HISTROY].idx_WBK_PDE_LIST_ORG_HISTROY
例七:通過子葉層覆寫索引查詢通路(INCLUDE)
--要求傳回IO統計,也就是資料頁通路的數量SETSTATISTICS IO ON--通過子葉層覆寫索引查詢通路的資料頁數量createindex idx_WBK_PDE_LIST_ORG_HISTROY on[WBK_PDE_LIST_ORG_HISTROY](qty_1) include([WBOOK_NO] ,[COP_G_NO] ,[G_NO] ,[CODE_T] ,[UNIT_1] ,[TRADE_TOTAL] ,[GROSS_WT])SELECT[WBOOK_NO] ,[COP_G_NO] ,[G_NO] ,[CODE_T] ,[QTY_1] ,[UNIT_1] ,[TRADE_TOTAL] ,[GROSS_WT]FROM[WBK_PDE_LIST_ORG_HISTROY]where qty_1 between50and500--表'WBK_PDE_LIST_ORG_HISTROY'。掃描計數1,邏輯讀取177 次,實體讀取4 次,預讀173 次,lob 邏輯讀取0 次,lob 實體讀取0 次,lob 預讀0 次。SETSTATISTICS IO OFFdropindex[WBK_PDE_LIST_ORG_HISTROY].idx_WBK_PDE_LIST_ORG_HISTROY
通路方式分頁 | 邏輯讀 | 實體讀 | 預讀 | 估計運算符開銷 |
全表掃描 | 1568 | 54 | 1.06575 | |
以QTY_1字段建立聚集索引 | 351 | 4 | 345 | 0.275863 |
以WBOOK_NO字段建非立聚集索引 | 61065 | 864 | 727 | 14.10295 |
以[WBOOK_NO],[COP_G_NO],[G_NO],[CODE_T],[QTY_1],[UNIT_1],[TRADE_TOTAL] ,[GROSS_WT]八個字段建複合索引 | 687 | 9 | 683 | 0.570198 |
以[QTY_1],[COP_G_NO],[G_NO],[CODE_T],[WBOOK_NO],[UNIT_1],[TRADE_TOTAL],[GROSS_WT]八個字段建複合索引 | 178 | 5 | 175 | 0.146974 |
以WBOOK_NO建立索引,include以下字段 [QTY_1],[COP_G_NO],[G_NO],[CODE_T],[UNIT_1],[TRADE_TOTAL],[GROSS_WT] | 682 | 1 | 492 | |
以[QTY_1]建立索引,include 以下字段[WBOOK_NO],[COP_G_NO],[G_NO],[CODE_T],[UNIT_1],[TRADE_TOTAL],[GROSS_WT] | 177 | 173 | ||
例一/例二/例三/例四/例五/例六/例七 | 8.8/2/345/3.9/1/3.9/1 | 54/4/864/9/5/1/4 | 8.8/2/4.1/3.9/1/2.8 | 7.2/1.9/96/3.9/1/3.9/1 |
從上表中可以得出一個結論,如果索引使用不當,例如上面的例三——強制使用選擇性很低的索引來查找資料(或是索引統計資料錯誤、優化引擎誤判等,造成索引使用不當),反而會導緻大量的I/O操作(邏輯讀61065次,實體讀864次),其成本比進行全表掃描(例一)還高。
例二,通過聚集索引來查找,因為縮小了資料表掃描範圍,是以效果較佳。
例五、例七,建立覆寫索引,因為資料結構遠小于資料表本身,是以不管組合索引的字段順序是否正确,都有更好的查詢效果。當然 ,依WHERE條件所需要的字段建立索引資料擺放順序,也就是[QTY_1]放在索引順序的第一位,再include查詢所需要的字段([WBOOK_NO],[COP_G_NO],[G_NO],[CODE_T],[UNIT_1],[TRADE_TOTAL],[GROSS_WT]),其查詢性能最佳。
最後要提醒注意:
1) 在建立覆寫查詢時要盡量限制索引鍵值的大小,保持Row-to-key的大小比例差異越大越好。否則掃描覆寫索引與掃描資料表所花的I/O操作差不多,這樣就失去了建立覆寫索引的意義。
2) 覆寫索引可以用來提升查詢性能,因為索引中包含了所有查詢裡的列.非聚集索引為表裡的每一行用索引鍵值來存儲一行。另外SQL Server能使用索引頁級裡的這些行來執行聚集計算。這意味着SQLServer不必去實際的表執行聚集計算,這樣可以提升性能。
3) 覆寫索引能提升擷取資料的性能,但它們也能降低INSERT、UPDATE和DELETE操作的性能。這是因為維護覆寫索引要求做一些額外的工作。通常這不是問題,除非你的資料庫經常進行非常高的INSERT、UPDATE和DELETE操作。你也許不得不在你的産品系統上應用覆寫索引之前,要先進行實驗,看看你所建立的覆寫索引是否在提升性能方面上比影響性能方面更有幫助。
4) 應該在那些SELECT查詢中常使用到的列上建立覆寫索引,但覆寫索引中包括過多的列也不行,因為覆寫索引列的值是存儲在記憶體中的,這樣會消耗過多記憶體,引發性能下降。
關于索引碎片的修複:
關于上圖的一些說明:
avg_fragmentation_in_percent:邏輯碎片(索引中的無序頁)的百分比。這是索引的葉級頁中出錯頁所占的百分比。對于出錯頁,配置設定給索引的下一個實體頁不是由目前葉級頁中的“下一頁”指針所指向的頁。
fragment_count : 索引中的碎片(實體上連續的葉頁)數量。
avg_fragment_size_in_pages :索引中一個碎片的平均頁數。
知道索引碎片程度後,可以使用下表确定修複碎片的最佳方法。
avg_fragmentation_in_percent 值 | 修複語句 |
> 5% 且 < = 30% | ALTER INDEX REORGANIZE |
> 30% | |
| |