原文連接:http://www.sqlservercentral.com/articles/Stairway+Series/72351/javascript
經過大衛·杜蘭特,2013/01/25(第一次出版:2011/06/22)java
本文是樓梯系列的一部分:SQL Server的階梯索引sql
索引數據庫設計的基礎,告訴開發人員使用數據庫設計者的意圖。 不幸的是索引時每每是後加上的性能問題出現。 終於在這裏是一個簡單的系列文章,應該讓任何數據庫專業迅速「加速」數據庫
前面的水平在這個樓梯概述了非彙集索引的索引通常和特別。 結論用如下關於SQL Server關鍵概念索引。 當一個請求到達您的數據庫,一個SELECT語句或一個INSERT、UPDATE或DELETE語句,SQL Server只有三種可能的方法來訪問數據表中引用的聲明:數據結構
這個水平首先關注的第三選擇上面的列表; 搜索表。 反過來,這將致使咱們的討論集羣索引; 一個被說起的話題,但沒有涵蓋,在2級。數據庫設計
主AdventureWorks咱們將使用的數據庫表是在這個水平SalesOrderDetail表。 在121317行,足以說明一些好處有彙集索引的表。 有兩個外鍵,它是複雜的足以說明一些設計決策,你必須使你的彙集索引。sqlserver
儘管咱們已經討論了在一級sample數據庫,值得重複。 在整個樓梯,咱們將使用示例來講明概念。 這些例子是基於微軟的AdventureWorks示例數據庫。 咱們專一於銷售訂單。 五表將給咱們一個良好的事務性和非事務性數據;客戶,銷售人員,產品,SalesOrderHeader,SalesOrderDetail。 爲了保持專一,咱們使用列的一個子集。 由於AdventureWorks規範化,銷售人員信息分解成三個表:銷售人員,員工和聯繫。性能
整個樓梯咱們使用如下兩個術語,指一行訂單互換:「行項目」和「訂單細節」。 前者是更常見的業務術語; 後者的名字出如今一個AdventureWorks表。spa
完整的一套表,和它們之間的關係,如圖1所示。設計
圖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_index從Sales.SalesOrderDetail;選擇*成dbo.SalesOrderDetail_noindex從Sales.SalesOrderDetail;去建立彙集索引IX_SalesOrderDetail在dbo.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集羣索引:
能夠有最多每一個表一個彙集索引。 一個表的行能夠在只有一個序列。 你須要決定什麼序列,若是有的話,最好爲每一個表; 若是可能的話,建立彙集索引表變得充滿了以前的數據。 作這個決定時,請記住,測序不只意味着訂購,這也意味着分組; 在分組由銷售訂單行項目。
這就是爲何的設計者AdventureWorks數據庫的選擇SalesOrderDetailID在SalesOrderID的序列SalesOrderDetail表; 行項目的天然順序。
例如,若是一個用戶請求一個訂單的行項目,他們一般會要求全部訂單的行項目。 看一個典型的銷售訂單的形式告訴咱們,訂單的打印副本老是包括全部行項目。 它的本質是銷售訂單業務集羣由銷售訂單行項目。 可能會有偶爾的請求從倉庫想看產品而非銷售訂單行項目; 但大多數的請求; 如來自銷售人員或客戶,或程序,打印發票,或查詢,計算每一個訂單的總價值; 須要任何銷售訂單的全部行項目。
用戶需求,然而,不肯定什麼是最好的彙集索引。 本系列之後的水平將覆蓋的內部索引; 由於某些內部方面的彙集索引列的索引也會影響你的選擇。
若是沒有彙集索引表,該表稱爲堆。 每一個表都是一堆或一個彙集索引。 因此,儘管咱們常常狀態的每一個索引分爲兩種類型,集羣或非彙集; 一樣重要的是要注意,每一個表分紅兩個類型; 它是一個彙集索引或一堆。 開發人員常常說一個表「有」或「沒有」一個彙集索引,但它是更有意義的說表」是」或「不是」一個彙集索引。
只有一種方法爲SQL Server來搜索一個堆在尋找行(不含非彙集索引的使用),這是開始在表中的第一行並經過表進行,直到全部的行已經閱讀。 沒有一個序列,沒有搜索鍵,沒有辦法快速導航到特定的行。
彙集索引的性能評價和一堆,清單1的兩個副本SalesOrderDetail表。 一份是堆版,另外一方面,咱們建立在原始表的彙集索引(SalesOrderID,SalesOrderDetailID)。 表都沒有任何非彙集索引。
咱們將運行相同的三個查詢每一個版本的表; 一個檢索一行,另外一個檢索單個訂單,全部行和檢索單個產品的全部行。 咱們如今每一個執行的SQL和結果表中所示。
咱們的第一個查詢檢索一行和執行細節如表1所示。
SQL |
SELECT * |
堆 |
(1行受影響) |
彙集索引 |
(1行受影響) |
彙集索引的影響 |
從1495年讀3讀IO減小。 |
評論 |
沒有驚喜。 表掃描121317行找到一個不是很是有效。 |
表1:檢索單個行
咱們的第二個查詢檢索全部行一個銷售訂單,你能夠看到表2的執行細節。
SQL |
SELECT * |
堆 |
(11行受影響) |
彙集索引 |
(11行受影響) |
彙集索引的影響 |
從1495年讀3讀IO減小。 |
評論 |
與前面的查詢相同的統計數據。 堆仍然須要表掃描,而分組的彙集索引11細節行所要求的訂單足夠近以便檢索所需的IO 11行是同樣的IO須要檢索一行。 即將到來的水平將詳細解釋爲何沒有額外的讀取須要檢索額外的10行。 |
表2:檢索單個SalesOrder全部行
和咱們的第三個查詢檢索一個產品的全部行,執行結果如表3所示。
SQL |
SELECT * |
堆 |
(228行受影響) |
彙集索引 |
(228行受影響) |
彙集索引的影響 |
彙集索引版本IO略大; 1513讀與1495讀。 |
評論 |
沒有ProductID上非彙集索引列幫助找到一個單一產品的行,兩個版本必須掃描。 由於有一個彙集索引的開銷,略大表的彙集索引版本; 所以掃描須要幾堆比掃描讀取。 |
表3:檢索單個產品的全部行
咱們的第一個兩個查詢從彙集索引的存在極大地受益; 第三個是大體相等。 是時候有一個彙集索引是損害? 答案是確定的,它主要是與插入、更新和刪除行。 像許多其餘方面的索引中遇到這些早期的水平,這也是一個主題,將更詳細地介紹一個更高的水平。
通常來講,獲取利益大於維護形成損害的; 彙集索引比一堆。 若是你是在一個Azure數據庫中建立表,你沒有選擇的餘地; 每一個表必須是一個彙集索引。
彙集索引是一個排序表的序列是由您建立索引時,指定由SQL Server和維護。 該表中的任意行快速訪問給定鍵值。 任何一組行,在索引鍵序列,也很快訪問給定的範圍鍵。
每一個表只能有一個彙集索引。 的決定應該是彙集索引的列索引鍵列是最重要的決定,你會讓任何表。
在咱們的四級咱們將把咱們的重點從邏輯到物理,介紹頁面和區段,並檢查指標的物理結構。