SQL Server索引進階第一篇:索引介紹

索引設計是數據庫設計中比較重要的一個環節,對數據庫的性能其中至關重要的作用,但是索引的設計卻又不是那麼容易的事情,性能也不是那麼輕易就獲取到的,很多的技術人員因爲不恰當的創建索引,最後使得其效果適得其反,可以說“成也索引,敗也索引”。

言歸正傳,其實索引就是數據庫中的對象,這一點和數據庫中的其他對象一樣(如表,視圖等),索引的作用就是使得SQL Server在尋找或者修改數據的時候所花的時間最少,所使用的系統資源更少,從而提升性能。


同時,好的索引也可以使得SQL Server儘可能最大的支持併發,從而使得每一個用戶的查詢對其他人得影響最小。最後,索引也提供了一個非常高效的方式來強制要求數據的完整,這只要是通過在創建索引的時候指定索引中數據列的唯一性來達到的。


我們本篇的文章是整個系列的第一篇,只要是介紹索引的概念和用途,更多的內容將在後續講述。


首先來看看SQL Server是如何對用戶請求的數據進行檢索的,基本上有兩種方式:

1.從第一行開始,依次掃描表中的每一行數據,然後檢查這一行是不是用戶需要的數據,直到最後一行。

2.使用索引,快速定位到所請求的數據上面。


對於上面說的第一種方式,這是SQL Server默認的方式,而第二種方式只有當我們在SQL Server上面建立了合適的索引之後才使用,並且會帶來很大的性能提升。

因爲索引的建立和使用是有一定的開銷的(它們會佔用物理磁盤空間,並且在於數據表同步的時候會消耗大量的CPU和內存,因爲涉及到重組,重建等操作),所以,在SQL Server中是允許數據庫沒有索引的。當然了,沒有索引可能會導致查詢過慢等性能問題,以及相關數據完整性問題,但是SQL Server沒有強制每個數據庫都必須建立索引。


示例數據庫準備


爲了使得後續的講解更加的具體,我們這裏將會採用SQL Server的示例數據庫AdventureWorks進行一些演示。在本篇中,我們主要關注一些與銷售和訂單相關的數據表,涉及到幾個5個表:Customer, SalesPerson, Product, SalesOrderHeader, and SalesOrderDetail。表之間的關係如下:



爲了使得大家的理解更加清楚,我們這裏從一個小故事開始。

從一個故事開始


有一天,你收到了你女兒壘球教練的一個信息:Tracy, Rebecca和Amy的團隊帽子沒有丟了,問你是否可以代勞幫她們去商店買三定新的帽子,然後快遞給她們。


看到信息之後,你就開始行動了。當然,對於這三個女孩子的父母,你都是認識的,彼此都比較熟悉。但是,你卻不知道每個女孩子帽子的尺寸。此時,你很自然的就想到了打電話去問三個孩子的父母,於是你便開始翻你家裏的那本記載了全鎮人的電話號碼的電話薄了。


首先你要找的第一個人就是Helen Meyer。看到“Meyer”之後,按照字母順序,你很快就跳轉到包含“M”的那幾頁,然後再那幾頁中找到了這家人的號碼。採用同樣的方式,你很快的找到了其他兩家人得號碼。然後一一的打電話過去得到了你想要的信息。


好,到這裏,例子就完了,很簡單,很原始的一個電話號碼查找的例子。在尋找號碼的過程中,就採用了索引,這和SQL Server中索引使用的方式是類似的。


在SQL Server索引可以分爲兩種類型:聚集和非聚集。剛剛在我們在例子中查詢電話號碼使用的索引其實就類似於一個非聚集索引。所以,我們下面就先介紹非聚集索引。


非聚集索引


爲什麼說我們剛剛在查詢電話號碼的時候,使用是非聚集索引呢?

這可能和大家看到的網上的一些介紹有點不同:因爲網上很多的文章在介紹聚集索引和非聚集索引的時候總是那書來做例子:聚集索引就類似書中的頁面,1,2,3…,而非聚集索引就是書後面的那個索引表,是按照A-Z排序一些關鍵字。


