SQL Server非聚集索引概述
此文章主要向大家描述的是SQL Server非聚集索引(Noclustered Index Indications),在實際操作中SQL Server 2000數據庫可以允許你在一個表上最大程度的創建249個非聚集索引。直到表變得非常巨大,一個非聚集索引實際所占用的空間與日益增長的訪問性能相比是微不足道的。
然而,時刻牢記:隨著你在系統添加更多索引,數據修改語句由于索引性能的負擔會變得更慢。
當定義SQL Server非聚集索引時,你也想在選擇性高的列上定義索引(也就是,具有低密度值的列)這樣它們能被優化器來使用。一個非聚集索引中的大量重復值經常使得使用非聚集索引比表掃描代價更高(按照I/O)。讓我們一起來看一個假設的例子:
- Sql代碼
- Select title from titles
- Where price between $5 and $10
- Select title from titles
- Where price between $5 and $10
假設你在范圍內有1,000,000行;這些1,000,000行隨機分散在整個表中。盡管索引葉級擁有全部的有序索引行,但在最壞情況下,一次讀一個數據行也將要求一個書簽查找。
這樣,在最壞情況下使用SQL Server非聚集索引來進行范圍檢索的I/O估計如下:
引用
非聚集索引的層數
+用于發現所有匹配行的掃描的索引頁數
+ 匹配的行數 × 每個書簽查找的頁數
假如你的表上沒有聚集索引,那書簽僅僅是一個包括頁和行的指針,當發現匹配的數據行時需要讀取一個數據頁。假如范圍內有1,000,000行,當該表沒有聚集索引時,借助非聚集索引的最壞情況的估計是:
引用
查找所有書簽需要讀取的索引頁數
+1,000,000匹配行 × 1數據頁的讀取
= 1,000,000 +I/Os
如果表中有聚集索引,書簽就是一個代表數據行的聚集索引鍵,用書簽來查找匹配的行要求搜索聚集索引樹來定位數據行。假設聚集索引有兩級非葉子節點,它將需要讀取三頁來在數據頁上查找每個滿足條件的行。如果范圍內有1,000,000行,那么借助聚集索引的SQL Server非聚集索引來查找數據,在最壞情況下它的代價估計如下:
引用
查找所有書簽所讀取的索引頁的個數
+1,000,000匹配的行 * 每個書簽查找需求的3頁
=3,000,00+I/Os
把每種情況與表掃描相對比。如果整個表占用了50,000頁,那么一個全表掃描將只花費50,000 I/O。所以,在這個例子中,一個表掃描實際將比用非聚集索引更有效。
下面的指南幫助你識別非聚集索引的潛在的候選者。
SARG或join子句中引用的相對來說具有較高的選擇性(密度值低)的列。
Where子句和order by子句都引用的列。
當使用非聚集索引來檢索數據行時,它們按照非聚集索引鍵的順序被檢索出來。如果結果集也需要按照SQL Server非聚集索引進行排序,SQL Server能避免對結果集重新排序,這樣可實現一個更有效的查詢。下面就是這樣一個例子:
- Sql代碼
- Select * from authors
- Where state like "c%"
- Order by state
- Select * from authors
- Where state like "c%"
- Order by state
一般情況下,非聚集索引對單行查找(single-row lookup),連接(join),有高選擇性的列的查詢,小范圍檢索的查詢有用。當你考慮非聚集索引的設計時也不要忽略了覆蓋索引的優點,下節將會講到。
索引覆蓋(Index Covering)
索引覆蓋是這樣一種情況,查詢中的select 和where子句中所需要的信息都能在非聚集索引中找到。因為非聚集索引包含了一個對應于表中每個數據行的一個葉子行,SQL Server能從非聚集索引的葉子行來滿足查詢。這導致了數據檢索的更快,因為所有的信息能從索引頁中直接獲得,并且避免了SQL Server查找數據頁。
因為非聚集索引的葉子頁都連接在一起,索引的葉級可以像表中的數據頁一樣進行掃描,因為頁級行都典型比數據行要小,一個覆蓋了查詢的非聚集索引將比同樣列的聚集索引更快,因為需要讀取的頁數要更少。
在下面的例子中,quthors表中的關于au_lname 和au_fname的SQL Server非聚集索引將覆蓋查詢,因為結果中的列和SARG都能從索引中提取出來:
- Sql代碼
- Select au_lname, au_fname
- From authors
- Where au_lname like "M%"
- GO
- Select au_lname, au_fname
- From authors
- Where au_lname like "M%"
- GO
其他使用聚合函數(MIN AVG SUM COUNT)的查詢或者僅僅檢查是否存在的查詢也能從索引覆蓋中獲益。下面是一些能夠利用索引覆蓋優點的查詢:
- Sql代碼
- Select count (au_lname) from authors where au_lname like 'm%'
- Select count (*) from authors where au_lname like 'm%'
- Select count (*) from authors
- Select count (au_lname) from authors where au_lname like 'm%'
- Select count (*) from authors where au_lname like 'm%'
- Select count (*) from authors
你可能會奇怪最后一個查詢,它甚至沒有一個具體的SARG,怎么還能使用索引。SQL Server知道非聚集索引的特性,一個非聚集索引為表中的每行數據都包含了一行;它能夠簡單的計算任何一個非聚集索引的行數,而不需要掃描整個表。對最后一個查詢,SQL Server選擇最小的SQL Server非聚集索引——也就是,具有最少的葉子頁的索引。
向非聚集索引添加列使得發生索引覆蓋是一種提高查詢響應時間的常見方法。考慮下面的查詢:
- Sql代碼
- Select royalty from titles
- Where price between $10 and $ 20
- Select royalty from titles
- Where price between $10 and $ 20
如果你僅在price列上創建索引,SQL Server能發現滿足price在該范圍的索引中的行,但是它還需要訪問數據行來檢索royalty。范圍中有100行,最壞情況下檢索數據所花費的IO代價計算如下:
引用
索引的級數
+查找匹配行的索引頁的數
+100 * 每個書簽查找頁數
如果royalty列添加到了price列索引中了,索引能被掃描來檢索結果,而不是進行書簽查找,這樣具有更快的查詢響應。使用索引覆蓋的IO代價將只是:
引用
索引級數
+查找匹配行的索引頁的數
引用
注意:
當考慮添加索引來利用索引覆蓋時,小心使得索引變得太寬。當索引行的寬度接近與數據行寬度時,覆蓋的優點將失去,因為增加了葉級頁的數目。當索引的葉級頁的數目接近了表中頁的數目,索引級數也增加了,那么索引掃描的時間就開始接近于表掃描時間了。
另外,如果你添加對到索引中的列頻繁修改,數據行中列的任何修改也會波及到索引中。這增加了維護的負擔,也會影響修改的性能。
正如第33章討論的那樣,當在一個表上創建了 一個聚集索引,聚集鍵會被所有的SQL Server非聚集索引引用,作為書簽來定位實際的數據行。聚集鍵實際就是一些列,它們構成了聚集索引和它們的數據值。這種特性有時也能導致索引覆蓋。
例如,假設suthors表在au_lname au_fname列上建立聚集索引,并有一個定義在au_id的非聚集索引。非聚集索引的每行都包含了與數據行對應的au_lname au_fname聚集鍵值。因為這個原因,下面查詢將被非聚集索引覆蓋:
- Sql代碼
- select au_lname, au_fname
- from authors
- where au_id like '123%'
- select au_lname, au_fname
- from authors
- where au_id like '123%'
以上的相關內容就是對SQL Server非聚集索引(Noclustered Index Indications)的介紹,望你能有所收獲。
【編輯推薦】