彙集索引
聚簇索引指示表中數據的物理順序,該表根據聚簇進行排序
索引鍵。該表只能定義一個聚簇索引。
假設您要在堆表上使用數據建立聚簇索引。做爲第一步,
如圖2-5所示,SQL Server建立了另外一個數據副本,而後根據該數據進行排序
羣集密鑰的值。數據頁連接在每一個頁面包含的雙鏈表中
指向鏈中下一頁和上一頁的指針。此列表稱爲索引的葉級別
包含實際的表數據。
第2章■表格和索引:內部結構和訪問方法數據庫
■注意頁面上的排序順序由插槽陣列控制。頁面上的實際數據未排序。
當葉級別由多個頁面組成時,SQL Server開始構建一箇中間級別
index,如圖2-6所示。
圖2-5。彙集的索引結構:葉級數組
圖2-6。彙集併發
的索引結構:中級和葉級
中間級別爲每一個葉級頁面存儲一行。它存儲了兩條信息:
物理地址和它引用的頁面中索引鍵的最小值。惟一的例外是
第一頁上的第一行,其中SQL Server存儲NULL而不是最小索引鍵值。
經過這種優化,SQL Server在插入行時不須要更新非葉級行
具備表中最低的鍵值。
中間級別的頁面也連接到雙鏈表。 SQL Server增長了更多
和更多中間級別,直到有一個只包含單個頁面的級別。這個級別叫作
根級別,它成爲索引的入口點,如圖2-7所示。
第2章■表格和索引:內部結構和訪問方法框架
如您所見,索引始終具備一個葉級別,一個根級別和零個或多箇中間級別。
惟一的例外是索引數據適合單個頁面。在這種狀況下,SQL Server不會建立
單獨的根級頁面,索引只包含單個葉級頁面。
索引中的級別數很大程度上取決於行和索引鍵的大小。例如,
4字節整數列上的索引在中間和根級別上每行須要13個字節。那些
13個字節包含一個2字節的插槽數組條目,一個4字節的索引鍵值,一個6字節的頁面指針和一個1字節的行
開銷,這是足夠的,由於索引鍵不包含可變長度和NULL列。
所以,每行可容納8,060字節/ 13字節=每頁620行。這意味着,
使用一箇中間級別,您能夠存儲最多620 * 620 = 384,400個葉級頁面的信息。
若是數據行大小爲200字節,則每一個葉級頁面最多可存儲40行,最多可存儲15,376,000行
只有三個級別的指數。向索引添加另外一箇中間級別基本上涵蓋全部
可能的整數值。函數
■注意在現實生活中,索引碎片會減小這些數字。咱們將討論索引碎片
在第6章。
SQL Server能夠經過三種不一樣的方式從索引中讀取數據。第一個是
經過有序掃描。假設咱們想要運行SELECT Name FROM dbo.Customers ORDER BY
CustomerId查詢。索引的葉級別上的數據已根據CustomerId列進行排序
值。所以,SQL Server能夠從第一頁到最後一頁掃描索引的葉級並返回
行按存儲順序排列。
SQL Server從索引的根頁開始,從那裏讀取第一行。該行引用
具備表中最小鍵值的中間頁面。 SQL Server讀取該頁面並重復
該過程直到找到葉級別的第一頁。而後,SQL Server開始逐個讀取行,
移動頁面的連接列表,直到讀取全部行。圖2-8說明了這個過程。
圖2-7。聚簇索引結構:根級別
第2章■表格和索引:內部結構和訪問方法
前一個查詢的執行計劃顯示帶有Ordered的Clustered Index Scan操做符
property設置爲true,如圖2-9所示。性能
圖2-8。有序索引掃描測試
圖2-9。有序索引掃描執行計劃
值得一提的是,觸發有序掃描不須要order by子句。
有序掃描只意味着SQL Server根據索引鍵的順序讀取數據。
SQL Server能夠向前和向後兩個方向導航索引。可是,有
您必須牢記的一個重要方面:SQL Server在向後期間不使用並行性
索引掃描。
第2章■表格和索引:內部結構和訪問方法
■提示您能夠經過檢查INDEX SCAN或INDEX SEEK運算符屬性來檢查掃描方向
執行計劃。但請記住,Management Studio不會在中顯示這些屬性
執行計劃的圖形表示。您須要打開「屬性」窗口以經過選擇它來查看它
操做員執行計劃並選擇「查看/屬性窗口」菜單項或按F4鍵。
SQL Server企業版有一個稱爲旋轉木馬掃描的優化功能
容許多個任務共享相同的索引掃描。咱們假設您有掃描的會話S1
指數。在掃描過程當中的某個時刻,另外一個會話S2運行須要掃描的查詢
相同的指數。經過旋轉木馬掃描,S2將S1鏈接到當前掃描位置。 SQL Server讀取每一個頁面
只有一次,將行傳遞給兩個會話。
當S1掃描到達索引的末尾時,S2開始從索引的開頭掃描數據
直到S2掃描開始的點。旋轉木馬掃描是您不能依賴的另外一個例子
按索引鍵的順序以及爲何在重要時應始終指定ORDER BY子句。
有序掃描以後的下一個訪問方法稱爲分配順序掃描。 SQL Server訪問
表數據經過IAM頁面,相似於堆表的方式。 SELECT名稱FROM
dbo.Customers WITH(NOLOCK)查詢和圖2-10說明了這種方法。查詢如圖2-11所示
執行計劃。優化
圖2-10。分配訂單掃描spa
第2章■表格和索引:內部結構和訪問方法
3d
圖2-11。分配訂單掃描執行計劃
圖2-12。索引尋求
不幸的是,當SQL Server使用分配順序掃描時,檢測起來並不容易。即使如此
執行計劃中的有序屬性顯示爲false,表示SQL Server不關心是否
按索引鍵的順序讀取行,而不是使用分配順序掃描。
儘管掃描大型表的啓動成本較高,但分配順序掃描能夠更快地掃描大型表。
當表很小時,SQL Server不使用此訪問方法。另外一個重要的考慮是
數據一致性。 SQL Server不使用具備聚簇索引的表中的轉發指針,以及
分配順序掃描會產生不一致的結果。因爲能夠屢次跳過或讀取行
頁面拆分致使的數據移動。所以,SQL Server一般會避免使用分配順序掃描
除非它讀取READ UNCOMMITTED或SERIALIZABLE事務隔離級別中的數據。
■注意咱們將在第6章「索引碎片」中討論頁面拆分和碎片,並進行討論
第三部分「鎖定,阻塞和併發」中的鎖定和數據一致性。
最後一個索引訪問方法稱爲索引查找。 SELECT名稱來自dbo.Customers WHERE
CustomerId BETWEEN 4和7查詢和圖2-12說明了操做。
第2章■表格和索引:內部結構和訪問方法
爲了從表中讀取行的範圍,SQL Server須要找到最小的行
從範圍中鍵的值,即4. SQL Server以根頁開始,其中第二行
引用最小鍵值爲350的頁面。它大於咱們要查找的鍵值
(4),SQL Server讀取根頁上第一行引用的中級數據頁(1:170)。
一樣,中間頁面將SQL Server引導到第一個葉級頁面(1:176)。 SQL Server讀取
該頁面,而後它讀取CustomerIds等於4和5的行,最後,它讀取剩餘的兩行
第二頁的行。
執行計劃如圖2-13所示。
圖2-13。索引尋求執行計劃
正如您所猜想的,索引搜索比索引掃描更有效,由於SQL Server只處理索引
行和數據頁的子集,而不是掃描整個表。
從技術上講,索引搜索操做有兩種。第一種稱爲單例查找,
或者有時是點查找,SQL Server尋找並返回單行。你能夠考慮一下WHERE
CustomerId = 2謂詞做爲示例。另外一種類型的索引查找操做稱爲範圍掃描,和
它要求SQL Server找到密鑰的最低或最高值並掃描(向前或向後)
行集直到它到達掃描範圍的末尾。客戶IDI 4和7之間的謂詞WHERE引導
到範圍掃描。這兩種狀況都在執行計劃中顯示爲INDEX SEEK操做。
您能夠猜到,範圍掃描徹底能夠強制SQL Server處理大量數據或
甚至索引中的全部數據頁面。例如,若是您將查詢更改成使用WHERE CustomerId> 0
謂詞,即便你有一個Index Seek運算符,SQL Server也會讀取全部行/頁面
顯示在執行計劃中。你必須記住這種行爲,並始終分析效率
查詢性能調整期間的範圍掃描。
關係數據庫中有一個名爲SARGable謂詞的概念,它表明S earch
Arg ement可以。若是SQL Server可使用索引查找操做(若是是索引),則謂詞是SARGable
存在。簡而言之,當SQL Server能夠隔離單個值或索引範圍時,謂詞是SARGable
要處理的關鍵值,從而限制謂詞評估期間的搜索。顯然,寫做是有益的
查詢使用SARGable謂詞並儘量利用索引查找。
SARGable謂詞包括如下運算符:=,>,> =,<,<=,IN,BETWEEN和LIKE(若是是前綴)
匹配)。非SARGable運算符包括NOT,<>,LIKE(在非前綴匹配的狀況下)和NOT IN。
使謂詞非SARGable的另外一種狀況是使用函數或數學
針對表列的計算。 SQL Server必須調用該函數或執行計算
它處理的每一行。幸運的是,在某些狀況下,您能夠重構查詢以生成此類謂詞
優化搜索。表2-1列出了一些例子。
第2章■表格和索引:內部結構和訪問方法
表2-1。將非SARGable謂詞重構爲SARGable的示例
操做非SARGable實施SARGable實施
數學計算列 - 1 = @Value Column = @Value + 1
ABS(列)= 1列IN(-1,1)
日期操做CAST(列爲日期)= @Date
(在2008年以前的SQL Server中)
轉換(datetime,轉換
(VARCHAR(10),柱,121))
列> = @Date和
列<DATEADD(day,1,@ Date)
DATEPART(年份,專欄)= @Year Column> = @Year and
列<DATEADD(年,1,@年)
DATEADD(第7天,第列)>
GETDATE()
專欄>
DATEADD(天,-7,GETDATE())
前綴搜索LEFT(Column,3)='ABC'列LIKE'ABC%'
子字符串搜索列LIKE'%ABC%'使用全文搜索或其餘
技術
您必須牢記的另外一個重要因素是類型轉換。在某些狀況下,你能夠作
經過使用不正確的數據類型來預測非SARGable。讓咱們建立一個包含varchar列的表
用一些數據填充它,如清單2-6所示。
清單2-6 SARG謂詞和數據類型:測試表建立
create table dbo.Data ( VarcharKey varchar(10) not null, Placeholder char(200) ); create unique clustered index IDX_Data_VarcharKey on dbo.Data(VarcharKey); ;with N1(C) as (select 0 union all select 0) -- 2 rows ,N2(C) as (select 0 from N1 as T1 cross join N1 as T2) -- 4 rows ,N3(C) as (select 0 from N2 as T1 cross join N2 as T2) -- 16 rows ,N4(C) as (select 0 from N3 as T1 cross join N3 as T2) -- 256 rows ,N5(C) as (select 0 from N4 as T1 cross join N4 as T2) -- 65,536 rows ,IDs(ID) as (select row_number() over (order by (select null)) from N5) insert into dbo.Data(VarcharKey) select convert(varchar(10),ID) from IDs;
聚簇索引鍵列定義爲varchar,即便它存儲整數值。 如今,咱們來吧
運行兩個選擇,如清單2-7所示,並查看執行計劃。
第2章■表格和索引:內部結構和訪問方法
清單2-7 SARG謂詞和數據類型:使用整數參數選擇
宣佈
@IntParam int ='200'
select * from dbo.Data where VarcharKey = @IntParam; select * from dbo.Data where VarcharKey = convert(varchar(10),@IntParam);
如圖2-14所示,對於整數參數,SQL Server掃描聚簇
index,將varchar轉換爲每行的整數。 在第二種狀況下,SQL Server轉換整數
在開頭的varchar參數,並利用更有效的聚簇索引查找操做。
圖2-14。 SARG謂詞和數據類型:帶整數參數的執行計劃
■提示請注意鏈接謂詞中的列數據類型。 隱式或顯式數據類型轉換
能夠顯着下降查詢的性能。
在unicode字符串參數的狀況下,您將觀察到很是相似的行爲。 咱們來運行查詢
如清單2-8所示。 圖2-15顯示了語句的執行計劃。
清單2-8 SARG謂詞和數據類型:使用字符串參數選擇
select * from dbo.Data where VarcharKey = '200'; select * from dbo.Data where VarcharKey = N'200'; -- unicode parameter
第2章■表格和索引:內部結構和訪問方法
圖2-15。 SARG謂詞和數據類型:帶字符串參數的執行計劃 如您所見,對於varchar列,unicode字符串參數是非SARGable。這是一個很大的問題比它看起來更大的問題。雖然您不多以這種方式編寫查詢,如清單2-8所示,如今大多數應用程序開發環境都將字符串視爲unicode。結果,SQL Server客戶端庫爲字符串對象生成unicode(nvarchar)參數,除非參數數據type顯式指定爲varchar。這使謂詞非SARGable,而且它能夠致使主要因爲沒必要要的掃描致使性能降低,即便對varchar列進行索引也是如此。■重要始終在客戶端應用程序中指定參數數據類型例如,在ADO.Net中,使用Parameters.Add(「@ ParamName」,SqlDbType.Varchar,<Size>)。Value = stringVariable而不是Parameters.Add(「@ ParamName」)。Value = stringVariable overload。在ORM框架中使用映射在類中顯式指定非unicode屬性。 值得一提的是,對於nvarchar unicode數據列,varchar參數是SARGable。