原文地址:
Stairway to SQL Server Indexes: Level 1, Introduction to Indexes
本文是SQL Server索引進階系列Stairway to SQL Server Indexes)的一部分。
索引是資料庫設計的基礎,向開發人員顯示了使用資料庫大量資料庫設計者的意圖。不幸的是,索引大部分時候是在出現效能問題的時候,才被事後添加上的。
第一級介紹一下SQL Server的索引:是一種資料庫物件,使得SQL Server可以在最短的時間內查詢 or 修改請求的資料,使用最少的系統資源帶來最大的效能提升。好的索引將會允許SQL Server實現最大化的並發,一個使用者的查詢對於其他使用者的查詢幾乎沒有影響。最終,索引給資料庫完整性提供一種高效的方式,當唯一索引建立的時候,確保唯一的索引值。這一級只是一個簡介,包括一些理論和使用,一些物理的細節留在後面的層級中介紹。
對於資料庫開發人員來說,徹底的理解索引是重要的,當一個請求從用戶端到達SQL Server,SQL Server只有兩種可能的路徑來訪問請求的資料行:
第一種方法對於SQL Server來說總是可用的,第二種方法只在你給資料庫設計了可用的索引的情況下才是可行的,但是第二種可以帶來極大地效能提升,接下來我們將繼續介紹。
因為索引需要維護它們會佔用物理空間,並且它們一定會和表保持同步),所以它們不是SQL Server必須的。有的資料庫會完全沒有索引。它們可能會導致效能下降,有可能帶來資料完整性問題,但是SQL Server允許它的存在。
但是,這些不好的不是我們想要的。我們都希望資料庫效能優良,資料完整,同時,保持索引維護的最小化。本層級將會朝著這個目標引導大家。
資料庫執行個體
貫穿整個進階系列,我們都會使用執行個體來闡述關鍵的理念。這些例子使用的是微軟的 AdventureWorks 樣本資料庫。我們主要使用銷售訂單部門。包含5張表:Customer, SalesPerson, Product, SalesOrderHeader, SalesOrderDetail。為了保持注意力的集中,我們使用部分的列。
AdventureWorks 設計的很規範,銷售人的資訊在三張表中都有:SalesPerson,Employee,Contact。在某些情況下,我們會把他們看成是一張表。是這些表之間的關係。
650) this.width=650;" border="0" alt="" src="http://www.bkjia.com/uploads/allimg/131229/19304M925-0.jpg" />
本層級用到的TSQL在後面都會給出。
什麼是索引
通過一個小故事開始我們的索引學習之旅,一個很老,但是證明很技術,貫穿整篇本章,介紹索引的基本概念。
你離開家去處理一些事情。當你回來的時候,從等待你回來的女兒的壘球教練口中得到一些訊息。三個女孩子:Tracy, Rebecca, and Amy弄丟了他們的帽子。你能否給他們買帽子,下一場比賽他們的父母將會還給你。
你認識那些女孩子,也認識他們的父母。但是你不知道他們帽子的尺寸。在你家的鎮子上有三家人,每家都有一些你需要的資訊。沒有問題,你會打電話給他們,得到帽子的尺寸。你一手拿著電話,一邊開啟電話本的目錄索引。
你需要聯絡的第一家是Hellen Meyer,你估計Meyer應該在姓名的中部,你直接跳到電話本的中間部分,但是你發現來到了頁頭部寫著“Kline-Koerber”的一頁,向前翻了幾頁,又發現了“Nagle-Nyeong”,又向前翻了幾頁,發現了“Maldonado-Nagle”。意識到你快要找到了,向後尋找,你找到了“Meyer,Helen”一行,找到的對應的號碼。打通了Meyer的家,得到了你想要的資訊。
重複上面的過程,找到了另外兩個家庭,擷取了另外兩個帽子的尺寸。
你使用了索引,你使用索引的方式和SQL Server使用索引的方式類似。他們有很多的相似,有一些不同,電話本和SQL Server的索引。
事實上,你剛才使用的就是SQL Server兩種索引聚集和非聚集)中的一種,非叢集索引。本層級我們介紹非叢集索引,下一層級介紹叢集索引,以及深入的分析這兩種索引。
非叢集索引
白紙類似非叢集索引,他們不是以資料本身來組織的,只是一個映射,協助你訪問資料。資料本身才是我們真正需要的。電話公司沒有將鎮子的居民整理到一個有意義的隊列中,將房子從一個地方移動到另一個地方,方便同一個壘球隊的女孩們的家是一個挨一個的,而不是像現在這樣用居民的姓氏來組織。相反,他給你一本書,包含每個居民。每行都包含一個鍵,使得你可以訪問到居民的電話。
就像白紙上的電話一樣,SQL Server的非叢集索引都包含兩部分的內容:
查詢鍵,例如:姓氏-名稱-中間部分這樣的格式,在SQL Server的詞彙中,叫做索引鍵。
標籤,提供相同的內容,那就是電話號碼,SQL Server中則直接指向鍵代表的資料行。
另外,一個SQL Server非叢集索引還會包含一些內部使用的頭部資訊,可能會包含一些可選的資訊。這些內容在後面的層級中會有介紹,現在還不是理解非叢集索引的重點內容。
就像電話本一樣,SQL Server的索引維護一個查詢鍵,經過幾次小的跳轉就會找到想要訪問的入口。給出一個查詢鍵,SQL Server可以快速的定位入口。不像電話本,SQL Server的索引是動態。那就是說,每次增加一行,刪除一樣,或者是包含查詢鍵的一列被修改,SQL Server都會更新索引。
就像在電話本上兩個挨著的家庭,在地理位置上不是挨著的一樣,在非叢集索引中挨著的兩個入口,也不是表中挨著的兩行資料。第一個入口可能是表中的最後一行,第二個入口可能是表中的第一行。事實上,不想索引,入口通常是有意義的序列。表中的行是完全無序的。
當我們建立一個索引,SQL Server會產生並且在額外的表中精確的維護每一行的入口。在一張表可以建立多於一個的非叢集索引。
最大的不同是:SQL Server不能使用電話。他只會使用標籤中的資訊,才可以導航到對應的表中的行。
建立並且從非叢集索引中受益
我們通過兩次樣本資料庫的查詢來結束這個層級,確保你使用的樣本資料庫是SQL Server 2005的AdventureWorks 資料庫,也可以是SQL Server 2008。每次我們會執行相同的查詢,但是第一次是在建立索引之前,第二次是在建立索引之後。每一次,SQL Server都會告訴我們擷取資料需要做多少工作。我們將在Contact表中查詢Helen Meyer行大概在表的中間部分)。初始時候,在FirstName或者是LastName列沒有索引,為了確保執行正確,可以通過下面的代碼來刪除索引。
- IF EXISTS (SELECT * FROM sys.indexes
- WHERE OBJECT_ID = OBJECT_ID('Person.Contact')
- AND name = 'FullName')
- DROP INDEX Person.Contact.FullName;
開啟IO和時間統計
- SET STATISTICS io ON
- SET STATISTICS time ON
- GO
執行查詢
- SELECT *
- FROM Person.Contact
- WHERE FirstName = 'Helen'
- AND LastName = 'Meyer';
- GO
我們會看到執行結果,也就是Helen的資訊。
在訊息tab中我們會看到
- 表 'Contact'。掃描計數 1,邏輯讀取 561 次,物理讀取 0 次,預讀 0 次,lob 邏輯讀取 0 次,lob 物理讀取 0 次,lob 預讀 0 次。
-
- SQL Server 執行時間:
- CPU 時間 = 0 毫秒,佔用時間 = 9 毫秒。
資訊中顯示本次查詢產生邏輯IO 561次,處理器佔用9毫秒的時間。你顯示的內容會和處理器的不同而不同。
建立非叢集索引
- CREATE NONCLUSTERED INDEX FullName
- ON Person.Contact
- ( LastName, FirstName );
- GO
再次執行查詢
- SELECT *
- FROM Person.Contact
- WHERE FirstName = 'Helen'
- AND LastName = 'Meyer';
- GO
這次看到的資訊變成了
- 表 'Contact'。掃描計數 1,邏輯讀取 4 次,物理讀取 0 次,預讀 0 次,lob 邏輯讀取 0 次,lob 物理讀取 0 次,lob 預讀 0 次。
-
- SQL Server 執行時間:
- CPU 時間 = 0 毫秒,佔用時間 = 1 毫秒。
建立索引之後只需要4次邏輯IO,更少的處理器佔用時間。
結論
建立合適的索引可以極大的提升資料庫效能。在下一層級,我們會瞭解索引的物理結構。我們將會知道非叢集索引為什麼會對查詢有好處。還會介紹其他類型的索引,索引的其他好處,索引相關的代價,監控和維護索引,和一些最佳實務。目標就是告訴你建立索引所必須的知識。
代碼下載
- Level 1 - IntroToIndexes_Durant_Code.sql
- Level 1 - MillionRowContactTable.sql