SQL Server 查詢性能優化——覆蓋索引(二)

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.非聚集索引的碎片情況:

  2.SQL查詢語句與查詢執行計劃成本

複製代碼
--要求返回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
複製代碼

例四:通過字段順序不適用的覆蓋索引查詢訪問
  1.非聚集索引的碎片情況:


  2.SQL查詢語句與查詢執行計劃成本

複製代碼
--要求返回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
複製代碼

例五:通過覆蓋索引查詢訪問


  1.非聚集索引的碎片情況:


  2.SQL查詢語句與查詢執行計劃成本

複製代碼
--要求返回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
複製代碼

例六:通過字段順序不適用的覆蓋索引查詢訪問

  1.聚集索引的碎片情況:


  2.SQL查詢語句與查詢執行計劃成本

複製代碼
--要求返回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)

  1.聚集索引的碎片情況:


  2.SQL查詢語句與查詢執行計劃成本

複製代碼
--要求返回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

1568

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

0.570198

以[QTY_1]建立索引,include 以下字段[WBOOK_NO],[COP_G_NO],[G_NO],[CODE_T],[UNIT_1],[TRADE_TOTAL],[GROSS_WT]

177

4

173

0.146974

 例一/例二/例三/例四/例五/例六/例七

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、UPDATEDELETE操作的性能這是因爲維護覆蓋索引要求做一些額外的工作通常這不是問題除非你的數據庫經常進行非常高的INSERT、UPDATEDELETE操作。你也許不得不在你的產品系統上應用覆蓋索引之前,要先進行實驗,看看你所建立的覆蓋索引是否在提升性能方面上比影響性能方面更有幫助

4) 應該在那些SELECT查詢中常使用到的列上創建覆蓋索引,但覆蓋索引中包括過多的列也不行,因爲覆蓋索引列的值是存儲在內存中的,這樣會消耗過多內存,引發性能下降。

關於索引碎片的修復:

關於上圖的一些說明:

avg_fragmentation_in_percent:邏輯碎片(索引中的無序頁)的百分比。這是索引的葉級頁中出錯頁所佔的百分比。對於出錯頁,分配給索引的下一個物理頁不是由當前葉級頁中的下一頁指針所指向的頁。

fragment_count : 索引中的碎片(物理上連續的葉頁)數量。 

avg_fragment_size_in_pages :索引中一個碎片的平均頁數。 

知道索引碎片程度後,可以使用下表確定修復碎片的最佳方法。

avg_fragmentation_in_percent 

  修復語句  

> 5% 且 < = 30%

ALTER INDEX REORGANIZE

> 30%

ALTER INDEX REBUILD WITH (ONLINE = ON)

http://www.cnblogs.com/chillsrc/archive/2012/09/05/2672529.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章