1. 程式人生 > >聚集索引: 三級階梯SQL Server索引

聚集索引: 三級階梯SQL Server索引

str 兩種 討論 數據庫設計 沒有 大表 告訴 結合 書簽

原文鏈接:http://www.sqlservercentral.com/articles/Stairway+Series/72351/

聚集索引:三級階梯SQL Server索引

通過大衛·杜蘭特,2013/01/25(第一次出版:2011/06/22)

該系列

本文是樓梯系列的一部分:SQL Server的階梯索引

索引數據庫設計的基礎,告訴開發人員使用數據庫設計者的意圖。 不幸的是索引時往往是後加上的性能問題出現。 終於在這裏是一個簡單的系列文章,應該讓任何數據庫專業迅速加速

前面的水平在這個樓梯概述了非聚集索引的索引一般和特別。 結論用以下關於SQL Server關鍵概念索引。 當一個請求到達您的數據庫,

一個SELECT語句或一個INSERTUPDATEDELETE語句,SQL Server只有三種可能的方法來訪問數據表中引用的聲明:

  • 訪問非聚集索引,避免訪問表。 這只可能在索引中包含的所有數據查詢這個表所要求的
  • 使用搜索鍵(s)來訪問索引,然後使用所選的書簽(s)來訪問表的單個行。
  • 忽略了索引和搜索請求的表行。

這個水平首先關註的第三選擇上面的列表; 搜索表。 反過來,這將導致我們的討論集群索引; 一個被提及的話題,但沒有涵蓋,2級。

AdventureWorks我們將使用的數據庫表是在這個水平SalesOrderDetail表。 在121317,足以說明一些好處有聚集索引的表。 有兩個外鍵

,它是復雜的足以說明一些設計決策,你必須使你的聚集索引。

示例數據庫

盡管我們已經討論了在一級sample數據庫,值得重復。 在整個樓梯,我們將使用示例來說明概念。 這些例子是基於微軟的AdventureWorks示例數據庫。 我們專註於銷售訂單。 五表將給我們一個良好的事務性和非事務性數據;客戶,銷售人員,產品,SalesOrderHeader,SalesOrderDetail。 為了保持專註,我們使用列的一個子集。 因為AdventureWorks規範化,銷售人員信息分解成三個表:銷售人員,員工和聯系。

整個樓梯我們使用以下兩個術語,指一行訂單互換:“行項目訂單細節。 前者是更常見的業務術語

; 後者的名字出現在一個AdventureWorks表。

完整的一套表,和它們之間的關系,如圖1所示。

Zoom in | Open in new window

1:表中使用的例子在這個樓梯

註意:
所有TSQL代碼所示這樓梯水平隨著文章可以下載。

聚集索引

我們先問以下問題:有多少工作需要找到表中的一行(s)如果不使用非聚集索引? 搜索請求的行表意味著掃描一個無序表中每一行嗎? 永久或SQL Server序列表的行,以便它可以快速訪問他們的搜索鍵,就像快速訪問非聚集索引的搜索鍵的條目嗎? 答案取決於你是否指示SQL Server上創建一個聚集索引表。

與非聚集索引是一個單獨的對象,占據自己的空間,聚集索引和表是一樣的。 通過創建聚集索引,您指示SQL Server排序表的行索引鍵序列,在未來保持序列數據的修改。 即將到來的水平會看看生成的內部數據結構來完成這個。 但是現在,想到一個聚集索引排序表。 鑒於連續索引鍵值,SQL Server可以快速訪問這一行; 並且可以通過表的行順序進行。

出於演示的目的,我們創建兩個我們的示例表的副本,SalesOrderDetail; 一個沒有索引,一個聚集索引。 關於索引的鍵列,我們的設計師做出同樣的選擇AdventureWorks數據庫:SalesOrderID/SalesOrderDetailID。 清單1中的代碼的副本SalesOrderDetail表。 我們可以隨時重新運行這段代碼,我們希望從一個白紙開始。

