搞懂MySQL InnoDB B+樹索引

一.InnoDB索引

  InnoDB支持如下幾種索引:html

  • B+樹索引
  • 全文索引
  • 哈希索引

  本文將着重介紹B+樹索引。其餘兩個全文索引和哈希索引只是作簡單介紹一筆帶過。mysql

  哈希索引是自適應的,也就是說這個不能人爲干預在一張表生成哈希索引,InnoDB會根據這張表的使用狀況來自動生成。sql

  全文索引是將存在數據庫的整本書的任意內容信息查找出來的技術,InnoDB從1.2.x版本支持。每張表只能有一個全文檢索的索引。數據庫

  B+樹索引是傳統意義上的索引,B+樹索引並不能根據鍵值找到具體的行數據,B+樹索引只能找到行數據鎖在的頁,而後經過把頁讀到內存,再在內存中查找到行數據。B+樹索引也是最經常使用的最爲頻繁使用的索引。函數

 

二.什麼是B+樹

概念

  B+樹是一種平衡查找樹,其實先想一想看爲何要用平衡查找樹,不用二叉樹?普通的二叉樹可能由於插入的數據最後變成一個很長的鏈表,怎麼能提升搜索的速度呢?你能夠想一想,爲何HashMap和ConcurrentHashMap在JDK8的時候,當鏈表大於8的時候把鏈表轉成紅黑樹(紅黑樹也是平衡查找樹)。技術思惟是想通的,那麼答案無非是加快速度,性能咯。性能

  一個B+樹有如下特徵:優化

  • 有n個子樹的中間節點包含n個元素,每一個元素不保存數據,只用來索引,全部數據都保存在葉子節點。
  • 全部葉子節點包含元素的信息以及指向記錄的指針,且葉子節點按關鍵字自小到大順序連接。
  • 全部的中間節點元素都同時存在於子節點,在子節點元素中是最大(或最小)元素。

  那麼咱們先來看一個B+樹的圖spa

  全部的數據都在葉子節點,且每個葉子節點都帶有指向下一個節點的指針,造成了一個有序的鏈表。爲何要有序呢?實際上是爲了範圍查詢。好比說select * from Table where id > 1 and id < 100; 當找到1後,只需順着節點和指針順序遍歷就能夠一次性訪問到全部數據節點,極大提到了區間查詢效率。是否是範圍查詢的話hash就搞不定這個事情了?如下爲B+樹的優點:設計

  • 單一節點存儲更多元素,減小IO
  • 全部查詢都要找到葉子節點,查詢穩定
  • 全部葉子節點造成有序鏈表,方便範圍查詢

  通常性狀況,數據庫的B+樹的高度通常在2~4層,這就是說找到某一鍵值的行記錄最多須要2到4次邏輯IO,至關於0.02到0.04s。指針

 

三.彙集索引和輔助索引

彙集索引

  彙集索引是按表的主鍵構造的B+樹,葉子節點存放的爲整張表的行記錄數據,每張表只能有一個彙集索引。優化器更傾向採用彙集索引。由於直接就能獲取行數據。

  請選擇自增id來作主鍵,不要非空UK列。避免大量分頁碎片。下面來看一個彙集索引的圖:

  那麼很簡單了,每一個葉子節點,都存有完整的行記錄。對於主鍵的查找速度那是至關的快,美滋滋。

輔助索引

  輔助索引也叫非彙集索引,葉子節點除了鍵值之外還包含了一個bookmark,用來告訴InnoDB在哪裏能夠找到對應的行數據,InnoDB的輔助索引的bookmark就是相對應行數據的彙集索引鍵。也就是先獲取指向主鍵索引的主鍵,而後經過主鍵索引來找到一個完整的行。若是輔助索引的樹和彙集索引的樹的高度都是3,若是不是走主鍵索引走輔助索引的話,那麼須要6次邏輯IO訪問獲得最終的數據頁。輔助索引和彙集索引的概念關係圖以下:

 

四.索引實戰

設計索引

  設計索引的時候,不管是組合索引仍是普通索引等。通常經驗是,選擇常常被用來過濾記錄的字段,高選擇性,高區分性。別把性別字段設計索引,性別屬於低選擇性的。你能夠選擇名字嘛,你好我大名叫苗嘉杏:)

  知道加索引快,可是也別亂加索引,插入以及更新索引的操做InnoDB都會維護B+樹的,多加不少索引只會致使效率下降!

  不要用重複的索引,好比有個聯合索引是a,b,你又整個a列的普通索引。那不是搞事麼?

  不要在索引上用函數和like

一顆彙集索引B+樹能夠放多少行數據?

  這裏咱們先假設B+樹高爲2,即存在一個根節點和若干個葉子節點,那麼這棵B+樹的存放總記錄數爲:根節點指針數*單個葉子節點記錄行數。假設一行記錄的數據大小爲1k,那麼單個葉子節點(頁)中的記錄數=16K/1K=16。

  那麼如今咱們須要計算出非葉子節點能存放多少指針,咱們假設主鍵ID爲bigint類型,長度爲8字節,而指針大小在InnoDB源碼中設置爲6字節,這樣一共14字節,咱們一個頁中能存放多少這樣的單元,其實就表明有多少指針,即16kb/14b=1170。那麼能夠算出一棵高度爲2的B+樹,大概能存放1170*16=18720條這樣的數據記錄。

  根據一樣的原理咱們能夠算出一個高度爲3的B+樹大概能夠存放:1170*1170*16=21902400行數據。因此在InnoDB中B+樹高度通常爲1-3層,它就能知足千萬級的數據存儲。在查找數據時一次頁的查找表明一次IO,因此經過主鍵索引查詢一般只須要1-3次邏輯IO操做便可查找到數據。

Cardinality值

  如何判斷一個索引創建的是否好呢?能夠用show index from指令查看Cardinality值,這個值是一個預估值,而不是一個準確值。每次對Cardinality值的統計都是隨機取8個葉子節點獲得的。

  對於innodb來講,達到如下2點就會從新計算cardinality

  • 若是表中1/16的數據發生變化 
  • 若是stat_modified_counter>200 000 0000

  實際應用中,(Cardinality/行數)應該儘可能接近1。若是很是小則要考慮是否須要此索引。實戰一下,好比有一張表,咱們來show index一下

mysql> show index from Order;
+---------+------------+------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table   | Non_unique | Key_name         | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+---------+------------+------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Order   |          0 | PRIMARY          |            1 | id          | A         |       99552 |     NULL | NULL   |      | BTREE      |         |               |
| Order   |          1 | IDX_orderId      |            1 | orderId     | A         |       96697 |     NULL | NULL   |      | BTREE      |         |               |
| Order   |          1 | IDX_productId    |            1 | productId   | A         |          52 |     NULL | NULL   |      | BTREE      |         |               |
+---------+------------+------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
3 rows in set (0.00 sec)

  那麼能夠看到IDX_productId這個索引的Cardinality比較低。 

  須要強制刷新Cardinality值的話能夠用:

analyze local table xxx;

 

 

 

 

 

參考:

MySQL5.1參考手冊 - http://dev.mysql.com/doc/refman/5.1/zh/index.html

《MySQL技術內幕》

《小灰漫畫》

http://www.javashuo.com/article/p-hwkjuonu-ct.html

http://blog.codinglabs.org/articles/theory-of-mysql-index.html

相關文章
相關標籤/搜索