聚簇索引不是一個獨立的索引類型,而是一種數據存儲的方式。php
InnoDB使用B-Tree來實現聚簇索引,並把索引和數據存放在同一結構中。node
在有聚簇索引的表中,索引和行是存放在同一個葉子節點(B-Tree)中的。ide
"聚簇"表示索引值相鄰的行的存儲位置通常也是相鄰的——通常來講是這樣,但在某些狀況下不是。佈局
一張表只能有一個聚簇索引,由於同一份數據不可能出現兩個地方。ui
圖中展現了一個聚簇索引的示例,須要注意的是:只有葉子節點才包括了數據行的值,非葉子節點只存了索引字段。this
在某些DB實現中,能夠選擇按照哪一個索引作聚簇,可是MySQL的全部存儲都不支持這樣——InnoDB只能按照主鍵作聚簇。.net
若是表中沒有定義主鍵,InnoDB會選擇一個「非空且有惟一性約束「的索引來作聚簇,若是沒有這樣的索引,則會定義一個不可見的主鍵,並以這個主鍵來聚簇。設計
從圖中的佈局還能夠看到,整個索引以B-Tree方式組織,其中葉子節點包含了行的主鍵值和這一行全部字段的值,非葉子節點只是單純的提供查找路徑信息。unix
在非主鍵索引中,葉子節點存放的是索引字段的值和一個指向聚簇索引的」行指針「——對應行的主鍵的值。指針
InnoDB clusters records together only within a page.Pages with adjacent key values may be distant from each other.
InnoDB的聚簇是頁面級別的,即聚簇索引值相鄰的行會被存入同一個頁面,當這個頁面存滿時,接下來的相鄰行會被存入另一個頁面,而這兩個頁面可能會離的很遠。如圖中的佈局所示,索引值爲1~10的行被存在第一個頁面,11~20的行被存在另一個頁面,數據被聚簇在單個頁面內,而頁面1和頁面2的磁盤位置卻可能離的很遠。
聚簇的好處:
1. You can keep related data close together——在設計一個郵件系統時,按照(用戶id,郵件id)來作聚簇,同一個用戶的郵件會被存在相鄰的磁盤區域,這樣獲取用戶全部郵件的時候,磁盤IO開銷就很小。
2. Data access is fast——聚簇索引把索引和數據放在同一個B樹中,在以聚簇索引獲取整行數據會很快。
3. Queries that use covering indexes can use the primary key values contained at the leaf node——只要是以聚簇索引爲條件來查找數據,都能利用到」覆蓋索引「這個特性,效率高。
聚簇的不足:
1. 聚簇是數據在磁盤上的組織方式,因此只能帶來磁盤IO上的收益,若是表的數據能所有載入內存,聚簇索引就沒什麼幫助了。
2. 數據插入(Insert)的速度依賴於插入順序——大量數據按照聚簇索引順序插入會很快,若是亂序插入,就會比較慢,而且在大量亂序插入數據後,最好對錶作一次rebuild操做。
3. 更新聚簇索引字段的開銷很大,由於系統不得不維護全部有關的頁面。
4. 聚簇的數據按照」頁面「來存放,當一個列必需要放入一個已經滿了的頁面時,就會觸發頁面分裂——頁面分裂會帶來附加的IO開銷,也會佔用更多的磁盤空間。
5. 在聚簇的表上作全表掃描會很慢,特別是數據的密集度很低(每一個頁面都有大量的空閒項)或由於屢次頁面分裂後,數據存放無序時。
6. 非主鍵索引會比想象中的要大,由於非主鍵索引除了要存放索引字段,還須要一個指向聚簇索引的入口指針。
7. 以非主鍵索引爲條件訪問不包含在索引中的字段時,須要作2次索引掃描。
The last point can be a bit confusing. Why would a secondary index require two index lookups? The answer lies in the nature of the "row pointers" the secondary index stores. Remember, a leaf node doesn't store a pointer to the referenced row's physical location; rather, it stores the row's primary key values.
That means that to find a row from a secondary index, the storage engine first finds the leaf node in the secondary index and then uses the primary key values stored there to navigate the primary key and find the row. That's double work: two B-Tree navigations instead of one. (In InnoDB, the adaptive hash index can help reduce this penalty.)
爲何非主鍵索引須要兩次索引掃描?這關係到非主鍵索引的一個特性——行指針——非主鍵索引的葉子節點存放的不是指向數據行實際物理位置的指針,而是行的主鍵值。
當按照非主鍵索引查找數據時,存儲引擎首先按照非主鍵索引取得對應的」行指針「,而後再按照這個指針查詢主鍵索引獲取整行的值,因此產生了兩次B-Tree查詢。
書中原文以 cluster index和secondary index來區分兩種索引,針對InnoDB,能夠直接說成是主鍵和非主鍵。
原文地址:http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=3639012&highlight=(感謝做者)