其實如果見過電話薄(這個電話薄是電話公司發的)的朋友應該知道:電話薄都是按照姓氏來排序的,其實也就是A-Z進行排的,而與電話號碼本身沒有任何的關係,沒有按照電話號碼來排,更加沒有按照用戶的住址來排(例如,按照用戶的門牌號)。所以,當我們尋找某個用戶的信息的時候,我們按照他們的姓名進行檢索,找到對應的電話號碼,然後打電話過去,詢問更多的信息,也就說,如果要獲取用戶的全部信息,要進行兩次的動作“找號碼-打電話“,從這點理解,就和數據庫中的非聚集索引類似,並且電話薄中僅僅只是包含了電話號碼和用戶的名字而已,沒有包含其他的更多的信息了,因爲如果是聚集索引話,一次跳轉就可搞定了。


我們再來說說SQL Server索引。其實索引的查找就是一個“跳着查找“。當我們給數據庫一個要檢索的值得之後,數據庫就會跳到包含這個值得索引中,然後從索引中獲取相關的信息,如果此時任然沒有找到需要的數據,那麼就繼續跳到包含完整信息的地方(數據頁)。



與電話薄不同的是,數據庫中的索引是動態的。每次只要表中有數據更新,添加或者刪除的時候,索引就會更新。

正如之前所說的一樣:電話薄中的電話號碼的順序不是這個小鎮上面用戶地理住址順序。在數據庫中,非聚集索引中的每個項的順序也不是底層的數據表中數據物理順序。那麼就很有可能,在索引中的第一個項所代表的數據在數據表中是最後一條,也有可能,索引中最後一個項代表的數據在表中是第一個。另外,索引中項都是按照一定的順序排序的,可能底層的數據卻是完全無序的。



當創建一個索引之後,SQL Server獲取爲底層的數據表中的每一行都在索引中維護一個與之對應的項,簡而言之就是:底層數據表中的每一行,在索引中都有一個引用指向它。我們可以再一個表上面建立多個非聚集索引,但是索引中的數據只能是這個表的,不能使其他的,否則就沒有意義了。


創建和使用非聚集索引


下面,我們就要初窺一下非聚集索引,練一下手。其實本篇主要是介紹,所以內容簡單,簡單不代表沒有意義,可能講的東西和理解的方式不一樣。

我們使用AdventureWorks數據庫中的Person.Contact表爲例子。手下運行下面的查詢,刪除已經存在的索引:

  1. IF EXISTS (SELECT * FROM sys.indexes
  2. WHERE OBJECT_ID = OBJECT_ID('Person.Contact')AND name = 'FullName')
  3. DROP INDEX Person.Contact.FullName; 
複製代碼

然後運行下面的批命令:

  1. SET STATISTICS io ON
  2. SET STATISTICS time ON
  3. GO

  4. SELECT * FROM Person.Contact 
  5. WHERE FirstName = 'Helen' AND LastName = 'Meyer'
  6. GO

  7. SET STATISTICS io off
  8. SET STATISTICS time off
複製代碼


查詢結果如下:



在這裏我們採用了下面兩個語句來獲取這個查詢運行所消耗的I/O和時間:

  1. SET STATISTICS io ON
  2. SET STATISTICS time ON
複製代碼

信息如下:



這個查詢執行了563個邏輯IO,消耗了3毫秒的CPU。

作爲對比,我們在運行下面的語句:

  1. CREATE NONCLUSTERED INDEX FullName 

  2. ON Person.Contact ( LastName, FirstName )
複製代碼

我們再次運行上面的查詢,得到的信息如下:



此時,IO讀取只有4個了。

當然,例子很簡單,作爲簡單的演示倒是可以,當時在實際的項目中就沒有這麼容易了。

不管如何,起碼可以知道:索引確確實實是數據庫中提升性能的重要手段,我們下一篇將會帶領大家看看索引的物理結構。


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