彙集索引與非彙集索引的總結

一.索引簡介

衆所周知,索引是關係型數據庫中給數據庫表中一列或多列的值排序後的存儲結構,SQL的主流索引結構有B+樹以及Hash結構,彙集索引以及非彙集索引用的是B+樹索引。這篇文章會總結SQL Server以及MySQL的InnoDB和MyISAM兩種SQL的索引。html

SQL Sever索引類型有:惟一索引,主鍵索引,彙集索引,非彙集索引。

MySQL 索引類型有:惟一索引,主鍵(彙集)索引,非彙集索引,全文索引。
sql

二.彙集索引

彙集(clustered)索引,也叫聚簇索引。數據庫

定義:數據行的物理順序與列值(通常是主鍵的那一列)的邏輯順序相同,一個表中只能擁有一個彙集索引。性能

單單從定義來看是否是顯得有點抽象,打個比方,一個表就像是咱們之前用的新華字典,彙集索引就像是拼音目錄,而每一個字存放的頁碼就是咱們的數據物理地址,咱們若是要查詢一個「哇」字,咱們只須要查詢「哇」字對應在新華字典拼音目錄對應的頁碼,就能夠查詢到對應的「哇」字所在的位置,而拼音目錄對應的A-Z的字順序,和新華字典實際存儲的字的順序A-Z也是同樣的,若是咱們中文新出了一個字,拼音開頭第一個是B,那麼他插入的時候也要按照拼音目錄順序插入到A字的後面,如今用一個簡單的示意圖來大概說明一下在數據庫中的樣子:優化

地址 id username score
0x01 1 小明 90
0x02 2 小紅 80
0x03 3 小華 92
.. .. .. ..
0xff 256 小英 70

注:第一列的地址表示該行數據在磁盤中的物理地址,後面三列纔是咱們SQL裏面用的表裏的列,其中id是主鍵,創建了彙集索引。
指針

結合上面的表格就能夠理解這句話了吧:數據行的物理順序與列值的順序相同,若是咱們查詢id比較靠後的數據,那麼這行數據的地址在磁盤中的物理地址也會比較靠後。並且因爲物理排列方式與彙集索引的順序相同,因此也就只能創建一個彙集索引了。code




彙集索引實際存放的示意圖server

從上圖能夠看出彙集索引的好處了,索引的葉子節點就是對應的數據節點(MySQL的MyISAM除外,此存儲引擎的彙集索引和非彙集索引只多了個惟一約束,其餘沒什麼區別),能夠直接獲取到對應的所有列的數據,而非彙集索引在索引沒有覆蓋到對應的列的時候須要進行二次查詢,後面會詳細講。所以在查詢方面,彙集索引的速度每每會更佔優點。htm

建立彙集索引

若是不建立索引,系統會自動建立一個隱含列做爲表的彙集索引。blog

1.建立表的時候指定主鍵(注意:SQL Sever默認主鍵爲彙集索引,也能夠指定爲非彙集索引,而MySQL裏主鍵就是彙集索引)

create table t1(
    id int primary key,
    name nvarchar(255)
)

2.建立表後添加彙集索引

SQL Server

create clustered index clustered_index on table_name(colum_name)

MySQL

alter table table_name add primary key(colum_name)

值得注意的是,最好仍是在建立表的時候添加彙集索引,因爲彙集索引的物理順序上的特殊性,所以若是再在上面建立索引的時候會根據索引列的排序移動所有數據行上面的順序,會很是地耗費時間以及性能。

三.非彙集索引

非彙集(unclustered)索引。

定義:該索引中索引的邏輯順序與磁盤上行的物理存儲順序不一樣,一個表中能夠擁有多個非彙集索引。

其實按照定義,除了彙集索引之外的索引都是非彙集索引,只是人們想細分一下非彙集索引,分紅普通索引,惟一索引,全文索引。若是非要把非彙集索引類比成現實生活中的東西,那麼非彙集索引就像新華字典的偏旁字典,他結構順序與實際存放順序不必定一致。




非彙集索引實際存放的示意圖

非彙集索引的二次查詢問題

非彙集索引葉節點仍然是索引節點,只是有一個指針指向對應的數據塊,此若是使用非彙集索引查詢,而查詢列中包含了其餘該索引沒有覆蓋的列,那麼他還要進行第二次的查詢,查詢節點上對應的數據行的數據。

若有如下表t1:

id username score
1 小明 90
2 小紅 80
3 小華 92
.. .. ..
256 小英 70

以及彙集索引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%。



在SQL Server裏面會對查詢自動優化,選擇適合的索引,所以若是在數據量不大的狀況下,SQL Server頗有可能不會使用非彙集索引進行查詢,而是使用匯集索引進行查詢,即使須要掃描整個彙集索引,效率也比使用非彙集索引效率要高。



本人試過在含有30w行表上創建非彙集索引,查詢非彙集索引覆蓋之外的列就會變成彙集索引的全索引掃描(index scan)查詢來避免二次查詢,而在另一張200w行表纔會用到非彙集索引seek對應的列再進行kek lookup,有關於SQL Server的有Index seek,index scan, table scan,key LookUp這幾個概念,能夠查看這個blog,描寫比較詳細。

但在MySQL裏面就算表裏數據量少且查詢了非鍵列,也不會使用匯集索引去全索引掃描,但若是強制使用匯集索引去查詢,性能反而比非彙集索引查詢要差,這就是兩種SQL的不一樣之處。

還有一點要注意的是非彙集索引其實葉子節點除了會存儲索引覆蓋列的數據,也會存放彙集索引所覆蓋的列數據。

如何解決非彙集索引的二次查詢問題

複合索引(覆蓋索引)

創建兩列以上的索引,便可查詢複合索引裏的列的數據而不須要進行回表二次查詢,如index(col1, col2),執行下面的語句

select col1, col2 from t1 where col1 = '213';

要注意使用複合索引須要知足最左側索引的原則,也就是查詢的時候若是where條件裏面沒有最左邊的一到多列,索引就不會起做用。

在SQL Server中還有include的用法,能夠把非彙集索引裏包含的列包含進來,而不必定須要創建複合索引。

四.總結與使用心得

  1. 使用匯集索引的查詢效率要比非彙集索引的效率要高,可是若是須要頻繁去改變彙集索引的值,寫入性能並不高,由於須要移動對應數據的物理位置。
  2. 非彙集索引在查詢的時候能夠的話就避免二次查詢,這樣性能會大幅提高。
  3. 不是全部的表都適合創建索引,只有數據量大表才適合創建索引,且創建在選擇性高的列上面性能會更好。


另附本人博客地址

參考資料:

[1]:微軟技術支持官方博客
[2]:彙集索引和非彙集索引(整理)

相關文章
相關標籤/搜索