1. 程式人生 > >MySQL索引 B+樹索引和雜湊索引的區別

MySQL索引 B+樹索引和雜湊索引的區別

備註:先說下,在MySQL文件裡,實際上是把B+樹索引寫成了BTREE,例如像下面這樣的寫法:

  1. CREATE TABLE t(
  2. aid int unsigned not null auto_increment,
  3. userid int unsigned not null default 0,
  4. username varchar(20) not null default ‘’,
  5. detail varchar(255) not null default ‘’,
  6. primary key(aid),
  7. unique key(uid) USING BTREE,
  8. key (username(12)) USING
    BTREE — 此處 uname 列只建立了最左12個字元長度的部分索引
  9. )engine=InnoDB;
一個經典的BTREE索引資料結構見下圖:

1、B-Tree索引 

    B-Tree 索引是 MySQL 資料庫中使用最為頻繁的索引型別,除了 Archive 儲存引擎之外的其他所有的儲存引擎都支援 B-Tree 索引。不僅僅在 MySQL 中是如此,實際上在其他的很多資料庫管理系統中B-Tree 索引也同樣是作為最主要的索引型別,這主要是因為B-Tree 索引的儲存結構在資料庫的資料檢索中有非常優異的表現。 
    一般來說, MySQL 中的 B-Tree 索引的物理檔案大多都是以 Balance Tree 的結構來儲存的,也就是所有實際需要的資料都存放於 Tree 的 Leaf Node ,而且到任何一個 Leaf Node 的最短路徑的長度都是完全相同的,所以我們大家都稱之為 B-Tree 索引當然,可能各種資料庫(或 MySQL 的各種儲存引擎)在存放自己的 B-Tree 索引的時候會對儲存結構稍作改造。如 Innodb 儲存引擎的 B-Tree 索引實際使用的儲存結構實際上是 B+Tree ,也就是在
B-Tree 資料結構的基礎上做了很小的改造,在每一個Leaf Node 上面出了存放索引鍵的相關資訊之外,還儲存了指向與該 Leaf Node 相鄰的後一個 LeafNode 的指標資訊,這主要是為了加快檢索多個相鄰 Leaf Node 的效率考慮。 
    B+樹是一個平衡的多叉樹,從根節點到每個葉子節點的高度差值不超過1,而且同層級的節點間有指標相互連結。
    在B+樹上的常規檢索,從根節點到葉子節點的搜尋效率基本相當,不會出現大幅波動,而且基於索引的順序掃描時,也可以利用雙向指標快速左右移動,效率非常高。
    因此,B+樹索引被廣泛應用於資料庫、檔案系統等場景。順便說一下,xfs檔案系統比ext3/ext4效率高很多的原因之一就是,它的檔案及目錄索引結構全部採用B+樹索引,而ext3/ext4的檔案目錄結構則採用Linked list, hashed B-tree、Extents/Bitmap等索引資料結構,因此在高I/O壓力下,其IOPS能力不如xfs。
詳細可參見:
   

https://en.wikipedia.org/wiki/Ext4
    https://en.wikipedia.org/wiki/XFS
    在 Innodb 儲存引擎中,存在兩種不同形式的索引,一種是 Cluster 形式的主鍵索引( Primary Key ),另外一種則是和其他儲存引擎(如 MyISAM 儲存引擎)存放形式基本相同的普通 B-Tree 索引,這種索引在 Innodb 儲存引擎中被稱為 Secondary Index。下面我們通過圖示來針對這兩種索引的存放 形式做一個比較。 

    圖示中左邊為 Clustered 形式存放的 Primary Key ,右側則為普通的 B-Tree 索引。兩種 Root Node 和 Branch Nodes 方面都還是完全一樣的。而 Leaf Nodes 就出現差異了。在 Prim中, Leaf Nodes 存放的是表的實際資料,不僅僅包括主鍵欄位的資料,還包括其他欄位的資料據以主鍵值有序的排列。而 Secondary Index 則和其他普通的 B-Tree 索引沒有太大的差異,Leaf Nodes 出了存放索引鍵 的相關資訊外,還存放了 Innodb
的主鍵值。 
    所以,在 Innodb 中如果通過主鍵來訪問資料效率是非常高的,而如果是通過 Secondary Index 來訪問資料的話, Innodb 首先通過 Secondary Index 的相關資訊,通過相應的索引鍵檢索到 Leaf Node之後,需要再通過 Leaf Node 中存放的主鍵值再通過主鍵索引來獲取相應的資料行。MyISAM 儲存引擎的主鍵索引和非主鍵索引差別很小,只不過是主鍵索引的索引鍵是一個唯一且非空 的鍵而已。而且 MyISAM 儲存引擎的索引和 Innodb 的 Secondary
Index 的儲存結構也基本相同,主要的區別只是 MyISAM 儲存引擎在 Leaf Nodes 上面出了存放索引鍵資訊之外,再存放能直接定位到 MyISAM 資料檔案中相應的資料行的資訊(如 Row Number ),但並不會存放主鍵的鍵值資訊

