從資料結構(樹)深入理解資料庫的索引
樹
二叉樹
性質: 1. 在非空二叉樹中,第層的結點總數不超過, ; 2. 深度為的二叉樹最多有個結點(h>=1),最少有h個結點; 3. 對於任意一棵二叉樹,如果其葉結點數為N0,而度數為2的結點總數為N2,則N0=N2+1; 4. 具有個結點的完全二叉樹的深度為; 5. 給定N個節點,能構成h(N)種不同的二叉樹,其中h(N)為卡特蘭數的第N項,h(n)=C(2*n, n)/(n+1)。
滿二叉樹
除葉子結點外的所有結點均有兩個子結點。節點數達到最大值,所有葉子結點必須在同一層上。
性質: 1. 一顆樹深度為h,最大層數為k,深度與最大層數相同,k=h; 2. 葉子數為2h; 3. 第k層的結點數是:; 4. 總結點數是:,且總節點數一定是奇數。
完全二叉樹
若設二叉樹的深度為h,除第 h 層外,其它各層 (1~(h-1)層) 的結點數都達到最大個數,第h層所有的結點都連續集中在最左邊,這就是完全二叉樹。
完全二叉樹是效率很高的資料結構,堆是一種完全二叉樹或近似完全二叉樹。
二叉查詢樹(BST)
二叉排序樹(Binary Sort Tree)或二叉搜尋樹。
二叉排序樹或者是一棵空樹,或者是具有下列性質的二叉樹: 1. 若左子樹不空,則左子樹上所有結點的值均小於它的根結點的值; 2. 若右子樹不空,則右子樹上所有結點的值均大於或等於它的根結點的值; 3. 左、右子樹也分別為二叉排序樹; 4. 沒有鍵值相等的節點。 5. 對二叉查詢樹進行中序遍歷,即可得到有序的數列。 6. 插入和查詢的時間複雜度均為O(logn),但是在最壞的情況下仍然會有O(n)的時間複雜度
平衡二叉樹(AVL)
AVL樹解決了BST退化成連結串列的問題。 它是一 棵空樹或它的左右兩個子樹的高度差的絕對值不超過1,並且左右兩個子樹都是一棵平衡二叉樹。 在平衡二叉搜尋樹中,插入,查詢,刪除的時間複雜度最好情況和最壞情況都維持在O(logN)。但是頻繁旋轉會使插入和刪除犧牲掉O(logN)左右的時間,不過相對二叉查詢樹來說,時間上穩定了很多。 平衡二叉樹的常用演算法有紅黑樹、AVL樹等。
紅黑樹
紅黑樹與AVL樹相比,紅黑樹並不是高度平衡的,它放棄了高度平衡的特性而只追求部分平衡,這種特性降低了插入、刪除時對樹旋轉的要求,從而提升了樹的整體效能。
紅黑樹是每個節點都帶有顏色屬性的二叉查詢樹,顏色為紅色或黑色。 性質: 1. 節點是紅色或黑色。 2. 根是黑色。 3. 所有葉子都是黑色(葉子是NIL節點)。 4. 每個紅色節點必須有兩個黑色的子節點。(從每個葉子到根的所有路徑上不能有兩個連續的紅色節點。) 5. 從任一節點到其每個葉子的所有簡單路徑都包含相同數目的黑色節點。
B樹與B+樹
B樹的非葉子節點包含關鍵字和指向關鍵字具體資訊的指標
B+樹是B樹的變體,二者的異同: 1. 所有的非終端結點可以看成是索引部分,結點中僅含有其子樹根結點中最大(或最小)關鍵字。 (而B 樹的非終節點也包含需要查詢的有效資訊) 2. 所有的葉子結點中包含了全部關鍵字的資訊,及指向含有這些關鍵字記錄的指標 (而B 樹的葉子節點並沒有包括全部需要查詢的資訊)
這裡“B樹的葉子節點不包含任何關鍵字資訊”,關鍵是把什麼當作葉子節點,有兩種解讀: - 葉子節點是存在的且非空,內含關係的全部資訊,但是不具有孩子節點了。所以,“關鍵字資訊”意為孩子節點了。 - B樹的葉子節點為null, 那麼葉子節點的確是不具有任何關鍵字資訊。
- 葉子結點本身依關鍵字的大小自小而大的順序連結(B樹則沒有)。
MySQL中常見的索引型別
FULLTEXT
全文檢索,MyISAM引擎支援, InnoDB(version 5.5之後)開始支援。 可以在CHAR,VARCHAR,TEXT列上建立全文索引。
先將資料填充到表中,再建立FULLTEXT索引的效率要比先建立FULLTEXT索引再匯入資料要快。
它的出現是為了解決WHERE name LIKE “%word%”這類針對文字的模糊查詢效率較低的問題。在沒有全文索引之前,這樣一個查詢語句是要進行遍歷資料表操作的,可見,在資料量較大時是極其的耗時的,如果沒有非同步IO處理,程序將被挾持,很浪費時間。
HASH
雜湊索引,MyISAM,InnoDB均不支援,Memory引擎支援。但InnoDB支援自適應索引(InnoDB 儲存引擎會監控對錶上索引的查詢,如果觀察到建立雜湊索引可以帶來速度的提升,則建立雜湊索引)。
可以為一列或幾列建立hash索引時,將這一列或幾列的值通過一定的演算法計算出一個hash值。這個雜湊值對應一行或多行資料(hash衝突)。將 Hash運算結果的 Hash 值和所對應的行指標資訊存放於一個 Hash 表中。hash索引可以一次定位,不需要像樹形索引那樣逐層查詢,因此具有極高的效率。
hash索引 vs. B+樹索引 1. hash索引只能等值過濾(=, IN, <=, >=),而不需要使用範圍查詢。 2. hash索引不能排序操作 hash值大小與鍵值大小沒有關係 3. hash索引不能利用部分索引鍵查詢 對於組合索引,是將多列的資料組合在一起計算hash值,而不是每列資料單獨計算hash值在拼接在一起的。通過組合索引的前一個或其中幾個索引鍵來查詢時,hash索引會失效。 4. hash 索引不能避免表掃描。 由於不同索引鍵存在相同 Hash 值,所以即使取滿足某個 Hash 鍵值的資料的記錄條數,也無法從 Hash 索引中直接完成查詢,還是要通過訪問表中的實際資料進行相應的比較,並得到相應的結果 5. Hash 索引遇到大量Hash值相等的情況後效能並不一定就會比B-Tree索引高。
B+樹
樹索引,MyISAM和InnoDB預設索引。 MyISAM的索引的葉子節點存放的是指標,指向資料檔案對應的資料行。 InnoDB的索引的葉子節點存放的資料。
InnoDB索引和MyISAM索引的區別: 一是主索引的區別,InnoDB的資料檔案本身就是索引檔案。而MyISAM的索引和資料是分開的。 二是輔助索引的區別:InnoDB的輔助索引data域儲存相應記錄主鍵的值而不是地址。而MyISAM的輔助索引和主索引沒有多大區別。
InnoDB
- 主鍵索引 而在InnoDB中,表資料檔案本身就是按B+Tree組織的一個索引結構,這棵樹的葉節點data域儲存了完整的資料記錄。這個索引的key是資料表的主鍵,因此InnoDB表資料檔案本身就是主索引。
- 輔助索引 InnoDB的所有輔助索引都引用主鍵作為data域。輔助索引搜尋需要檢索兩遍索引:首先檢索輔助索引獲得主鍵,然後用主鍵到主索引中檢索獲得記錄。 例如,下圖為定義在Col3上的一個輔助索引:
MyISAM
MyISAM索引檔案和資料檔案是分離的,索引檔案僅儲存資料記錄的地址。
主鍵索引 這裡設表一共有三列,假設我們以Col1為主鍵,圖myisam1是一個MyISAM表的主索引(Primary key)示意。可以看出MyISAM的索引檔案僅僅儲存資料記錄的地址。
輔助索引 在MyISAM中,主索引和輔助索引(Secondary key)在結構上沒有任何區別,只是主索引要求key是唯一的,而輔助索引的key可以重複。如果我們在Col2上建立一個輔助索引,則此索引的結構如下圖所示:
進階面試題
MySQL資料庫中的索引為什麼能夠加快查詢速度?
- MySQL中的索引是B+樹,使用B+樹查詢資料的時間複雜度度為
O(logN)
. - 索引中的葉子節點是連在一起的,這會加快範圍查詢。 如下圖所示(InnoDB),查詢訂單號在18到49的全部訂單。首先通過從根節點開始查詢,找到訂單號為18的葉子節點,然後從當前的葉子節點向後查詢,直到找到訂單號為49的訂單才停止查詢。遍歷的葉子節點皆作為查詢結果返回。
- MySQL中的索引是B+樹,使用B+樹查詢資料的時間複雜度度為
倘若將索引改成B樹,BST,AVL, 或紅黑樹,可以嗎?
- B+樹 vs. B樹
- 減少磁碟訪問次數 B樹的非葉子節點包含了關鍵字和指向關鍵字具體資訊的指標,而葉子節點不含任何關鍵資訊;而B+樹非葉子節點不含指向關鍵字的指標,只是葉子節點關鍵字的索引,葉子節點才包含指向關鍵字資訊。因此,B+樹內部節點比B樹小。如果把所有同一內部結點的關鍵字存放在同一盤塊中,那麼盤塊所能容納的關鍵字數量也越多。一次性讀入記憶體中的需要查詢的關鍵字也就越多。相對來說IO讀寫次數也就降低了。
- 查詢效率更加穩定 B樹搜尋可能在非葉子節點結束,也靠近根節點的記錄查詢時間越短,而查詢不存在的關鍵字,仍需要從根節點走到葉子節點才能確定。而在B+樹中,任何關鍵字的查詢都必須走一條從根節點到葉節點的路徑,查詢路徑相同,導致每個關鍵字查詢效率相當。儘管B+樹找到一個記錄所需的比較次數要比B-樹多,但是一次磁碟訪問的時間相當於成百上千次記憶體比較的時間,因此實際中B+樹的效能可能還會好些。
- B樹不支援順序遍歷 B+樹的葉子節點使用指標順序連線在一起,只要遍歷葉子節點就可以實現整棵樹的遍歷。而資料庫中的範圍查詢是非常頻繁的,而B樹順序遍歷效率比較低。
- 二分查詢(BST)樹 BST樹的深度不可控,最糟糕的情況是BST變成單向連結串列,深度較大的BST不但會增加磁碟IO,也會影響查詢效率。
- 平衡二叉樹(AVL) 與BST相比,雖然能夠保證絕對平衡,保證了查詢效率,但是維護AVL樹平衡的開銷比較大,尤其對於插入刪除頻繁的業務場景而言,絕對平衡帶來的查詢效率優勢就不再那麼明顯了。
- 紅黑樹 紅黑樹雖然解決了AVL樹維護平衡開銷比較大的問題,但是紅黑樹跟B樹一樣,非葉子節點也會儲存資料,同樣面臨跟B樹同樣的問題,磁碟IO負擔大。此外,紅黑樹分支因子比較少,深度比B+樹更深,那麼在查詢時頁缺失的概率也隨之增大。
- B+樹 vs. B樹
雜湊(hash)比樹(tree)更快,索引結構為什麼要設計成樹型? 索引設計成樹形,和SQL的需求相關。 對於這樣一個單行查詢的SQL需求:
select * from t where name=”shenjian”;
確實是雜湊索引更快,因為每次都只查詢一條記錄。所以,如果業務需求都是單行訪問,例如passport,確實可以使用雜湊索引。 但是對於排序查詢的SQL需求:分組:group by; 排序:order by; 比較:<、> 雜湊型的索引,時間複雜度會退化為O(n),而樹型的“有序”特性,依然能夠保持O(log(n)) 的高效率。