第五章 索引與算法(學習筆記)

  1. MySQL索引爲何使用B+樹數據結構,而不是B樹,紅黑樹等?

  1.1 二叉查找樹/二叉搜索樹數據庫

  下圖b爲二叉查找樹的典型結構,其特色爲:  數組

  • 任意節點左子樹不爲空,則左子樹的值均小於根節點的值
  • 任意節點右子樹不爲空,則右子樹的值均大於於根節點的值
  • 任意節點的左右子樹也分別是二叉查找樹
  • 沒有鍵值相等的節點

   

  侷限性: 若是數據排序好插入二叉查找樹時,會退化爲鏈表,此時時間複雜度爲O(n),最好的狀況是O(log n)。緩存

  1.2 AVL 樹(Adelson-Velsky and Landis Tree,平衡二叉樹)數據結構

  因爲二叉查找樹的侷限性,引入了AVL樹,在插入數據時,調整這棵樹,讓它的節點儘量均勻分佈。該樹節點左右子樹深度之差只能是0,1,-1. 若是插入數據後,不知足平衡性,會經過旋轉來實現平衡,具體的經過旋轉保證平衡的四種操做參見專欄https://www.bilibili.com/read/cv8748171。性能

  侷限性:旋轉是很是耗時的,故而AVL樹適合用於插入刪除次數比較少,但查找多的狀況。 優化

  1.3 紅黑樹.net

  紅黑樹是平衡二叉樹的一種變體。紅黑樹確保沒有一條路徑會比其它路徑長出兩倍。它是一種弱平衡二叉樹(因爲是弱平衡,能夠推出,相同的節點狀況下,AVL樹的高度低於紅黑樹),相對於要求嚴格的AVL樹來講,它的旋轉次數少,因此對於搜索、插入、刪除操做較多的狀況下,就用紅黑樹。Java8中,hashmap底層數據結構是基於數組和鏈表(紅黑樹 )來實現的,它之因此有至關快的查詢速度主要是由於它是經過計算散列碼來決定存儲的位置。經過key值計算hash碼值,將其傳遞進數組,若hash碼值衝突則採用鏈表鏈接,若該hashmap容量大於64且該鏈表長度大於8則將該鏈表轉化爲紅黑樹,若鏈表長度減小到6時候,則將紅黑樹轉化回鏈表。設計

  

  紅黑樹的特徵:3d

  • 每一個節點非紅即黑指針

  • 根節點是黑的

  • 每一個葉節點(葉節點即樹尾端NULL指針或NULL節點)都是黑的

  • 若是一個節點是紅的,那麼它的兩兒子都是黑的

  • 對於任意節點而言,其到葉子點樹NULL指針的每條路徑都包含相同數目的黑節點

  • 高度始終保持在h = logn紅黑樹的查找、插入、刪除的時間複雜度最壞爲O(log n)

  一樣的經過旋轉保證平衡的操做參見專欄  https://www.bilibili.com/read/cv8748171。

  爲何文件系統和數據庫常使用B樹及其變體B+樹存儲數據索引呢?

  紅黑樹每一個節點最多隻有兩個子節點,而B樹,B+樹是多路查找樹,分支多則層數變少。文件系統和數據庫的索引都是存放在磁盤上。磁盤IO次數與樹的深度相同,因爲磁盤特殊的結構,磁盤的存取速度每每是主存的幾百分之一,所以爲了提升效率,要儘可能減小磁盤I/O,即下降樹的深度。故而不使用紅黑樹。

  那能否設計爲無限多路樹,即退化爲有序數組?

  文件系統和數據庫的索引都是存在硬盤上的,而且若是數據量大的話,不必定能一次性加載到內存中。B+樹的多路存儲容許每次加載B+樹的一個結點,然後一步步往下找。

  1.4 B樹和B+樹

  B樹和B+樹的典型結構以下圖所示,關於其特性能夠參考博客 https://blog.csdn.net/herr_kun/article/details/80550652:

      

                           B樹

 

                                                                                                          B+ 樹

  這裏討論的是,爲何使用B+樹數據結構存儲索引(幫助MySQL高效獲取數據的數據結構)?

  • 磁盤讀寫代價更低    B+樹的內部結點並無指向關鍵字(root中的5,28,65)具體信息的指針。所以其內部結點相對於B樹更小。若是把全部同一內部結點的關鍵字存放在同一盤塊中,那麼盤塊所能容納的關鍵字數量也越多。一次性讀入內存中須要查找的關鍵字也就越多。相對來講IO讀寫次數也就下降了。
  • 遍歷數據更加方便     多數時候須要從數據庫中select多條數據,比如按照id進行排序後選100條。如果是多條的話,B樹需要做局部的中序遍歷,可能要跨層訪問。而B+樹由於所有數據都在葉子結點不用跨層,同時由於有鏈表結構,只需要找到首尾,通過鏈表橫向遍歷便可。爲了提升效率,要儘可能減小磁盤I/O。爲了達到這個目的,磁盤每每不是嚴格按需讀取,而是每次都會預讀,即便只須要一個字節,磁盤也會從這個位置開始,順序向後讀取必定長度的數據放入內存(不須要尋道時間,只需不多的旋轉時間)。這樣作的理論依據是計算機科學中著名的局部性原理:當一個數據被用到時,其附近的數據也一般會立刻被使用。
  • 查詢效率更加穩定     非終結點並非最終指向文件內容的結點,而只是葉子結點中關鍵字的索引。因此任何關鍵字的查找必須走一條從根結點到葉子結點的路。全部關鍵字查詢的路徑長度相同,致使每個數據的查詢效率至關 

  2. B+樹索引

  B+樹索引在數據庫中有一個特色是高扇出(指該模塊直接調用的下級模塊的個數)性,B+樹的高度通常都在2~4層,即查詢某一鍵值的行記錄時最多隻須要2到4次IO,查詢時間在0.02~0.04秒。B+樹索引能夠分爲彙集索引和輔助索引。

  2.1 彙集索引

  InnoDB存儲引擎是索引組織表,即表中數據按照主鍵順序存放。彙集索引(clustered index)就是按照每張表的主鍵構造一棵B+樹,同時葉子節點中存放的即爲整張表的行記錄數據,也將彙集索引的葉子節點稱爲數據頁。

  數據頁上存放的是完整的每行的記錄,而在非數據頁的索引頁中,存放的僅僅是鍵值及指向數據頁的偏移量。彙集索引的存儲並非物理上連續的,而是邏輯上連續的。

  

 

  彙集索引對於主鍵的排序查找和範圍查找速度很是快。若是查找主鍵某一範圍內的數據,經過葉子節點的上層中間節點就能夠找到頁的範圍,以後直接讀取數據頁便可。

   2.2 輔助索引

  對於輔助索引(secondary index),葉子節點並不包含行記錄的所有數據。葉子節點除了包含鍵值之外,每一個葉子節點中的索引行中還包含了一個書籤(bookmark,相應行數據的彙集索引鍵)。該書籤用來告訴InnoDB存儲引擎哪裏能夠找到與索引相對應的行數據。  當經過輔助索引來尋找數據時,InnoDB存儲引擎會遍歷輔助索引並經過葉級別的指針得到指向主鍵索引的主鍵,而後再經過主鍵索引來找到一個完整的行記錄。

  

 

   2.3 索引的建立與刪除

  

    

 

  2.4 Fast Index Creation

  MySQL數據庫對主鍵的添加和刪除過程爲:

  • 首先建立一張新的臨時表,表結構經過命令ALTER TABLE新定義的結構
  • 而後把原表中數據導入到臨時表
  • 接着刪除原表
  • 最後把臨時表重命名爲原來的表名

  對於輔助索引的建立,InnoDB存儲引擎會對建立索引的表加上一個S鎖。在建立過程當中,不須要重建表,故而速度更快。刪除輔助索引時,InnoDB存儲引擎只須要更新內部視圖,並將輔助索引的空間標記爲可用,同時刪除MySQL數據庫內部視圖上對該表的索引定義便可。

  3. Cardinality 值

  若是某個字段的取值範圍很廣,幾乎沒有重複,既屬於高選擇性,最適合用於B+樹索引。Cardinality值表示索引中不重複記錄數量的預估值。Cardinality/n_rows_in_table應儘量接近1.

  4. B+樹索引的使用

  4.1 聯合索引

  聯合索引對錶上的多個列進行索引。聯合索引也是一顆B+樹。

  對於查詢SELECT * FROM TABLE WHERE a=xxx and b=xxx/ SELECT * FROM TABLE WHERE a=xxx ORDER BY b 可使用(a,b)聯合索引。

  對於查詢SELECT * FROM TABLE WHERE a=xxx,也可使用聯合索引。

  可是對於SELECT * FROM TABLE WHERE b=xxx,不能使用聯合索引,由於b列不是排好序的。

   

 

  4.2 覆蓋索引

  InnoDB存儲引擎支持覆蓋索引,即從輔助索引中就能夠獲得查詢的記錄,而不須要查詢彙集索引中的記錄。使用覆蓋索引的一個好處是輔助索引不包含整行記錄的全部信息,故其大小要遠小於彙集索引,所以能夠減小大量的IO操做。

  對於InnoDB存儲引擎的輔助索引,因爲其包含了主鍵信息,所以其葉子節點存放的數據爲(primary key1, primary key2, ..., key 1, key2, ...)例如,下面語句可以使用一次覆蓋索引完成:

  SELECT key2 FROM table WHERE key1 = xxx

  SELECT primary key2, key2 FROM table WHERE key1=xxx

  SELECT primary key1, key2 FROM table WHERE key1=xxx

  SELECT primary key1, primary key2, key2 FROM table WHERE key1=xxx

  4.3 優化器選擇不使用索引的狀況

  在某些狀況下,當執行EXPLAIN命令進行SQL語句分析時,會發現優化器並無選擇索引去查找數據,而是經過掃描彙集索引,也便是直接進行全表掃描來獲得數據。這種狀況多發生於範圍查找、JOIN連接操做等狀況。例如:

  SELECT * FROM orderdetails WHERE orderid>10000 and orderid<102000;

  上述掃描能夠經過orderid輔助索引來進行掃描,可是實際優化器使用匯集索引進行掃描。

  這時由於用戶選取的數據是整行信息,而OderID索引查詢到指定數據,還須要進行一次書籤訪問來查找整行數據信息。雖然OderID索引中數據是順序存放的,可是再進行一次書籤查找的數據則是無序的,即磁盤上的離散讀操做。若是要求訪問的數據量很小,則優化器仍是會選擇輔助索引,可是當訪問的數據佔整個表中數據超過20%左右,優化器會選擇彙集索引來查找數據。可是若使用的磁盤是固態硬盤,隨機讀操做很快,能夠經過使用FORCE INDEX來強制使用某個索引:

  SELECT * FROM orderdetails FORCE INDEX(OrderID)WHERE orderid>10000 and orderid<102000;

  

 

   

 

   4.4 索引提示

  MySQL數據庫支持索引提示,顯式地告訴優化器使用哪一個索引,有兩種狀況可能用到INDEX HINT:

  • MySQL數據庫地優化器錯誤的選擇了某個索引,致使查詢很慢。這種狀況較爲少見,大多數時候優化器工做的很是有效
  • 某SQL語句能夠選擇的索引很是多,這時優化器進行各個執行路徑地成本分析開銷可能會大於SQL語句自己。DBA或開發人員分析最優的索引選擇,而後經過FORCE INDEX來強制優化器按指定索引完成查詢

  4.5 Multi-Range Read優化

  MRR地好處:

  • MRR使數據訪問變得較爲順序。在查詢輔助索引時,首先根據獲得的查詢結果,按照主鍵進行排序,並按照主鍵排序地順序進行書籤查找
  • 減小緩衝池中頁被替換地次數
  • 批量處理對鍵值的查詢操做

  對於InnoDB和MyISAM存儲引擎的範圍查詢和JOIN查詢操做,MRR工做方式爲:

  • 將查詢獲得的輔助索引鍵值存放於一個緩存中,這時緩存中的數據根據輔助索引鍵值排序的
  • 將緩存中的鍵值根據RowID進行排序
  • 根據RowID的排序順序來訪問實際地數據文件

   此外,MRR還能夠將某些範圍查詢,拆分爲鍵值對,以此來進行批量的數據查詢。例如:SELECT * FROM t WHERE key_part1 >= 1000 AND key_part1<2000 AND key_part2 = 10000.

  若沒有MRR,此時查詢類型爲range,SQL優化器會先將key_part1大於1000且小於2000的數據都取出,而後再按key_part2的條件進行過濾。

  若啓用了MRR優化,優化器會先將查詢條件進行拆分((1000,1000),(1001,1000)(1002,1000)...(1999,1000)),而後再進行數據查詢。

  4.6 Index Condition Pushdown (ICP) 優化

   在進行索引查詢時,首先根據索引查找記錄,而後判斷是否能夠進行WHERE條件的過濾,也就是將WHERE的部分過濾操做放在了存儲引擎層。在某些查詢下,能夠大大減小上層SQL層對記錄的索取,從而提升數據庫的總體性能。

  ICP優化支持range,ref, eq_ref, ref_or_null類型的查詢,當優化器選擇ICP優化時,可在執行計劃的列Extra看到Using Index Condition提示。

  5. hash索引

  hash索引進行字典類型的數據查詢(SELECT * FROM TABLE WHERE index_col='xxx')的時間複雜度爲O(1)。可是對於範圍查找卻無能爲力。

  6. 全文檢索

  6.1 概述

  下面的SQL語句能夠查詢博客內容以xxx開頭的文章,而且只要content添加了B+樹索引,就能利用索引進行快速查詢。

  SELECT * FROM blog WHERE content like 'xxx%'

  然而,更多的時候,用戶須要查詢的是博客內容包含單詞xxx的文章,即:

  SELECT * FROM blog WHERE content like '%xxx%'

  全文檢索(full-text search)是將存儲於數據庫中的整本書或整篇文章中的任意內容信息查找出來的技術。它能夠根據須要得到全文中有關章、節、段、句、詞等信息,也能夠進行各類統計和分析。

  6.2 倒排索引

  倒排索引也是一種索引結構。它在輔助表中存儲了單詞與單詞自身在一個或多個文檔中所在位置之間的映射。有兩種表現形式:

  • inverted file index {單詞,單詞所在文檔ID}
  • full inverted index {單詞,(單詞所在文檔的ID,在文檔中的位置)}

  

  

  

 

   6.3 InnoDB 全文檢索

  在InnoDB存儲引擎中,將(documentid, position)視爲一個ilist。所以,在全文檢索的表中,有兩個列,一個是Word段,一個是ilist段,這個表稱爲Auxiliary Table(輔助表),存放於磁盤上。FTS Index Cache(全文檢索索引緩存)是一個紅黑樹結構,用來提升全文檢索的性能。參數innodb_ft_cache_size用來控制FTS Index Cache的大小,默認值是32M。當該緩存滿時,會將其中的(word,ilist)分詞信息同步到磁盤的Auxiliary Table中。增大該參數能夠提升全文檢索的性能,可是在宕機時,未同步到磁盤中的索引信息須要更多時間來回復。

  stopword列表表示該列表中的Word不須要對其進行索引分詞操做,例如單詞the。

  InnoDB 全文檢索存在如下限制:

  • 每張表只能有一個全文檢索的索引
  • 由多列組合而成的全文檢索的索引列必須使用相同的字符集與排序規則
  • 不支持沒有單詞界定符的語言,如中文,日語,韓語等

  6.4 全文檢索

  語法爲:

  

 

  正負表示這個單詞必定出現或者必定不存在。

    

相關文章
相關標籤/搜索