索引深刻淺出(4/10):非彙集索引的B樹結構在彙集表

一個表只能有一個彙集索引,數據行以此彙集索引的順序進行存儲,一個表卻能有多個非彙集索引。咱們已經討論了彙集索引的結構,這篇咱們會看下非彙集索引結構。html

非彙集索引的邏輯呈現

簡單來講,非彙集索引是表的子集。當咱們定義了一個非彙集索引時,SQL Server把整套非彙集索引鍵存在不一樣的頁裏。咱們來看下一個包含BusinessEntityID(PK),PersonType,FirstName,LastName這4列的表,這個表上有一個非彙集索引定義。主體表按BusinessEntityID列(彙集索引鍵)的順序存儲。非彙集索引的存儲是與主體表分離的。若是你仔細看非彙集索引表,你會發現,記錄是按Firstname,lastname 列的順序排列的。簡單理解下,非彙集索引就是主體表的子集。sql

假設如今咱們要找出first name值爲Michael的記錄。若是你從實體表找的話,咱們須要從頭到腳把每條記錄匹配一次,由於記錄並無按first name列排序保存。若是這個表記錄有上千條記錄的話,這將是一個很是無聊且費時的工做。若是咱們在非彙集索引表裏找將會容易不少,由於這個表是按first name列以字母順序排列的。咱們很容易定位到first name是Michael的記錄。咱們並不須要再往下找,由於咱們肯定沒有更多的first name是Michael的記錄了。瀏覽器

如今咱們獲得了Firstname,lastname的值。那咱們如何拿到其它2列的值?讓咱們對非彙集索引作一些改動,將BusinessEntityID列也做爲非彙集索引。ide

如今,一旦咱們定位到記錄,咱們可使用BusinessEntityID(彙集索引鍵)列返回主體表,獲得其餘列的值,這個操做被稱爲書籤查找(bookmark lookups)或RID查找。工具

彙集索引與非彙集索引

 非彙集索引和彙集索引有同樣的B樹結構。非彙集索引鍵不會對主體表的數據排序作任何改變,由於彙集索引強制SQL Server將數據以彙集索引鍵的順序存儲。彙集索引的葉子層由包含表具體數據的數據頁組成,而非彙集索引的葉子層由索引頁組成。sqlserver

非彙集索引能夠定義在堆表彙集表。在非彙集索引的葉子層,每一個索引行包含非彙集索引鍵值和行定位器。這個定位器指向彙集索引或堆表的數據行。在非彙集索引行裏的行定位器要麼指向行,要麼指向行彙集索引鍵。若是是堆表,它沒有彙集索引,行定位器是個指向行的指針。這個指針由頁裏行的(文件號:頁號:槽號,file identifier :page number :slot number)組成。整個指針被稱爲ROW ID(RID)。若是表有彙集索引,行定位器是行的彙集索引鍵。spa

非彙集索引深刻淺出

咱們用文章「索引深刻淺出:彙集索引的B樹結構」用到的salesorderdetails建立一個非彙集索引,這個表在salesorderdetailid列有一個彙集索引。3d

1 CREATE UNIQUE INDEX Ix_ProductId ON SalesOrderDetail(ProductId,Salesorderid) 

收集非彙集索引相關信息:指針

 1 TRUNCATE TABLE dbo.sp_table_pages
 2 INSERT INTO sp_table_pages EXEC('DBCC IND(IndexDB,SalesOrderDetail,2)')
 3 GO
 4 
 5 SELECT * FROM dbo.sp_table_pages ORDER BY IndexLevel DESC --根節點/索引頁
 6 DBCC TRACEON(3604)
 7 DBCC PAGE(IndexDB,1,3472,3)
 8 
 9 DBCC TRACEON(3604)
10 DBCC PAGE(IndexDB,1,3416,3)--葉子節點/索引頁
11 
12 DBCC TRACEON(3604)
13 DBCC PAGE(IndexDB,1,3557,3)--葉子節點/索引頁
14 SELECT * FROM dbo.sp_table_pages WHERE IndexLevel=0 --葉子節點/索引頁

根據上述信息進行非彙集索引邏輯示意圖的繪製:code

如今咱們來分析下SQL Server如何存儲非彙集索引,首先咱們經過DBCC IND命令查看非彙集索引的頁分配狀況,最後一個參數,2是Ix_ProductId的索引號。

1 DBCC IND(IndexDB,SalesOrderDetail,2)

 咱們看到輸出結果一共有229條記錄,包含1個IAM頁和229個索引頁。咱們能夠經過找IndexLevel 列值最大的記錄,來找根頁(root page)。記住索引層級是從葉子層向根層增加的。

