mysql技術內幕(五)索引與算法

#索引與算法

5.1 InnoDB 存儲引擎索引概述

InnoDB 存儲引擎支持以下幾種常見的索引

  • B+ 樹索引
  • 全文索引
  • 哈希索引
       innodb 存儲引擎支持的哈希索引是自適應的, innodb 存儲引擎會根據表的使用情況自動爲表生成哈希索引,不能人爲干預是否在一張表中生成哈希索引。
       B+ 樹索引就是傳統意義上的索引.這是目前關係型數據庫系統中查找最爲常用最爲有效的索引。B+ 樹索引的構造類似於二叉樹,根據鍵值(Key Value) 快速找到數據。
      B+ 樹索引並不能找到一個給定鍵值的具體行。B+ 樹索引能找到的只是被查找數據行所在的頁。然後數據庫通過把頁讀入到內存, 再在內存中進行查找,最後得到要在找的數據。

5.2 數據結構與算法

5.2.1 二分查找法

5.2.2 二叉查找樹和平衡二叉樹

  B+ 樹是通過二叉查找樹,再由平衡二叉樹,若想最大性能地構造一棵二叉查找樹, 需要這棵二叉查找樹是平衡的,從而引出一一平衡二叉樹,或稱爲 AVL 樹。平衡二叉樹的查找性能是比較高的,但不是最高的, 只是接近最高性能。最好的性能需要建立一棵最優二叉樹,但是最優二叉樹的建立和維護需要大量的操作。平衡二叉樹的查詢速度的確很快,但是維護一棵平衡二叉樹的代價是非常大的。

5.3 B+ 樹

   B+ 樹由B 樹和索引順序訪問方法(ISAM)演化而來, B+ 樹是爲磁盤或其他直接存取輔助設備設計的一種平衡查找樹。在B+ 樹中,所有記錄節點都是按鍵值的大小順序存放在同一層的葉子節點上,由各葉子節點指針進行連接。先來看一個B+ 樹,其高度爲2 ,每頁可存放4 條記錄,扇出(fan out) 爲5,所有記錄都在葉子節點上,並且是順序存放的,如果用戶從最左邊的葉子節點開始順序遍歷,可以得到所有鍵值的順序排序: 5 、10 、15 、20 、25 、30 、50 、55 、60 、65 、75 、80 、85 、90 。
在這裏插入圖片描述

5.3.1 B+ 樹的插入操作

  B+ 樹的插入必須保證插入後葉子節點中的記錄依然排序,同時需要考慮插入到B+樹的三種情況,每種情況都可能會導致不同的插入算法。
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
  旋轉發生在Leaf Page 已經滿,但是其的左右兄弟節點沒有滿的情況下。這時B+ 樹並不會急於去做拆分頁的操作,而是將記錄移到所在頁的兄弟節點上。

5.3.2 B+ 樹的刪除操作

  B+ 樹使用填充因子(fill factor) 來控制樹的刪除變化, 50% 是填充因子可設的最小值。B+ 樹的刪除操作同樣必須保證刪除後葉子節點中的記錄依然排序,同插入一樣,B+ 樹的刪除操作同樣需要考慮以下表5-2 中的三種情況,與插入不同的是,刪除根據填充因子的變化來衡量。
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

5.4 B+ 樹索引

  B+ 樹索引的本質就是B+ 樹在數據庫中的實現。但是B+ 索引在數據庫中有一個特點是高扇出性,因此在數據庫中, B+樹的高度一般都在2 - 4 層,這也就是說查報一鍵值的行記錄時最多隻2-4次。
  數據庫中的B+ 樹索引可以分爲聚集索引(clustered inex) 和輔助索引(secondary index) ,但是不管是聚集還是輔助的索引,其內部都是B+ 樹的,即高度平衡的,葉子節點存放着所有的數據。聚集索引與輔助索引不同的是,葉子節點存放的是否是一整行的信息。

5.4.1 聚集索引

  InnoDB 存儲引肇表是索引組織表,即表中數據按照主鍵順序,存放。而聚集索引(clustered index) 就是按照每張表的主鍵構造一棵B+ 樹,同時葉子節點中存放的即爲整張表的行記錄數據,也將聚集索引的葉子節點稱爲數據頁。聚集索引的這個特性決定了索引組織表中數據也是索引的一部分。同B+ 樹數據結構一樣,每個數據頁都通過一個雙向鏈表來進行鏈接。
  由於實際的數據頁只能按照一棵B+ 樹進行排序,因此每張表只能擁有一個聚集索引。在多數情況下,查詢優化器傾向於採用聚集索引。因爲聚集索引能夠在B+ 樹索引的葉子節點上直接找到數據。此外,由於定義了數據的邏輯順序,聚集索引能夠特別快地訪問針對範圍值的查詢。查詢優化器能夠快速發現某一段範圍的數據頁需要掃描。

  聚集索引的存儲並不是物理上連續的,而是邏輯上連續的。這其中有兩點: 一是前面說過的頁通過雙向鏈表鏈接,頁按照主鍵的
