聚簇索引並不是一個獨立的索引類型。確切的說它們是儲存資料的一個途徑。在不同實現之間,還是有一些細節上的變化,但是InnoDB的聚簇索引實際是在相同的結構中把B-TREE索引和這些行一併的儲存。
當一個表中有聚簇索引,它的行實際儲存在索引的葉子的頁(leaf pages)。“clustered”所指的意思是行的相鄰索引值彼此儲存的非常近。你可能每張表只會有一個聚簇索引,因為你不能同時的在兩個地方儲存這些行。
因為儲存引擎負責實現索引,因此並不是所有的儲存引擎都支援聚簇索引。目前來說,solidDB和InnoDB支援。這部分我們重點放在InnoDB上,但是對於那些已經實現或者將要實現聚簇索引的引擎來說,在概念上至少有部分的正確。
下圖展示了記錄是怎樣分布在聚簇索引上的。注意的是葉子的頁(leaf pages)包含了整個行,但是節點的頁僅僅包含了索引的列。這個例子中,索引列包含了整型值。
一些資料庫伺服器使你可以選擇哪個索引可以聚簇,但是MySQL目前的儲存引擎是不能那麼做的。InnoDB通過主鍵來集中資料的。也就是說上圖的索引列就是主鍵列。
如果你沒有定義主鍵,InnoDB會選擇一個唯一非空的索引來替代主鍵索引。如果沒有這麼個索引,InnoDB就會定義一個隱藏的主鍵。InnoDB僅僅在一個頁中聚集資料。頁所伴隨的臨近的索引值可能彼此相互遠離。
一個聚簇主鍵索引對效能有所協助,但是也可能會導致嚴重的效能問題。因此,你應該仔細的思考聚簇,尤其是當你改變一個表的儲存引擎從InnoDB轉為其他的引擎。
聚簇資料有很多重要的優勢。
能使相關聯的資料距離很近。比如,當要實現一個mailbox,你可以通過user_id來聚簇,因此你能通過擷取硬碟上一小部分的頁來獲得一個單獨使用者的所有訊息。如果你不做聚簇,那麼每個訊息可能都需要各自的硬碟I/O。
資料訪問更加快速。一個聚簇索引在B-TREE上即儲存了索引也儲存了資料。因此從叢集索引擷取行一般要快於在非叢集索引中比較尋找。
使用覆蓋索引的語句可以使用包含在葉子節點的主索引值。
如果你設計表和語句的時候好好利用它們,對效能的提高大有協助。然而聚簇索引也有很多缺點
聚簇會大幅提高IO限制(IO-BOUND)工作量。如果資料在記憶體中的順序對資料的訪問並不是什麼問題的話,聚簇就不能帶來那麼多好處了。
插入的順序影響插入的速度。按照主鍵的速度插入行是最快的讀入資料到InnoDB表的方法。如果你沒有依照主鍵的順序來讀取資料,那麼在讀取很多資料之後,使用OPTIMIZE TABLE來重新組織表是個很好的主意。
更新聚簇索引的列消耗是非常高的。因為它迫使InnoDB把每一行移動到新的位置。
當新的一行插入或行的主鍵更新,這樣會導致聚簇索引的頁的分裂。當一個行的索引值決定了該行以及它所有的資料一定要放在一頁裡的時候,頁的分裂就發生了。因為儲存引擎必須把頁分為兩個來容納這個行。頁的分裂會導致表佔用更多的空間。
聚簇的表會降低檢索整張表的速度。尤其是在由於頁的分裂,造成行沒有被壓縮或者沒有連續的儲存的情況下,問題就很嚴重。
非聚簇索引可能比你所想像的要大很多。因為它們的葉子節點包含了它們引用行的主鍵列。
非聚簇索引的訪問需要兩個索引的尋找。
最後的一條可能有些困惑,為什麼非聚簇索引需要兩次索引的尋找?答案就隱藏在非聚簇索引所儲存的“行指標”上。記住一個葉子節點儲存的指標並不是引用行的物理地址,而是儲存了行的主索引值。
意思就是從非聚簇索引中尋找一行,首先,儲存引擎會在非聚簇索引找到葉子節點並且之後只用它所儲存的主索引值找到主鍵並且找到這個行。這是個雙重工作:兩個B-TREE的導航。