1 SELECT * FROM dbo.sp_table_pages ORDER BY IndexLevel DESC --根節點/索引頁

在這個表裏,咱們根層(root leve)頁號是3472,index level是1,這就是說,這個非彙集索引的B樹結構只有根層(root level)和葉子層(leaf level),沒有中間層(intermediate level)。咱們來看看3472頁。 

1 DBCC TRACEON(3604)
2 DBCC PAGE(IndexDB,1,3472,3)

返回結果一共有227條記錄(227個葉子層的索引頁)。部分結果如上所示。這和彙集索引裏的根層(root)/中間層(intermediate)的頁結構是同樣的。productid與salesorderid組合的值小於或等於(707,51151)的全部記錄,能夠在子頁3416裏找到。productid與salesorderid組合的值在(707,51151)與(707,55920)之間的全部記錄,能夠在子頁3417裏找到,並以此類推。

咱們來看看3417頁。

1 DBCC TRACEON(3604)
2 DBCC PAGE(IndexDB,1,3417,3)

一共返回539條記錄,都是product id爲707的記錄。這裏的索引只用2層,這個是B樹結構的葉子層。你會注意到,這裏沒有子頁ID列,但咱們有salesorderdetailid列(彙集索引鍵),SQL Server用它來進行鍵或書籤查找操做。

 咱們來看看,SQL Server如何使用這個索引進行一個SELECT操做。點擊工具欄的顯示包含實際的執行計劃。

1 SET STATISTICS IO ON
2 GO
3 SELECT *  FROM SalesOrderDetail WHERE productid=707 AND SalesOrderid=51192 

能夠看到執行計劃的鍵查找操做。由於這裏where條件恰好徹底符合咱們非彙集索引定義,SQL Server用這個索引來執行查詢。首先SQL Server讀取B樹結構的根頁。咱們的查詢條件組合(707,51192)落在根頁的第二條記錄上,所以SQL Server走到它的子頁(頁號3417)。在這個頁裏,咱們能夠用條件組合(707,51192)定位到具體的記錄上,它的salesorderdetailid值是37793。從這裏開始,SQL Server使用salesorderdetailid值進行鍵查找(key look up)操做。從上一個文章知道,但咱們進行任何彙集索引鍵查找是,須要執行3個I/O。 所以這裏,SQL Server須要執行5個I/O操做(2個在非彙集索引,3個在彙集索引的書籤/鍵查找(bookmark/key lookup),這個和你的結果輸出一致。

爲了更好的理解它,咱們能夠把非彙集索引看成salesorderdetail 表的一個子表(咱們把它叫作Saleorderdetail_NC),有productid,salesorderid 和 SalesorderDetailid列,而且 ProductId與salesorderid列組合爲彙集索引。上述查詢的結果能夠經過如下2個查詢來得到。

1 SELECT *  FROM SalesOrderDetail_nc WHERE productid=707 AND SalesOrderid=51192
2 GO
3 SELECT *  FROM SalesOrderDetail WHERE SalesOrderDetailid=37793

咱們再來看一個查詢:


1
SELECT * FROM SalesOrderDetail WHERE productid=707

查詢返回3083條記錄,查詢條件與非彙集索引的第一列匹配。可是SQL Server並沒用非彙集索引來執行這個查詢,查詢計劃以下所示。

這樣作的緣由是,若是使用非彙集索引,就須要爲3083條記錄執行書籤查找(key lookup)。這會產生9249個I/O操做(3083*3)。所以,SQL Server使用了彙集索引掃描,它只須要1501(對於彙集索引樹結構須要的頁數)個I/O操做。若是咱們作一個小的改動,只要Productid ,SalesOrderDetailid和SalesOrderId列,SQL Server會使用非彙集索引,由於它不須要進行書籤查找(bookmark lookup)操做。非彙集索引的葉子層已經包含這些列了。

1 SELECT productid,salesorderdetailid,salesorderid  FROM SalesOrderDetail WHERE productid=707

這篇文章真的有點長,並且我是該死的BING輸入法出錯,致使瀏覽器崩潰,丟失一個晚上3個小時成果,從新寫好的,但願你們看了以後能夠透徹理解非彙集索引了,晚安各位!!2015-05-14 00:18:42 

參考文章:

http://www.sqlservercentral.com/blogs/practicalsqldba/2013/03/14/sql-server-part-4-explaining-the-non-clustered-index-structure-/

相關文章
相關標籤/搜索