順序排序:另一點是每個頁中的記錄也是通過雙向鏈表進行維護的,物理存儲上可以同樣不按照主鍵存儲。
  聚集索引的另一個好處是,它對於主鍵的排序查找和範圍查找速度非常快。葉子節點的數據就是用戶所要查詢的數據。如用戶需要查詢一張註冊用戶的表,查詢最後註冊的10 位用戶,由於B+ 樹索引是雙向鏈表的,用戶可以快速找到最後一個數據頁,並取出10 條記錄。

5.4.2 輔助索引

  對於輔助索引(Secondary Index,也稱非聚集索引), 葉子節點並不包含行記錄的全部數據。葉子節點除了包含鍵值以外, 每個葉子節點中的索引行中還包含了一個書籤(bookmark) 。該書籤用來告訴InnoDB 存儲引擎哪裏可以找到與索引相對應的行數據。由於InnoDB 存儲引擎表是索引組織表, 因此InnoDB 存儲引擎的輔助索引的書籤就是相應行數據的聚集索引鍵。
在這裏插入圖片描述

  輔助索引的存在並不影響數據在聚集索引中的組織,因此每張表上可以有多個輔助索引。當通過輔助索引來尋找數據時, lnnoDB 存儲引擎會遍歷輔助索引並通過葉級別的指針獲得指向搜索引的主鍵,然後再通過主鍵索引來找到他。

5.4.3 B+ 樹索引的分裂

5.4.4 B+ 樹索引的管理

1. 索引管理
  索引的創建和刪除可以通過兩種方法, 一種是ALTER TABLE ,另一種是CREATE/DROPINDEX

5.5 Cardinality 值

5.5.1 什麼是Cardinality

   並不是在所有的查詢條件中出現的列都需要添加索引。對於什麼時候添加B+ 樹索引,在訪問表中很少一部分時使用叫索引纔有意義,就是有區分度的列。
  可以通過SHOW 時DEX 結果中的列Cardinality來觀察。Cardinality 值非常關鍵,表示索引中不重複記錄數量的預估值。同時需要注意的是, Cardinality 是一個預估值,而不是一個準確值,基本上用戶也不可能得到一個準確的值。在實際應用中, Cardinality/o_rows_io_table 應儘可能地接近1 。如果非常小,那麼用戶需要考慮是否還有必要創建這個索引。故在訪問高選擇性屬性的字段並從表中取出很少一部分數據時,對這個字段添加B+ 樹索引是非常有必要的。

5.5.2 InnoDB 存儲引擎的Cardinality 統計

  在InnoDB 存儲引擎中, Cardinality 統計信息的更新發生在兩個操作中: lNSERT和UPDATE。 不可能在每次發生lNSERT 和UPDATE 時就去更新Cardinality 信息,這樣會增加數據庫系統的負荷,同時對於大表的統計,時間上也不允許數據庫這樣去操作。因此, InnoDB 存儲引擎內部對更新Cardinality可信息的策略爲:

  • 表中1/16 的數據已發生過變化。
  • stat modified counter>2 000 000 000 。

5.6 B+ 樹索引的使用

5.6.1 不同應用中B+ 樹索引的使用

  需要根據自己的具體生產環境來使用索引,並觀察索引使用的情況,判斷是否需要添加索引。區分OLTP 和OLAP應用。
  在OLTP 應用中, 查詢操作只從數據庫中取得一小部分數據,一般可能都在10 條記錄以下,甚至在很多時候只取1 條記錄,如根據主鍵值來取得用戶信息,根據訂單號取得訂單的詳細信息,這都是典型OLTP 應用的查詢語句。在這種情況下, B+ 樹索引建立後,對該索引的使用應該只是通過該索引取得表中少部分的數據。這時建立B+ 樹索引纔是有意義的,否則即使建立了,優化器也可能選擇不使用索引。
   對於OLAP 應用,在OLAP 應用中,都需要訪問表中大量的數據,根據這些數據來產生查詢的結果,這些查詢多是面向分析的查詢,目的是爲決策者提供支持。如這個月每個用戶的消費情況,銷售額同比、環比增長的情況。因此在OLAP 中索引的添加根據的應該是宏觀的信息,而不是微觀, 因爲最終要得到的結果是提供給決策者的。例如不需要在OLAP 中對姓名字段進行索引,因爲很少需要對單個用戶進行查詢。但是對於OLAP 中的複雜查詢,要涉及多張表之間的聯接操作,因此索引的添加依然是有意義的。但是,如果聯接操作使用的是Hash Join,那麼索引可能又變得不是非常重要了,所以這需要DBA 或開發人員認真並仔細地研究自己的應用。不過在OLAP 應用中,通常會需要對時間字段進行索引,這是因爲大多數統計需要根據時間維度來進行數據的篩選。