2、hash索引

雜湊索引的示意圖則是這樣的:


    Hash 索引結構的特殊性,其檢索效率非常高,索引的檢索可以一次定位,不像B-Tree 索引需要從根節點到枝節點,最後才能訪問到頁節點這樣多次的IO訪問,所以 Hash 索引的查詢效率要遠高於 B-Tree 索引。

   可能很多人又有疑問了,既然 Hash 索引的效率要比 B-Tree 高很多,為什麼大家不都用 Hash 索引而還要使用 B-Tree 索引呢?任何事物都是有兩面性的,Hash 索引也一樣,雖然 Hash 索引效率高,但是 Hash 索引本身由於其特殊性也帶來了很多限制和弊端,主要有以下這些。
  1).Hash 索引僅僅能滿足”=”,”IN”和”<=>”查詢,不能使用範圍查詢。
    由於 Hash 索引比較的是進行 Hash 運算之後的 Hash 值,所以它只能用於等值的過濾,不能用於基於範圍的過濾,因為經過相應的 Hash 演算法處理之後的 Hash 值的大小關係,並不能保證和Hash運算前完全一樣。
  2).Hash 索引無法被用來避免資料的排序操作。
    由於 Hash 索引中存放的是經過 Hash 計算之後的 Hash 值,而且Hash值的大小關係並不一定和 Hash 運算前的鍵值完全一樣,所以資料庫無法利用索引的資料來避免任何排序運算;
  3).Hash 索引不能利用部分索引鍵查詢。
    對於組合索引,Hash 索引在計算 Hash 值的時候是組合索引鍵合併後再一起計算 Hash 值,而不是單獨計算 Hash 值,所以通過組合索引的前面一個或幾個索引鍵進行查詢的時候,Hash 索引也無法被利用。
  4).Hash 索引在任何時候都不能避免表掃描。
    前面已經知道,Hash 索引是將索引鍵通過 Hash 運算之後,將 Hash運算結果的 Hash 值和所對應的行指標資訊存放於一個 Hash 表中,由於不同索引鍵存在相同 Hash 值,所以即使取滿足某個 Hash 鍵值的資料的記錄條數,也無法從 Hash 索引中直接完成查詢,還是要通過訪問表中的實際資料進行相應的比較,並得到相應的結果。
  5).Hash 索引遇到大量Hash值相等的情況後效能並不一定就會比B-Tree索引高。
    對於選擇性比較低的索引鍵,如果建立 Hash 索引,那麼將會存在大量記錄指標資訊存於同一個 Hash 值相關聯。這樣要定位某一條記錄時就會非常麻煩,會浪費多次表資料的訪問,而造成整體效能低下
 
     簡單地說,雜湊索引就是採用一定的雜湊演算法,把鍵值換算成新的雜湊值,檢索時不需要類似B+樹那樣從根節點到葉子節點逐級查詢,只需一次雜湊演算法即可立刻定位到相應的位置,速度非常快。
從上面的圖來看,B+樹索引和雜湊索引的明顯區別是:
    1).如果是等值查詢,那麼雜湊索引明顯有絕對優勢,因為只需要經過一次演算法即可找到相應的鍵值;當然了,這個前提是,鍵值都是唯一的。如果鍵值不是唯一的,就需要先找到該鍵所在位置,然後再根據連結串列往後掃描,直到找到相應的資料;
    2).從示意圖中也能看到,如果是範圍查詢檢索,這時候雜湊索引就毫無用武之地了,因為原先是有序的鍵值,經過雜湊演算法後,有可能變成不連續的了,就沒辦法再利用索引完成範圍查詢檢索;
    3).同理,雜湊索引也沒辦法利用索引完成排序,以及like ‘xxx%’ 這樣的部分模糊查詢(這種部分模糊查詢,其實本質上也是範圍查詢);
    4).雜湊索引也不支援多列聯合索引的最左匹配規則;
    5).B+樹索引的關鍵字檢索效率比較平均,不像B樹那樣波動幅度大,在有大量重複鍵值情況下,雜湊索引的效率也是極低的,因為存在所謂的雜湊碰撞問題。

    在MySQL中,只有HEAP/MEMORY引擎表才能顯式支援雜湊索引(NDB也支援,但這個不常用),InnoDB引擎的自適應雜湊索引(adaptive hash index)不在此列,因為這不是建立索引時可指定的。
    還需要注意到:HEAP/MEMORY引擎表在mysql例項重啟後,資料會丟失。
    通常,B+樹索引結構適用於絕大多數場景,像下面這種場景用雜湊索引才更有優勢:
    在HEAP表中,如果儲存的資料重複度很低(也就是說基數很大),對該列資料以等值查詢為主,沒有範圍查詢、沒有排序的時候,特別適合採用雜湊索引
例如這種SQL:
SELECT … FROM t WHERE C1 = ?; — 僅等值查詢
在大多數場景下,都會有範圍查詢、排序、分組等查詢特徵,用B+樹索引就可以了。