SQL Server索引進階第三篇:聚集索引

上一篇文章中,我們已經介紹了非聚集索引相關的內容。在本篇中,我們會介紹與聚集索引相關的話題。


首先,我們綜合之前的文章,再來思考一下數據庫接收到請求之後(Select,Update,Delete,Insert),獲取數據的幾種方式:


1.僅僅只訪問非聚集索引而不訪問底層的數據表來獲取數據。這種情況只有當索引中包含了所有要請求的數據的時候才發生。

2.通過在索引中查找請求傳入的關鍵字,找到對應的索引信息之後,然後通過索引定位到數據表中的數據列獲取額外的信息。

3.不管索引,直接去底層的數據表中獲取需要的數據列。


好,我們本篇就關注在第三種獲取數據的方法。正如之前一樣,我們還是使用SQL Server自帶的數據庫AdventureWorks,此時,我們只要關注在SalesOrderDetail表,因爲表中包含了幾十萬條數據,可以讓我們盡情的測試。另外,和之前一樣,還採用了5個與銷售和訂單相關的表,大家可以參考之前的第一篇文章。


聚集索引示例

首先,我們考慮這樣幾個問題:

1.如果我們在一個沒有非聚集索引的表中需找一行數據,所花的成本是多少?

2.在一個無序的表中查找需要的數據的時候是否就意味着對錶中的每一行都進行掃描?

3.SQL Server是否可以把表中的數據進行永久的排序,使得數據的檢索更快,就和我們之前使用關鍵字查找非聚集索引結構一樣(我們之前的非聚集索引的表中的底層額數據是無序的)?


非聚集索引與底層的數據表不是保存在一起的,非聚集索引中包含了數據表中的一部數據,而且還包含了指向底層數據行的指針。聚集索引則不一樣,它是和數據表在一起的。


通過在表上面建立聚集索引之後,SQL Server就會對錶中的數據按照索引鍵進行排序,並且在每次數據修改的時候都對排序進行維護。現在我們沒有必須知道太多的細節,只要知道:建立了聚集索引的表是一個有序表就行了,而且對於每一行數據都存在一個索引鍵,使得SQL Server可以通過這個鍵更快的訪問每一行數據。


說了這麼多,我們還是來看幾個簡單的小例子。我們會創建兩個表,表中的數據來SalesOrderDetail。一個表上面有聚集索引,一個沒有,代碼如下:

  1. IF EXISTS (SELECT * FROM sys.tables

  2. WHERE OBJECT_ID = OBJECT_ID('dbo.SalesOrderDetail_index'))

  3. DROP TABLE dbo.SalesOrderDetail_index

  4. GO



  5. IF EXISTS (SELECT * FROM sys.tables

  6. WHERE OBJECT_ID = OBJECT_ID('dbo.SalesOrderDetail_noindex'))

  7. DROP TABLE dbo.SalesOrderDetail_noindex

  8. GO



  9. SELECT * INTO dbo.SalesOrderDetail_index FROM Sales.SalesOrderDetail

  10. SELECT * INTO dbo.SalesOrderDetail_noindex FROM Sales.SalesOrderDetail

  11. GO



  12. CREATE CLUSTERED INDEX IX_SalesOrderDetail ON

  13. dbo.SalesOrderDetail_index (SalesOrderID, SalesOrderDetailID)

  14. GO
複製代碼



SalesOrderDetail沒有建立聚集索引之前,數據是這樣的:



當我們把SalesOrderDetail導入到了dbo.SalesOrderDetail_index表中,然後建立了索引之後,數據是這樣的:



看完了上面一個小例子,我們就來談一談與聚集索引相關的內容。


聚集索引


我們可以在表中任何的列上面建立聚集索引,並不一定非得要在主鍵上面建立,只不過SQL Server在創建主鍵的時候,默認的就創建了聚集索引。


另外還有一點需要講清楚的就是:因爲聚集索引的每一個條目就是數據表中的數據行,所以通過聚集索引查找數據的時候,沒有必要進行額外的“跳轉”或者說沒有必要進行所謂的書籤查找。


啥都別說了,上個圖,先了解一下吧:


估計看到這個圖之後,很多朋友就越來越糊塗了,因爲存在下面的一個疑惑:

不是說非聚集索引與底層的數據表不是保存在一起的,非聚集索引中包含了數據表中的一部數據,而且還包含了指向底層數據行的指針。聚集索引則不一樣,它是和數據表在一起的

但時從上面圖的來看,聚集索引也是和數據表分開保存的


如果這麼問,說明你在思考了;如果沒有問,說明你已經懂了。

我承認,之前說的有一些不準確,只要是爲了讓大家不要再拘泥於細節,有個總體的把握,現在是時候澄清一下內容了。


我們現在應該這樣說纔算準確:非聚集索引中的數據與底層的數據表不是保存在一起的,因爲各自都維護着一份數據,而且非聚集索引中包含了數據表中的一部數據,而且還包含了指向底層數據行的指針。而且在非聚集索引的結構中,最下面的葉子節點不是底層的數據表的數據頁。

聚集索引則不一樣,因爲整個數據表中的數據頁是它的節點,所以,聚集索引中包含了整個表的數據。

如何選擇合適的列建立聚集索引


到這裏,文章可能就和原本的英文有點不同了,以爲我們團隊認爲這裏應該加入更多的實際的內容,所以做了大膽的改動。

可以說這個話題是個被討論很多,也存在很多爭論的話題,我們這裏主要不會對網絡上面的每個論點都做解釋,而是從分析的角度出發,幫大家理清本質的東西,好讓大家自己決定。


首先解釋一下聚集索引和非聚集索引在進行不同操作時的特點,如下表:

操作名字


聚集索引和非聚集索引比較


讀取


聚集索引:很快,因爲沒有額外的書籤查找操作。

非聚集索引:相對而言,可能會慢一些,尤其適當查找的數據列不包含在索引中的時候。如何包含了,也可能慢一些,因爲可能查找的索引頁要多一些。


更新


聚集索引:如果修改的是非索引列的數據,那是沒問題了,如果修改的列包含在索引列中,波動就很大,因爲會涉及到數據的重新排序,還有表上面的非聚集索引也會受到影響。

非聚集索引:前者類似,後者修改涉及到的波動少一些。


插入、刪除


聚集索引:快些。

因爲如果是非聚集索引,每次插入新數據之後,也需要同時插入數據到非聚集索引中,因爲數據表和索引各自維護一份數據。

刪除數據,道理亦然。



另外,還有一點需要說。如果添加的數據不是添加在數據表中數據頁的最後面,那麼可能這個插入數據的操作就會涉及到分頁操作。此時,分頁的波動對非聚集索引影響小一點。如果數據插入的時候,採用的自動增長列作爲主鍵,而且建立了聚集索引,那麼每次數據的插入都會在末尾,那麼就不存在這個問題了。


最後要提的一點就是索引列大小的問題。在一個建立了聚集索引的表上面在建立非聚集索引,那麼這個非聚集索引會在內部使用聚集索引來定位數據,如果聚集索引所在列很長,那麼勢必會使得非聚集索引所在空間變大,甚至產生更多的原本不需要的頁拆分。


好了,今天就到這裏,我們在後續會介紹更多的具體的案例和實踐。

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