詳解聚簇索引

1、聚族索引的構造緩存

    聚簇索引並非一種單獨的索引類型,而是一種數據存儲方式。具體的細節依賴於其實現方式,但InnoDB的聚族索引實際上在同一個結構中保存了B-Tree索引和數據行。當表有聚族索引時,它的數據行存放在索引的葉子頁中。術語「聚族」表示數據行和相鄰的鍵值緊湊的存儲在一塊兒。由於沒法同時把數據行放在兩個不一樣的地方,因此一個表只能有一個聚族索引。併發

    由於是存儲引擎負責實現索引,所以不是全部的存儲引擎都支持聚族索引。這裏咱們主要關注InnoDB,可是這裏討論的原理對於任何支持聚族索引的存儲引擎都是適用的。高併發

    下面展現了聚族索引中的記錄是如何存放的。注意到,葉子頁包含了行的所有數據,可是節點頁只包含了索引列。性能


    在InnoDB中經過主鍵彙集數據,這也就是說上圖中「被索引的列」就是主鍵列。若是沒有定義主鍵,InnoDB會選擇一個惟一的非空索引代替。若是沒有這樣的索引,InnoDB會隱式定義一個主鍵來做爲聚族索引。InnoDB只彙集在同一個頁面中的記錄。包含相鄰鍵的頁面可能會相距甚遠。測試

    聚族主鍵可能對性能有幫助,但也可能致使嚴重的性能問題。因此須要仔細的考慮聚族索引,尤爲是將表的引擎從InnoDB改爲其餘引擎的時候。優化

2、聚族索引的優勢設計

  • 能夠把相關數據保存在一塊兒。例如實現電子郵件時,能夠根據用戶ID來彙集數據,這樣只須要從磁盤讀取少數的數據頁就能獲取某個用戶的所有郵件。若是沒有使用聚族索引,則每封郵件均可能致使一次磁盤I/O;
  • 數據訪問更快。聚族索引將索引和數據保存在同一個B-Tree中,所以從聚族索引中獲取數據一般比在非聚族索引中查找更快。
  • 使用覆蓋索引掃描的查詢能夠直接使用節點中的主鍵值。

3、聚族索引的缺點代理

  • 聚簇數據最大限度的提升了I/O密集型應用的性能,但若是數據所有都放在內存中,則訪問的順序就沒有那麼重要了,聚簇索引也就沒有那麼優點了;
  • 插入速度嚴重依賴於插入順序。按照主鍵的順序插入是加載數據到InnoDB表中速度最快的方式。但若是不是按照主鍵順序加載數據,那麼在加載完成後最好使用OPTIMIZE TABLE命令從新組織一下表。
  • 更新聚簇索引列的代價很高,由於會強制InnoDB將每一個被更新的行移動到新的位置。
  • 基於聚簇索引的表在插入新行,或者主鍵被更新致使須要移動行的時候,可能面臨「頁分裂」的問題。當行的主鍵值要求必須將這一行插入到某個已滿的頁中時,存儲引擎會將該頁分裂成兩個頁面來容納該行,這就是一次分裂操做。頁分裂會致使表佔用更多的磁盤空間。
  • 聚簇索引可能致使全表掃描變慢,尤爲是行比較稀疏,或者因爲頁分裂致使數據存儲不連續的時候。
  • 二級索引(非聚簇索引)可能比想象的要更大,由於在二級索引的葉子節點包含了引用行的主鍵列。
  • 二級索引訪問須要兩次索引查找,而不是一次。

    備註:有關二級索引須要兩次索引查找的問題?答案在於二級索引中保存的「行指針」的實質。要記住,二級索引葉子節點保存的不是指向行的物理位置的指針,而是行的主鍵值。這意味着經過二級索引查找行,存儲引擎須要找到二級索引的葉子節點得到對應的主鍵值,而後根據這個值去聚簇索引中查找到對應的行。這裏作了重複的工做:兩次B-Tree查找而不是一次。對於InnoDB,自適應哈希索引可以減小這樣的重複工做。指針

4、InnoDB和MyISAM的數據分佈對比blog

    聚簇索引和非聚簇索引的數據分佈有區別,以及對應的主鍵索引和二級索引的數據分佈也有區別。

