本文關(guān)注以下方面(本文所有的討論基于SQL Server數(shù)據(jù)庫):
一、索引定義分類
讓我們先來回答幾個問題:
二、索引數(shù)據(jù)結(jié)構(gòu)
在SQL Server數(shù)據(jù)庫中,索引的存儲是以B+樹(注意和二叉樹的區(qū)別)結(jié)構(gòu)來存儲的,又稱索引樹,其節(jié)點類型為如下兩種:
索引節(jié)點按照層級關(guān)系,有時又可以分為根節(jié)點和中間節(jié)點,其本質(zhì)是一樣的,都只包含下一層節(jié)點的入口值和入口指針;
葉子節(jié)點就不同了,它包含數(shù)據(jù),這個數(shù)據(jù)可能是表中真實的數(shù)據(jù)行,也有可能是索引列值和行書簽,前者對應(yīng)于聚集索引,后者對應(yīng)于非聚集索引。
三、索引存儲結(jié)構(gòu)
在正式討論索引的存儲結(jié)構(gòu)之前,我們有必要先來了解一下SQL Server數(shù)據(jù)庫的存儲結(jié)構(gòu)。
SQL Server數(shù)據(jù)庫存儲(結(jié)構(gòu))的最小單位是頁,大小為8K,共8 * 1024 = 8192Byte,不論是數(shù)據(jù)頁還是索引頁都是以此方式存放。實際上對于SQL Server數(shù)據(jù)庫而言,其頁(Page)類型有很多種,大概有如下十幾種(http://www.sqlnotes.info/2011/10/31/page-type/):
表中所有數(shù)據(jù)頁的存放在磁盤上又有兩種組織方式:
如果表中所有數(shù)據(jù)頁是以一種頁間無序、隨機存儲的方式,則稱這樣的表為堆表;
否則如果表中數(shù)據(jù)頁間按某種方式(如表中某個字段)有序地存儲與磁盤上,則稱為索引組織表。
四、聚集索引
下面我們將深入研究一下數(shù)據(jù)庫中的索引到底是如何存儲的以及如何被使用的。
為了測試驗證等,我們在數(shù)據(jù)庫PCT上新建一張測試表Employee,有兩個字段,其中EmployeeId為主鍵
USE PCTCREATE TABLE Employee ( EmployeeId NVARCHAR(32) NOT NULL PRIMARY KEY, EmployeeName NVARCHAR(40) NOT NULL,);
插入10W筆測試數(shù)據(jù)
SET NOCOUNT ONdeclare @i intset @i=1while @i<=100000begin INSERT INTO Employee VALUES(replace(newid(), '-', ''), 'Employee_' + CONVERT(varchar, @i) );set @i = @i+1end
通過DBCC IND命令來查看索引的情況
DBCC IND ([PCT], [DBO.Employee], -1)
結(jié)果如下

紅色標(biāo)記說明:
為了方便查找,我們也可以把上述結(jié)果存入表中,為此建表
CREATE TABLE DBCCIndResult ( PageFID NVARCHAR(200), PagePID NVARCHAR(200), IAMFID NVARCHAR(200), IAMPID NVARCHAR(200), ObjectID NVARCHAR(200), IndexID NVARCHAR(200), PartitionNumber NVARCHAR(200), PartitionID NVARCHAR(200), iam_chain_type NVARCHAR(200), PageType NVARCHAR(200), IndexLevel NVARCHAR(200), NextPageFID NVARCHAR(200), NextPagePID NVARCHAR(200), PrevPageFID NVARCHAR(200), PrevPagePID NVARCHAR(200))
插入數(shù)據(jù)
INSERT INTO DBCCIndResult EXEC ('DBCC IND(PCT,Employee,-1) ')我們可以通過下面的語句來查看索引的深度
select * from sys.dm_db_index_physical_stats(db_id('PCT'),object_id('Employee'),null,null,null)
我們看到索引的深度為3,上面的IndexLevel分別有0,1,2也驗證了這一點。page_count為1944,但是我們上面查到的結(jié)果卻是1977,這是因為這里的語句沒有計算Index為1和2的頁(注意index_level列)
接下來我們看看B樹中各種節(jié)點存儲的到底是什么?
找到根節(jié)點283
select * from DBCCIndResult where pagetype = 2 and indexLevel = 2
查看頁里的數(shù)據(jù)
DBCC TRACEON (3604);GODBCC PAGE (PCT, 1, 283, 3);GO

從上圖,可以看出,此根節(jié)點共有31個兒子(中間節(jié)點),而且還存有主鍵值EmployeeId,那么這31個主鍵值是哪些記錄的主鍵值呢?我們繼續(xù)深入
以中間節(jié)點1863為例
DBCC TRACEON (3604);GODBCC PAGE (PCT, 1, 1863, 3);GO

這和根節(jié)點很類似,標(biāo)明了包含下一層的節(jié)點(共65個)和主鍵值,繼續(xù)深入
以葉節(jié)點807為例
DBCC TRACEON (3604);GODBCC PAGE (PCT, 1, 807, 3);GO


由于結(jié)果太多,我就不把所有的截圖都發(fā)出來了,但是從上面我們已經(jīng)看到了一些重要的東西
首先PAGE:(1:807)表明這是一個葉節(jié)點,同時也是一個數(shù)據(jù)頁,因為它存放了表里所有字段的數(shù)據(jù)(EmployeeId和EmployeeName),換句話說這兒的葉節(jié)點就是表Employee在數(shù)據(jù)庫中的存儲數(shù)據(jù)頁,也就是說聚集索引的葉節(jié)點其實就是表的數(shù)據(jù)存儲頁
其次我們看標(biāo)紅的EmployeeId,它就是我們在之前根節(jié)點283和中間節(jié)點1863存儲的主鍵值,而且它是位于數(shù)據(jù)存儲頁的第一個數(shù)據(jù)
至此我們總結(jié)如下:
為了更方便地查看葉節(jié)點的數(shù)據(jù),我們將其存入表中
DBCC PAGE(PCT,1,807, 3) WITH TABLERESULTS
這種方式是以表的方式展示

但是這種方式也不便查找,我們索性新建表
CREATE TABLE DBCCPageResult( ParentObject NVARCHAR(200), Object NVARCHAR(200), Field NVARCHAR(200), Value NVARCHAR(200))
插入數(shù)據(jù)
INSERT INTO DBCCPageResult EXEC ('DBCC PAGE(PCT,1,807, 3) WITH TABLERESULTS')查看EmployeeId數(shù)據(jù)
select * from DBCCPageResult where Field = 'EmployeeId'

注意Value,是按順序排好的,這也是聚集索引的意義了 - 把數(shù)據(jù)按順序存儲.
至此我們又可以得出:

五、非聚集索引
在表Employee字段EmployeeName建立非聚集索引
CREATE NONCLUSTERED INDEX IX_TBL_Employee_EmployeeName ON Employee(EmployeeName) WITH FILLFACTOR= 30GO
再增加一列Pho
新聞熱點
疑難解答
圖片精選