聚集(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消耗。這就是為什么主鍵上創建非聚集索引比主鍵上創建聚集索引在插入數據時要快的真正原因。