聚集索引和非聚集索引 簡析與對比
聚集(clustered)索引,也叫聚簇索引
定義:資料行的物理順序與列值(一般是主鍵的那一列)的邏輯順序相同,一個表中只能擁有一個聚集索引。
注:第一列的地址表示該行資料在磁碟中的實體地址,後面三列才是我們SQL裡面用的表裡的列,其中id是主鍵,建立了聚集索引。
結合上面的表格就可以理解這句話了吧:資料行的物理順序與列值的順序相同,如果我們查詢id比較靠後的資料,那麼這行資料的地址在磁碟中的實體地址也會比較靠後。而且由於物理排列方式與聚集索引的順序相同,所以也就只能建立一個聚集索引了。
聚集索引實際存放的示意圖
從上圖可以看出聚集索引的好處了,索引的葉子節點就是對應的資料節點(MySQL的MyISAM除外,此儲存引擎的聚集索引和非聚集索引只多了個唯一約束,其他沒什麼區別),可以直接獲取到對應的全部列的資料,而非聚集索引在索引沒有覆蓋到對應的列的時候需要進行二次查詢,後面會詳細講。因此在查詢方面,聚集索引的速度往往會更佔優勢。
建立聚集索引
如果不建立索引,系統會自動建立一個隱含列作為表的聚集索引。
1.建立表的時候指定主鍵(注意:SQL Sever預設主鍵為聚集索引,也可以指定為非聚集索引,而MySQL裡主鍵就是聚集索引)
create table t1( id int primary key, name nvarchar(255) )
2.建立表後新增聚集索引
MySQL
alter table table_name add primary key(colum_name)
值得注意的是,最好還是在建立表的時候新增聚集索引,由於聚集索引的物理順序上的特殊性,因此如果再在上面建立索引的時候會根據索引列的排序移動全部資料行上面的順序,會非常地耗費時間以及效能。
____________________________________________________________________________
非聚集(unclustered)索引
定義:該索引中索引的邏輯順序與磁碟上行的物理儲存順序不同
其實按照定義,除了聚集索引以外的索引都是非聚集索引,只是人們想細分一下非聚集索引,分成普通索引,唯一索引,全文索引。如果非要把非聚集索引類比成現實生活中的東西,那麼非聚集索引就像新華字典的偏旁字典,他結構順序與實際存放順序不一定一致。
非聚集索引的二次查詢問題
非聚集索引葉節點仍然是索引節點,只是有一個指標指向對應的資料塊,此如果使用非聚集索引查詢,而查詢列中包含了其他該索引沒有覆蓋的列,那麼他還要進行第二次的查詢,查詢節點上對應的資料行的資料。
有表t1:
其中有 聚集索引clustered index(id), 非聚集索引index(username)。
使用以下語句進行查詢,不需要進行二次查詢,直接就可以從非聚集索引的節點裡面就可以獲取到查詢列的資料。
select id, username from t1 where username = '小明'
select username from t1 where username = '小明'
但是使用以下語句進行查詢,就需要二次的查詢去獲取原資料行的score:
select username, score from t1 where username = '小明'
在SQL Server裡面查詢效率如下所示,Index Seek就是索引所花費的時間,Key Lookup就是二次查詢所花費的時間。可以看的出二次查詢所花費的查詢開銷佔比很大,達到50%。
這篇部落格有一個簡單示例:https://blog.csdn.net/jiadajing267/article/details/54581262
總結如下:
動作描述 |
使用聚集索引 |
使用非聚集索引 |
列經常被分組排序 |
應 |
應 |
返回某範圍內的資料 |
應 |
不應 |
一個或極少不同值 |
不應 |
不應 |
小數目的不同值 |
應 |
不應 |
大數目的不同值 |
不應 |
應 |
頻繁更新的列 |
不應 |
應 |
外來鍵列 |
應 |
應 |
主鍵列 |
應 |
應 |
頻繁修改索引列 |
不應 |
應 |
____________________________________________________________________________
我們需要搞清楚以下幾個問題:
第一:聚集索引的約束是唯一性,是否要求欄位也是唯一的呢? 不要求唯一!
分析:如果認為是的朋友,可能是受系統預設設定的影響,一般我們指定一個表的主鍵,如果這個表之前沒有聚集索引,同時建立主鍵時候沒有強制指定使用非聚集索引,SQL會預設在此欄位上建立一個聚集索引,而主鍵都是唯一的,所以理所當然的認為建立聚集索引的欄位也需要唯一。
結論:聚集索引可以建立在任何一列你想建立的欄位上,這是從理論上講,實際情況並不能隨便指定,否則在效能上會是惡夢。
第二:為什麼聚集索引可以建立在任何一列上,如果此表沒有主鍵約束,即有可能存在重複行資料呢?
粗一看,這還真是和聚集索引的約束相背,但實際情況真可以建立聚集索引。
分析其原因是:如果未使用 UNIQUE 屬性建立聚集索引,資料庫引擎將向表自動新增一個四位元組 uniqueifier 列。必要時,資料庫引擎 將向行自動新增一個 uniqueifier 值,使每個鍵唯一。此列和列值供內部使用,使用者不能檢視或訪問。
第三:是不是聚集索引就一定要比非聚集索引效能優呢?
如果想查詢學分在60-90之間的學生的學分以及姓名,在學分上建立聚集索引是否是最優的呢?
答:否。既然只輸出兩列,我們可以在學分以及學生姓名上建立聯合非聚集索引,此時的索引就形成了覆蓋索引,即索引所儲存的內容就是最終輸出的資料,這種索引在比以學分為聚集索引做查詢效能更好。
第四:在資料庫中通過什麼描述聚集索引與非聚集索引的?
索引是通過二叉樹的形式進行描述的,我們可以這樣區分聚集與非聚集索引的區別:聚集索引的葉節點就是最終的資料節點,而非聚集索引的葉節仍然是索引節點,但它有一個指向最終資料的指標。
第五:在主鍵是建立聚集索引的表在資料插入上為什麼比主鍵上建立非聚集索引錶速度要慢?
有了上面第四點的認識,我們分析這個問題就有把握了,在有主鍵的表中插入資料行,由於有主鍵唯一性的約束,所以需要保證插入的資料沒有重複。我們來比較下主鍵為聚集索引和非聚集索引的查詢情況:聚集索引由於索引葉節點就是資料頁,所以如果想檢查主鍵的唯一性,需要遍歷所有資料節點才行,但非聚集索引不同,由於非聚集索引上已經包含了主鍵值,所以查詢主鍵唯一性,只需要遍歷所有的索引頁就行(索引的儲存空間比實際資料要少),這比遍歷所有資料行減少了不少IO消耗。這就是為什麼主鍵上建立非聚集索引比主鍵上建立聚集索引在插入資料時要快的真正原因。