一、MyISAM的主鍵索引和二級索引

    MyISAM的數據分佈很是簡單,MyISAM按照數據插入的順序存儲在磁盤上。在行的旁邊顯示了行號,從0開始遞增。由於行是定長的,因此MyISAM能夠從表的開頭跳過所需的字節找到須要的行。這種分佈方式很容易建立索引。而且,MyISAM中主鍵索引和其餘索引在結構上沒有什麼不一樣。主鍵索引就是一個名爲primary的惟一非空索引。以下圖:

一、MyISAM數據行分佈


二、MyISAM的主鍵分佈


三、MyISAM上的其餘索引分佈

二、InnoDB的主鍵索引和二級索引

    InnoDB的數據分佈,由於InnoDB支持聚簇索引,索引使用很是不一樣的方式存儲這樣的數據,以下圖:


    仔細查看,會注意到該圖顯示了整個表,而不是隻有索引。由於在InnoDB中,聚簇索引「就是」表,因此不像MyISAM那樣須要獨立的行存儲。聚簇索引的每一個葉子節點都包含了主鍵值、事務ID、用於事務和MVCC的回滾指針以及全部的剩餘列。若是主鍵是一個列前綴索引,InnoDB也會包含完整的主鍵列和剩下的其餘列。

    還有一點和MyISAM的不一樣是,InnoDB的二級索引和聚簇索引很不相同。InnoDB二級索引的葉子節點中存儲的不是「行指針」,而是主鍵值,並以此做爲指向行的「指針」。這樣的策略減小了當出現航移動或者數據頁分裂時二級索引的維護工做。使用主鍵值看成指針會讓二級索引佔用更多的空間,換來的好處是,InnoDB在移動行時無需更新二級索引中的這個「指針」。下圖就是InnoDB的二級索引:


三、MyISAM和InnoDB的對比


5、在InnoDB表中按主鍵順序插入行

    若是正在使用InnoDB表而且沒有什麼數據須要彙集,那麼能夠定義一個代理鍵做爲主鍵,這種主鍵的數據應該和應用無關,最簡單的方法是使用auto_increment自增列。這樣能夠保證數據行是按照順序寫入,對於根據主鍵作關聯操做的性能也會更好。

    最好避免隨機的聚簇索引,特別對於I/O密集型的應用。例如,從性能的角度考慮,使用UUID做爲聚簇索引會很糟糕:它使得聚簇索引的插入變得徹底隨機,這是最壞的狀況,使得數據沒有任何彙集特性。經過測試,向UUID主鍵插入行不只花費的時間更長,並且索引佔用的空間也更大。這一方面是因爲主鍵字段更長,另外一方面毫無疑問是因爲頁分裂和碎片致使的。

    這是因爲當主鍵的值是順序的,則InnoDB把每一條記錄都存儲在上一條記錄的後面。當達到頁的最大填充因子時(InnoDB默認的最大填充因子是頁大小的15/16,留出的部分空間用於之後修改),下一條記錄就會寫入新的頁中。一旦數據按照這樣順序的方式加載,主鍵頁就會近似於被順序的記錄填滿,這也是所指望的結果。

    而當採用UUID的聚簇索引的表插入數據,由於新行的主鍵值不必定比以前的插入值大,因此InnoDB沒法簡單的老是把新行插入到索引的最後,而是須要爲新的行尋找合適的位置----一般是已有數據的中間位置----而且分配空間。這會增長不少額外的工做,並致使數據分佈不夠優化。下面是總結的一些缺點:

  • 寫入目標頁可能已經刷到磁盤上並從緩存中移除,或者是尚未被加載到緩存中,InnoDB在插入以前不得不先找到並從磁盤讀取目標頁到內存中,這將致使大量的隨機I/O;
  • 由於寫入是亂序的,InnoDB不得不頻繁的作頁分裂操做,以便爲新的行分配空間。頁分裂會致使移動大量數據,一次插入最少須要修改三個頁而不是一個頁。
  • 因爲頻繁的頁分裂,頁會變得稀疏並被不規則的填充,因此最終數據會有碎片。
  • 把這些隨機值載入到聚簇索引之後,須要作一次optimize table來重建表並優化頁的填充。

注意:順序主鍵也有缺點:對於高併發工做負載,在InnoDB中按主鍵順序插入可能會形成明顯的爭用。主鍵的上界會成爲「熱點」。由於全部的插入都發生在這裏,因此併發插入可能致使間隙鎖競爭。另外一個熱點多是auto_increment鎖機制;若是遇到這個問題,則可能須要考慮從新設計表或者應用,或者更改innodb_autonc_lock_mode配置。

相關文章
相關標籤/搜索