InnoDB支持如下幾種索引:html
本文將着重介紹B+樹索引。其餘兩個全文索引和哈希索引只是作簡單介紹一筆帶過。mysql
哈希索引是自適應的,也就是說這個不能人爲干預在一張表生成哈希索引,InnoDB會根據這張表的使用狀況來自動生成。sql
全文索引是將存在數據庫的整本書的任意內容信息查找出來的技術,InnoDB從1.2.x版本支持。每張表只能有一個全文檢索的索引。數據庫
B+樹索引是傳統意義上的索引,B+樹索引並不能根據鍵值找到具體的行數據,B+樹索引只能找到行數據鎖在的頁,而後經過把頁讀到內存,再在內存中查找到行數據。B+樹索引也是最經常使用的最爲頻繁使用的索引。函數
B+樹是一種平衡查找樹,其實先想一想看爲何要用平衡查找樹,不用二叉樹?普通的二叉樹可能由於插入的數據最後變成一個很長的鏈表,怎麼能提升搜索的速度呢?你能夠想一想,爲何HashMap和ConcurrentHashMap在JDK8的時候,當鏈表大於8的時候把鏈表轉成紅黑樹(紅黑樹也是平衡查找樹)。技術思惟是想通的,那麼答案無非是加快速度,性能咯。性能
一個B+樹有如下特徵:優化
那麼咱們先來看一個B+樹的圖spa
全部的數據都在葉子節點,且每個葉子節點都帶有指向下一個節點的指針,造成了一個有序的鏈表。爲何要有序呢?實際上是爲了範圍查詢。好比說select * from Table where id > 1 and id < 100; 當找到1後,只需順着節點和指針順序遍歷就能夠一次性訪問到全部數據節點,極大提到了區間查詢效率。是否是範圍查詢的話hash就搞不定這個事情了?如下爲B+樹的優點:設計
通常性狀況,數據庫的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+樹高爲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操做便可查找到數據。
如何判斷一個索引創建的是否好呢?能夠用show index from指令查看Cardinality值,這個值是一個預估值,而不是一個準確值。每次對Cardinality值的統計都是隨機取8個葉子節點獲得的。
對於innodb來講,達到如下2點就會從新計算cardinality
實際應用中,(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