5.6.2 聯合索引

  聯合索引是指對錶上的多個列進行索引。前面討論的情況都是隻對錶上的一個列進行索引。聯合索引的創建方法與單個索引創建的方法一樣,不同之處僅在於有多個索引列。
假設這是一個多列索引(col1, col2,col3),對於葉子節點,是這樣的:
在這裏插入圖片描述
  也就是說,聯合索引(col1, col2,col3)也是一棵B+Tree,其非葉子節點存儲的是第一個關鍵字的索引,而葉節點存儲的則是三個關鍵字col1、col2、col3三個關鍵字的數據,且按照col1、col2、col3的順序進行排序。

  從本質上來說,聯合索引也是一棵B+ 樹,不同的是聯合索引的鍵值的數量不是1.而是大於等於2 。接着來討論兩個整型列組成的聯合索引,假定兩個鍵值的名稱分別爲a、b ,如圖
在這裏插入圖片描述
  可以觀察到多個鍵值的B+ 樹情況。其實和之前討論的單個鍵值的B+ 樹並沒有什麼不同,鍵值都是排序的,通過葉子節點可以邏輯上順序地讀出所有數據,就上面的例子來說,即(1, 1 ) 、(1, 2) 、(2 , 1 ) 、(2 , 4 ) 、(3, 1 ) 、(3 . 2 ) 。數據按(a,b ) 的順序進行了存放。
  因此,對於查詢SELECT • FROM TABLE WHERE a=xxx and b=xxx,顯然是可以使用( a. b ) 這個聯合索引的。對於單個的a 列查詢SELECT * FROM TABLE WHERE a=xxx,也可以使用這個( a, b ) 索引。但對於b 列的查詢SELECT • FROM TABLE WHERE b=xxx ,則不可以使用這棵B+ 樹索引。可以發現葉子節點上的b 值爲1 、2、1 、4 、1 、2. 顯然不是排序的,因此對於b 列的查詢使用不到(a, b) 的索引。
  聯合索引的第二個好處是已經對第二個鍵值進行了排序處理。例如,在很多情況下應用程序都需要查詢某個用戶的購物情況,並按照時間進行排序,最後取出最近三次的購買記錄,這時使用聯合索引可以避免多一次的排序操作,因爲索引本身在葉子節點已經排序了。
  對於索引的選擇,如果有排序,聯合索引裏有排序列,則優化器會選擇聯合索引,省去排序,但是要看B+樹上對那個列已經進行排序了

5.6.3 覆蓋索引

  InnoDB 存儲引擎支持覆蓋索引(covering index ,或稱索引覆蓋),即從輔助索引中就可以得到查詢的記錄,而不需要查詢聚集索引中的記錄。使用覆蓋索引的一個好處是輔助索引不包含整行記錄的所有信息,故其大小要遠小於聚集索引,因此可以減少大量的IO操作。
  對於InnoDB 存儲引擎的輔助索引而言,由於其包含了主鍵信息,因此其葉子節點存放的數據爲(primary key 1, primary key2,…, key 1, key2 ,…〉。覆蓋索引的另一個好處是對某些統計問題而言的,InnoDB 存儲引擎並不會選擇通過查詢聚集索引來進行統計。由於 表上還有輔助索引,而輔助索引遠小於聚集索引,選擇輔助索引可以減少IO 操作。

5.6.4 優化器選擇不使用索引的情況

  在某些情況下,當執行EXPLAIN 命令進行SQL 語句的分析時,會發現優化器並沒有選擇索引去查找數據,而是通過掃描聚集索引,也就是直接進行全表的掃描來得到數據。這種情況多發生於範圍查找、JOIN 鏈接操作等情況下。

  用戶要選取的數據是整行信息,而 索引不能覆蓋到我們要查詢的信息,因此在對 索引查詢到指定數據後,還需要一次書籤訪問來查找整行數據的信息。雖然 索引中數據是順序存放的,但是再一次進行書籤查找的數據則是無序的,因此變爲了磁盤上的離散讀操作。如果要求訪問的數據量很小,則優化器還是會選擇輔助索引,但是當訪問的數據佔整個表中數據的蠻大一部分時〈一般是20% 左右),優化器會選擇通過聚集索引來查找數據。因爲之前已經提到過,順序讀要遠遠快於離散讀。
  因此對於不能進行索引覆蓋的情況,優化器選擇輔助索引的情況是,通過輔助索引查找的數據是少量的。這是由當前傳統機械硬盤的特性所決定的,即利用順序讀來替換隨機讀的查找。

5.6.5 索引提示

  MySQL 數據庫支持索引提示CINDEX HINT) , 顯式地告訴優化器使用哪個索引。

5.6.6 Multi-Range Read 優化

  Multi-Range Read 優化的目的就是爲了減少磁盤的隨機訪問,並且將隨機訪問轉化爲較爲順序的數據訪問,這對
於IO-bound 類型的SQL 查詢語句可帶來性能極大的提升。Multi-Range Read 優化可適用於range , ref, eq_ref 類型的查詢。

5.6.7 Index Condition Pushdown (ICP) 優化

  當進行索引查詢時,首先根據索引來查找記錄,然後再根據WHERE 條件來過濾、記錄。在支持Index Condition Pushdown 後, MySQL 數據庫會在取出索引的同時,判斷是否可以進行WHERE 條件的過濾,也就是將WHERE 的部分過濾操作放在了存儲引擎層。在某些查詢下,可以大大減少上層SQL 層對記錄的索取(fetch) ,從而提高數據庫的整體性能。

5.7 哈希算法

5.7.1 哈希表

  哈希表( Hash Table) 也稱散列表,由直接尋址表改進而來。我們先來看直接尋址表。當關鍵字的全域U 比較小時,直接尋址是一種簡單而有效的技術

5.7.2 InnoDB 存儲引擎中的哈希算法

  InnoDB 存儲引擎使用哈希算法來對字典進行查找, 其衝突機制採用鏈表方式,哈希函數採用除法方式。對於緩衝池頁的時表來說,在緩衝池中的page頁都有一個chain 指針,它指向相同哈希函數值的頁。
  InnoDB 存儲引擎的表空間都有一個space_id ,用戶所要查詢的應該是某個表空間的某個連續16KB 的頁,即偏移量offset。InnoDB 存儲引擎將space_id 左移20 位,然後加上這個space_id 和offset,即關鍵字K space id<<20+space _id+offset ,然後通過除法散列到各個槽中去。

5.7.3 自適應哈希索引

  自適應哈希索引採用之前討論的哈希表的方式實現。不同的是,這僅是數據庫自身創建並使用的, DBA 本身並不能對其進行干預。自適應哈希索引經哈希函數映射到一個哈希表中,因此對於字典類型的查找非常快速

5.8 全文檢索

5.8.1 概述

   全文檢索(Full-Text Search) 是將存儲於數據庫中的整本書或整篇文章中的任意內容信息查找出來的技術。它可以根據需要獲得全文中有關章、節、段、句、詞等信息,也可以進行各種統計和分析。

5.8.2 倒排索引

  全文檢索通常使用倒排索引(inverted index) 來實現。倒排索引同B+ 樹索引一樣,也是一種索引結構。它在輔助表(auxiliary table) 中存儲了單詞與單詞自身在一個或多個文檔中所在位置之間的映射。這通常利用關聯數組實現,其擁有兩種表現形式。

  • inverted file index ,其表現形式爲{單詞,單詞所在文檔的IO}
  • full inverted index,其表現形式爲{單詞, (單詞所在文檔的田,在具體文檔中的位置)}

5.8.3 InnoDB 全文檢索

InnoDB 存儲引擎採用 full inverted index的方式。在InnoDB 存儲引擎中,將(DocumentId , Position) 視爲一個" ilist"。因此在全文檢索的表中,有兩個列,一個是word 字段,另一個是ilist 字段,並且在word 字段上有設有索引。此外,由於InnoDB 存儲引擎在ilist 字段中存放了Position 信息,故可以進行Proximity Search。
  正如之前所說的那樣,倒排索引需要將word 存放到一張表中,這個表稱爲Auxiliary Table (輔助表)。在InnoDB 存儲引擎中,爲了提高全文檢索的並行性能,共有6 張Auxiliary Table ,目前每張表根據word 的Latin 編碼進行分區。
  Auxiliary Table 是持久的表,存放於磁盤上。然而在InnoDB 存儲引擎的全文索引中,還有另外一個重要的概念FTS Index Cache (全文檢索索引緩存),其用來提高全文檢索的性能。
  FTS Index Cache 是一個紅黑樹結構,其根據(word , ilist) 進行排序。這意味着插入的數據已經更新了對應的表,但是對全文索引的更新可能在分詞操作

5.8.4 全文檢索

用的少,略

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章