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


  在SQL Server 查詢性能優化——覆蓋索引(一)  中講了覆蓋索引的一些理論。

  本文將具體講一下使用不同索引對查詢性能的影響。

  下面通過實例,來查看不同的索引結構,如聚集索引、非聚集索引、組合索引等來查看相同的SQL語句查詢的不同性能

 

例一:沒有任何索引的查詢訪問

  1.表的碎片情況:

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

--要求返回IO統計,也就是數據頁訪問的數量
SET STATISTICS 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 between 50 and 500

--表'WBK_PDE_LIST_ORG_HISTROY'。掃描計數1,邏輯讀取1568 次,物理讀取54 次,預讀1568 次,lob 邏輯讀取0 次,lob 物理讀取0 次,lob 預讀 0 次。

SET STATISTICS IO OFF

 

例二:通過聚集索引查詢訪問

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

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

--要求返回IO統計,也就是數據分頁訪問的數量
SET STATISTICS IO ON
---通過聚集索引查詢訪問的數據頁數量
create clustered index 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 between 50 and 500

--表'WBK_PDE_LIST_ORG_HISTROY'。掃描計數1,邏輯讀取351 次,物理讀取4 次,預讀345 次,lob 邏輯讀取0 次,lob 物理讀取0 次,lob 預讀0 次。

SET STATISTICS IO OFF
---
drop index [WBK_PDE_LIST_ORG_HISTROY].idx_WBK_PDE_LIST_ORG_HISTROY ---

 

 

例三:強制通過非聚集索引查詢訪問

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

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

 

--要求返回IO統計,也就是數據頁訪問的數目
SET STATISTICS IO ON

--強制通過非聚集索引查詢訪問的數據頁數量,用錯索引比不用索引更糟糕
create  index 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 between 50 and 500

--表'WBK_PDE_LIST_ORG_HISTROY'。掃描計數1,邏輯讀取61065 次,物理讀取864 次,預讀727 次,lob 邏輯讀取0 次,lob 物理讀取0 次,lob 預讀0 次。

SET STATISTICS IO OFF
---
drop index [WBK_PDE_LIST_ORG_HISTROY].idx_WBK_PDE_LIST_ORG_HISTROY

 

 

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


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

 

--要求返回IO統計,也就是數據頁訪問的數量
SET STATISTICS IO ON

--通過字段順序不適用的覆蓋索引查詢訪問的數據頁數量
create  index 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 between 50 and 500

--表'WBK_PDE_LIST_ORG_HISTROY'。掃描計數1,邏輯讀取687 次,物理讀取9 次,預讀683 次,lob 邏輯讀取0 次,lob 物理讀取0 次,lob 預讀0 次。

SET STATISTICS IO OFF

---
drop index [WBK_PDE_LIST_ORG_HISTROY].idx_WBK_PDE_LIST_ORG_HISTROY

 

 

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


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


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

 

--要求返回IO統計,也就是數據頁訪問的數量
SET STATISTICS IO ON

--通過覆蓋索引查詢訪問的數據頁數量
create  index 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 between 50 and 500

--表'WBK_PDE_LIST_ORG_HISTROY'。掃描計數1,邏輯讀取178 次,物理讀取5 次,預讀175 次,lob 邏輯讀取0 次,lob 物理讀取0 次,lob 預讀0 次。

SET STATISTICS IO OFF

---
drop index [WBK_PDE_LIST_ORG_HISTROY].idx_WBK_PDE_LIST_ORG_HISTROY

 

 

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

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


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

 

--要求返回IO統計,也就數據頁訪問的數量
SET STATISTICS IO ON

---通過字段順序不適用的覆蓋索引查詢訪問的數據頁數量
create  index 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 between 50 and 500

--表'WBK_PDE_LIST_ORG_HISTROY'。掃描計數1,邏輯讀取682 次,物理讀取1 次,預讀492 次,lob 邏輯讀取0 次,lob 物理讀取0 次,lob 預讀0 次。

SET STATISTICS IO OFF

---
drop index [WBK_PDE_LIST_ORG_HISTROY].idx_WBK_PDE_LIST_ORG_HISTROY

 

 

例七:通過子葉層覆蓋索引查詢訪問(INCLUDE)

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


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

 

--要求返回IO統計,也就是數據頁訪問的數量
SET STATISTICS IO ON

--通過子葉層覆蓋索引查詢訪問的數據頁數量
create  index 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 between 50 and 500

--表'WBK_PDE_LIST_ORG_HISTROY'。掃描計數1,邏輯讀取177 次,物理讀取4 次,預讀173 次,lob 邏輯讀取0 次,lob 物理讀取0 次,lob 預讀0 次。

SET STATISTICS IO OFF
drop index [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)

   
   

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM