聚簇索引與非聚簇索引 txt

2022-05-08 12:27:11 字數 3617 閱讀 3673

聚簇索引與非聚簇索引的本質區別到底是什麼?什麼時候用聚簇索引,什麼時候用非聚簇索引?

這是乙個很複雜的問題,很難用三言兩語說清楚。我在這裡從sql server索引優化查詢的角度簡單談談(如果對這方面感興趣的話,可以讀一讀微軟出版的《microsoft sql server 2000資料庫程式設計》第3單元的資料結構引論以及第6、13、14單元)。

一、索引塊與資料塊的區別

大家都知道,索引可以提高檢索效率,因為它的二叉樹結構以及占用空間小,所以訪問速度塊。讓我們來算一道數學題:如果表中的一條記錄在磁碟上占用1000位元組的話,我們對其中10位元組的乙個字段建立索引,那麼該記錄對應的索引塊的大小只有10位元組。我們知道,sql server的最小空間分配單元是「頁(page)」,乙個頁在磁碟上占用8k空間,那麼這乙個頁可以儲存上述記錄8條,但可以儲存索引800條。現在我們要從乙個有8000條記錄的表中檢索符合某個條件的記錄,如果沒有索引的話,我們可能需要遍歷8000條×1000位元組/8k位元組=1000個頁面才能夠找到結果。如果在檢索欄位上有上述索引的話,那麼我們可以在8000條×10位元組/8k位元組=10個頁面中就檢索到滿足條件的索引塊,然後根據索引塊上的指標逐一找到結果資料塊,這樣io訪問量要少的多。

二、索引優化技術

是不是有索引就一定檢索的快呢?答案是否。有些時候用索引還不如不用索引快。比如說我們要檢索上述表中的所有記錄,如果不用索引,需要訪問8000條×1000位元組/8k位元組=1000個頁面,如果使用索引的話,首先檢索索引,訪問8000條×10位元組/8k位元組=10個頁面得到索引檢索結果,再根據索引檢索結果去對應資料頁面,由於是檢索所有資料,所以需要再訪問8000條×1000位元組/8k位元組=1000個頁面將全部資料讀取出來,一共訪問了1010個頁面,這顯然不如不用索引快。

sql server內部有一套完整的資料檢索優化技術,在上述情況下,sql server的查詢計畫(search plan)會自動使用表掃瞄的方式檢索資料而不會使用任何索引。那麼sql server是怎麼知道什麼時候用索引,什麼時候不用索引的呢?sql server除了日常維護資料資訊外,還維護著資料統計資訊,下圖是資料庫屬性頁面的乙個截圖:

從圖中我們可以看到,sql server自動維護統計資訊,這些統計資訊包括資料密度資訊以及資料分布資訊,這些資訊幫助sql server決定如何制定查詢計畫以及查詢是是否使用索引以及使用什麼樣的索引(這裡就不再解釋它們到底如何幫助sql server建立查詢計畫的了)。我們還是來做個實驗。建立一張表:tabtest(id, unqvalue,intvalue),其中id是整形自動編號主索引,unqvalue是uniqueidentifier型別,在上面建立普通索引,intvalue 是整形,不建立索引。之所以掛上乙個沒有索引的intvalue欄位,就是防止sql server使用索引覆蓋查詢優化技術,這樣實驗就起不到作用了。向表中錄入10000條隨機記錄,**如下:

create table [dbo].[tabtest] (

[id] [int] identity (1, 1) not null ,

[unqvalue] [uniqueidentifier] not null ,

[intvalue] [int] not null

) on [primary]

goalter table [dbo].[tabtest] with nocheck add

constraint [pk_tabtest] primary key  clustered

([id]

)  on [primary]

goalter table [dbo].[tabtest] add

constraint [df_tabtest_unqvalue] default (newid()) for [unqvalue]

gocreate  index [ix_tabtest_unqvalue] on [dbo].[tabtest]([unqvalue]) on [primary]

godeclare @i int

declare @v int

set @i=0

while @i<10000

begin

set @v=rand()*1000   

insert into tabtest ([intvalue]) values (@v)

set @i=@i+1

end然後我們執行兩個查詢並檢視執行計畫,如圖:(在查詢分析器的查詢選單中可以開啟查詢計畫,同時圖上第乙個查詢的guid是我從資料庫中找的,大家做實驗的時候可以根據自己資料庫中的值來定):

從圖中可以看出,在第乙個查詢中,sql server使用了ix_tabtest_unqvalue索引,根據箭頭方向,計算機先在索引範圍內找,找到後,使用bookmark lookup將索引節點對映到資料節點上,最後給出select結果。在第二個查詢中,系統直接遍歷表給出結果,不過它使用了聚簇索引,為什麼呢?不要忘了,聚簇索引的頁節點就是資料節點!這樣使用聚簇索引會更快一些(不受資料刪除、更新留下的儲存空洞的影響,直接遍歷資料是要跳過這些空洞的)。

下面,我們在sql server中將id欄位的聚簇索引更改為非聚簇索引,然後再執行select * from tabtest,這回我們看到的執行計畫變成了:

sql server沒有使用任何索引,而是直接執行了table scan,因為只有這樣,檢索效率才是最高的。

三、聚簇索引與非聚簇索引的本質區別

還是通過一道數學題來看看它們的區別吧:假設有一8000條記錄的表,表中每條記錄在磁碟上占用1000位元組,如果在乙個10位元組長的字段上建立非聚簇索引主鍵,需要二叉樹節點16000個(這16000個節點中有8000個葉節點,每個頁節點都指向乙個資料記錄),這樣資料將占用8000條×1000位元組/8k位元組=1000個頁面;索引將占用16000個節點×10位元組/8k位元組=20個頁面,共計1020個頁面。

同樣一張表,如果我們在對應欄位上建立聚簇索引主鍵,由於聚簇索引的頁節點就是資料節點,所以索引節點僅有8000個,占用10個頁面,資料仍然占有1000個頁面。

下面我們看看在執行插入操作時,非聚簇索引的主鍵為什麼比聚簇索引主鍵要快。主鍵約束要求主鍵不能出現重複,那麼sql server是怎麼知道不出現重複的呢?唯一的方法就是檢索。對於非聚簇索引,只需要檢索20個頁面中的16000個節點就知道是否有重複,因為所有主鍵鍵值在這16000個索引節點中都包含了。但對於聚簇索引,索引節點僅僅包含了8000個中間節點,至於會不會出現重複必須檢索另外1000個頁資料節點才知道,那麼相當於檢索10+1000=1010個頁面才知道是否有重複。所以聚簇索引主鍵的插入速度要比非聚簇索引主鍵的插入速度慢很多。

讓我們再來看看資料檢索的效率,如果對上述兩表進行檢索,在使用索引的情況下(有些時候sql server執行計畫會選擇不使用索引,不過我們這裡姑且假設一定使用索引),對於聚簇索引檢索,我們可能會訪問10個索引頁面外加1000個資料頁面得到結果(實際情況要比這個好),而對於非聚簇索引,系統會從20個頁面中找到符合條件的節點,再對映到1000個資料頁面上(這也是最糟糕的情況),比較一下,乙個訪問了1010個頁面而另乙個訪問了1020個頁面,可見檢索效率差異並不是很大。所以不管非聚簇索引也好還是聚簇索引也好,都適合排序,聚簇索引僅僅比非聚簇索引快一點。

結語好了,寫了半天,手都累了。關於聚簇索引與非聚簇索引效率問題的實驗就不做了,感興趣的話可以自己使用查詢分析器對查詢計畫進行分析。sql server是乙個很複雜的系統,尤其是索引以及查詢優化技術,oracle就更複雜了。了解索引以及查詢背後的事情不是什麼壞事,它可以幫助我們更為深刻的了解我們的系統。

聚簇索引與非聚簇索引

聚簇索引介紹 聚簇索引並不是一種單獨的索引型別,而是一種資料儲存方式。具體的細節依賴於實現方式,例innodb的聚簇索引實際上在同乙個結構中儲存了b tree索引和資料行。當表有聚簇索引時,他的資料行實際放在索引的葉子頁 leaf page 術語 聚簇 聚簇索引實現 儲存引擎負責實現索引,因此不是所...

聚簇索引與非聚簇索引

mysql的索引主要使用b 樹和雜湊索引的方式進行組織。雜湊索引底層即是雜湊表,查詢時只需要進行一次雜湊操作即可得到位址,查詢速度比較快,但是查詢時操作只適合 的查詢操作,對於範圍查詢不友好,因此只適用於大多數需求為單錶查詢的情況。mysql中常用的兩大引擎myisam和innodb 對於b 樹的使...

聚簇與非聚簇索引

我們平時建立的索引唯一鍵索引,復合索引,字首索引都是非聚簇索引,有的也叫輔助索引 secondary index 其資料結構是b 樹。在mysql中,聚簇索引沒有語句可以生成,在 innodb中,資料是按照主鍵的順序來進行儲存的。葉子節點就是存放每條記錄的。由於表所有資料只能按照乙個b 樹進行排序,...