如果存在(選擇*sys.tables&# 160;在哪裏OBJECT_ID=OBJECT_ID(“dbo.SalesOrderDetail_index”))刪除表dbo.SalesOrderDetail_index;如果存在(選擇*sys.tables&# 160;在哪裏OBJECT_ID=OBJECT_ID(“dbo.SalesOrderDetail_noindex”))刪除表dbo.SalesOrderDetail_noindex;選擇*dbo.SalesOrderDetail_indexSales.SalesOrderDetail;選擇*dbo.SalesOrderDetail_noindexSales.SalesOrderDetail;創建聚集索引IX_SalesOrderDetaildbo.SalesOrderDetail_index(SalesOrderID,SalesOrderDetailID)

清單1:創建SalesOrderDetail表的副本

所以,假設SalesOrderDetail表創建聚集索引前是這樣的:

SalesOrderID SalesOrderDetailID ProductID OrderQty UnitPrice
38.10 69389 102201 864 3
34.99 56658 59519 711 1
59044 70000 956 2 1430.442
44.994 48299 22652 853 4
44.994 50218 31427 854 8
34.99 53713 50716 711 1
744.2727 50299 32777 739 1
2024.994 45321 6303 775 6
2.29 72644 115325 873 1
141.615 48306 22705 824 4
120.00 69134 101554 876 1
469.794 48361 23556 760 3
602.346 53605 50098 888 1
183.9382 48317 22901 722 1
8.99 66430 93291 872 1
65281 90265 889 2 602.346
9.99 52248 43812 871 1
47978 20189 794 2 1308.9375

創建上面所示的聚集索引後,生成的表/集群指數看起來像這樣:

SalesOrderID SalesOrderDetailID ProductID OrderQty UnitPrice
178.58 43668 106 722 3
20.19 43668 107 708 1
356.90 43668 108 733 3
419.46 43668 109 763 3
714.70 43669 110 747 1
5.70 43670 111 710 1
43670 112 709 2 5.70
43670 113 773 2 2039 .99
43670 114 776 1 2024 .99
43671 115 753 1 2146 .96
43671 116 714 2 28.84
874.79 43671 117 756 1
43671 118 768 2 419.46
43671 119 732 2 356.90
43671 120 763 2 419.46
43671 121 755 2 874.79
43671 122 764 2 419.46
28.84 43671 123 716 1
20.19 43671 124 711 1
20.19 43671 125 708 1
5.70 43672 126 709 6
43672 127 776 2 2024 .99
43672 128 774 1 2039 .99
874.79 43673 129 754 1
28.84 43673 130 715 3
183.94 43673 131 729 1

你看上面所示的示例數據,您可能會註意到,每一個SalesOrderDetailID價值是獨一無二的。 不要混淆;SalesOrderDetailID不是表的主鍵。 的結合SalesOrderID/SalesOrderDetailID是表的主鍵; 以及聚集索引的索引鍵。

了解基本的聚集索引

聚集索引鍵可以由你選擇的任何列; 它不需要基於主鍵。 在我們的例子中,最重要的是,最左側列的關鍵是一個外鍵,SalesOrderID價值。 因此,銷售訂單的所有行項目中連續出現SalesOrderDetail表。

記住這些額外的點對SQL Server集群索引:

  • 由於聚集索引的條目表的行,沒有收藏價值在集群索引條目。 當SQL Server已經在一行,它不需要一個信息,告訴它在哪裏找到這一行。
  • 聚集索引總是覆蓋查詢。 自指數和同一個表,表的每一列的索引。
  • 桌子上有一個聚集索引不影響你選擇創建非聚集索引表。

選擇聚集索引鍵列(s)

可以有最多每個表一個聚集索引。 一個表的行可以在只有一個序列。 你需要決定什麽序列,如果有的話,最好為每個表; 如果可能的話,創建聚集索引表變得充滿了之前的數據。 做這個決定時,請記住,測序不僅意味著訂購,這也意味著分組; 在分組由銷售訂單行項目。

這就是為什麽的設計者AdventureWorks數據庫的選擇SalesOrderDetailIDSalesOrderID的序列SalesOrderDetail; 行項目的自然順序。
例如,如果一個用戶請求一個訂單的行項目,他們通常會要求所有訂單的行項目。 看一個典型的銷售訂單的形式告訴我們,訂單的打印副本總是包括所有行項目。 它的本質是銷售訂單業務集群由銷售訂單行項目。 可能會有偶爾的請求從倉庫想看產品而非銷售訂單行項目; 但大多數的請求; 如來自銷售人員或客戶,或程序,打印發票,或查詢,計算每個訂單的總價值; 需要任何銷售訂單的所有行項目。

用戶需求,然而,不確定什麽是最好的聚集索引。 本系列以後的水平將覆蓋的內部索引; 因為某些內部方面的聚集索引列的索引也會影響你的選擇。

如果沒有聚集索引表,該表稱為堆。 每個表都是一堆或一個聚集索引。 所以,盡管我們經常狀態的每個索引分為兩種類型,集群或非聚集; 同樣重要的是要註意,每個表分成兩個類型; 它是一個聚集索引或一堆。 開發人員經常說一個表沒有一個聚集索引,但它是更有意義的說表不是一個聚集索引。

只有一種方法為SQL Server來搜索一個堆在尋找行(不含非聚集索引的使用),這是開始在表中的第一行並通過表進行,直到所有的行已經閱讀。 沒有一個序列,沒有搜索鍵,沒有辦法快速導航到特定的行。

比較一個聚集索引和一堆

聚集索引的性能評價和一堆,清單1的兩個副本SalesOrderDetail表。 一份是堆版,另一方面,我們創建在原始表的聚集索引(SalesOrderID,SalesOrderDetailID)。 表都沒有任何非聚集索引。

我們將運行相同的三個查詢每個版本的表; 一個檢索一行,另一個檢索單個訂單,所有行和檢索單個產品的所有行。 我們現在每個執行的SQL和結果表中所示。

我們的第一個查詢檢索一行和執行細節如表1所示。

SQL

SELECT *
SalesOrderDetail
SalesOrderID = 43671
SalesOrderDetailID = 120

(1行受影響)
“SalesOrderDetail_noindex”。 掃描數1,邏輯讀1495

聚集索引

(1行受影響)
“SalesOrderDetail_noindex”。 掃描數1,邏輯讀3

聚集索引的影響

1495年讀3IO減少。

評論

沒有驚喜。 表掃描121317行找到一個不是非常有效。

1:檢索單個行

我們的第二個查詢檢索所有行一個銷售訂單,你可以看到表2的執行細節。

SQL

SELECT *
SalesOrderDetail
SalesOrderID = 43671

(11行受影響)
“SalesOrderDetail_noindex”。 掃描數1,邏輯讀1495

聚集索引

(11行受影響)
“SalesOrderDetail_noindex”。 掃描數1,邏輯讀3

聚集索引的影響

1495年讀3IO減少。

評論

與前面的查詢相同的統計數據。 堆仍然需要表掃描,而分組的聚集索引11細節行所要求的訂單足夠近以便檢索所需的IO 11行是一樣的IO需要檢索一行。 即將到來的水平將詳細解釋為什麽沒有額外的讀取需要檢索額外的10行。

2:檢索單個SalesOrder所有行

和我們的第三個查詢檢索一個產品的所有行,執行結果如表3所示。

SQL

SELECT *
SalesOrderDetail
ProductID = 755

(228行受影響)
“SalesOrderDetail_noindex”。 掃描數1,邏輯讀1495

聚集索引

(228行受影響)
“SalesOrderDetail_index”。 掃描數1,邏輯讀1513

聚集索引的影響

聚集索引版本IO略大; 1513讀與1495讀。

評論

沒有ProductID上非聚集索引列幫助找到一個單一產品的行,兩個版本必須掃描。 因為有一個聚集索引的開銷,略大表的聚集索引版本; 因此掃描需要幾堆比掃描讀取。

3:檢索單個產品的所有行

我們的第一個兩個查詢從聚集索引的存在極大地受益; 第三個是大致相等。 是時候有一個聚集索引是損害? 答案是肯定的,它主要是與插入、更新和刪除行。 像許多其他方面的索引中遇到這些早期的水平,這也是一個主題,將更詳細地介紹一個更高的水平。

一般來說,獲取利益大於維護造成損害的; 聚集索引比一堆。 如果你是在一個Azure數據庫中創建表,你沒有選擇的余地; 每個表必須是一個聚集索引。

結論

聚集索引是一個排序表的序列是由您創建索引時,指定由SQL Server和維護。 該表中的任意行快速訪問給定鍵值。 任何一組行,在索引鍵序列,也很快訪問給定的範圍鍵。

每個表只能有一個聚集索引。 的決定應該是聚集索引的列索引鍵列是最重要的決定,你會讓任何表。

在我們的四級我們將把我們的重點從邏輯到物理,介紹頁面和區段,並檢查指標的物理結構。

聚集索引: 三級階梯SQL Server索引