自從兩年前瞭解到的索引以來的,就一直想寫一篇有關索引的文章。然而我是個拖延癌症患者,一拖就是兩年,不愧是我。該篇文章算是本身的筆記,歡迎批評。sql
索引是什麼?不少書和文章都會使用圖書的目錄來類比。目錄的目的就是用方便咱們查找具體內容的位置,具體的章節的範圍。與此相似,MySQL中索引的用途是幫助咱們加速查詢以及排序。數據庫
在InnoDB中的索引類型有哈希索引、B+樹索引、全文索引。哈希索引在InnoDB中設計爲自適應的,不展開討論。在InnoDB1.12以後有了全文索引,也是應用倒排,還沒踩過坑(聽說不支持中文),有時間能夠研究一下。數組
本篇主要討論B+樹索引。數據結構
學習MySQL的索引,必須得先了解其原理,不然不少問題將一頭霧水。下文將講述索引數據結構的原理,而不涉及其複雜的具體實現。函數
在談B+樹以前,不妨先思考,爲何索引能加快查詢?爲何要用B+樹做爲索引而不用B樹?哈希索引查詢複雜度爲O(1)爲何又不用哈希索引?性能
在瞭解B樹和B+樹以前,先了解一下二叉搜索樹(BST)和平衡二叉樹(AVL)。學習
在順序存儲結構中(如數組),最快的狀況是第一個就是目標值,最壞的狀況是最後一個是目標值,假設有n個元素,用大O標記法平均的時間複雜度爲O(n)。大數據
使用二叉搜索樹能夠有效的優化搜索時間。利用二叉搜索樹的特性左子節點比父節點小、右子節點比父節點大,能夠很方便的進行二分查找,有效優化的搜索時間。優化
正常狀況下,咱們使用二叉搜索樹能夠了,但若是出現如下的狀況,二叉搜索樹反而起不到效果,搜索的平均時間複雜度依舊爲O(n)。spa
引入平衡二叉樹,深度差不超過1,從而保證不傾斜,或者說更矮,保證其搜索效率。
既然平衡二叉樹已經能夠加快查詢了,但實際上InnoDB並不會使用。在思考B樹和B+樹的相關問題的時候,離不開一個問題——磁盤IO。索引文件存儲在磁盤,假設有平衡二叉樹樹高30,那麼你可能要掃描30次磁盤才能完成搜索。
對於須要磁盤IO的狀況,使用平衡二叉樹依舊比較糟糕,因此須要引入多路樹,即B樹和B+樹,使得樹更「矮」。
若是上述B樹改爲二叉樹,那麼樹的高度就大了不少,換而言之就須要更屢次的磁盤IO。
B+樹是B樹的變種。B+樹的非葉子結點不存儲數據,而且全部的葉子節點以雙向鏈表的形式相連。
如今的索引模型基本都是B+樹。
相對於B樹來講,B+樹的搜索更加穩定,由於B樹有的數據是分佈在非葉子節點上的。
B樹的葉子節點以鏈表的形式相連且按照規則排了序,經過B+索引,能夠更加方便的獲取範圍數據。
這也是不使用哈希索引的緣由。雖然哈希索引搜索的時間複雜度爲O(1),但大部分時候咱們並不會只查詢一條記錄,這種時候使用哈希索引就比較乏力了。
彙集索引,亦可稱爲主鍵索引。一張表只存在一個彙集索引。
彙集索引是根據其主鍵做爲排序規則的B+樹,搜索時根據其主鍵進行搜索。
其中葉子節點上存儲着整條記錄的數據。
InnoDB的B+樹在磁盤中的存儲是以數據頁的形式,在樹中間進行插入和刪除操做涉及列「頁分裂」和「頁合併」的複雜過程(關於這點我我的也講不明白,但能夠類比AVL樹的旋轉去理解),十分耗性能,而直接插入尾部是比較快捷的方式,因此在不少的規範中寫道,當使用InnoDB引擎的時候,強烈建議用一個與業務無關的自增id做爲主鍵。
此外,刪除也是同樣的,不少時候會要求作僞刪除,不只僅只是爲了數據分析,更是爲了索引的性能。
非彙集索引又稱輔助索引,以非主鍵列來創建。非彙集索引能夠有多個。非彙集索引和彙集索引的區別在於,非彙集索引的葉子節點並不存儲整條記錄的數據,而是存儲指向的主鍵的指針。因此,當利用非主鍵索引進行搜索時,還須要經過主鍵索引獲取整條數據。
單值索引就是在數據表單個列上創建單個值。
CREATE INDEX index_name ON table_name(column);
複製代碼
與主鍵索引相似,單值索引按照所指定列排序創建二叉樹。當利用單值搜索到目標後,再經過主鍵索引去讀取整條數據。
惟一索引與單值索引區別不大,只是惟一索引的值不會重複。
惟一索引除了能提升一些效率之外,有時也用來保證列的惟一性,如用戶的手機號身份證等。這裏不作過多贅述。
建立聯合索引時指定多列便可。
CREATE INDEX index_name ON table_name(column1, columm2, column3 [,...])
複製代碼
聯合索引會按照創建索引時的順序,對每一個字段進行排序。即第一個字段排完序,接着排第二個字段,第三...
覆蓋索引
在前面提到,非彙集索引搜索記錄時還須要經過的主鍵索引,但若是查找的列剛恰好是聯合索引的字段,那就沒有必要去再去搜索主鍵索引,直接取葉子節點值便可,這就是覆蓋索引。
爲何不用select *
,緣由就在此,不只僅是爲了減小讀取更多列帶來的開銷,也是爲了可以使用上覆蓋索引。使用覆蓋索引能夠減小磁盤IO,有效提升性能。
下文將講述有關聯合索引的更多細節。
上文了解了聯合索引,知道了聯合索引的節點數據是按照建索引的順序依次排序,由此咱們引出了最左前綴原則,聯合索引中,若是要用上索引字段,前面的字段不能跳過。若是上圖的例子,假設是找column2=「ccc」的記錄,大概的sql以下
SELECT some_column FROM table_name WHERE column2="ccc"
複製代碼
這種狀況下索引是用不上的,由於索引是先排序的column1,再排序column2,直接經過column2搜索,B+樹並不知道怎麼搜索。
除了上述的最左前綴原則下索引的失效,還有其餘索引失效狀況。
使用MySQL內置函數運算的列索引會失效。
使用!=,is null,is not null 索引會失效。
好比你查找id != 500的記錄,至關把掃描id<500,以及id >500的記錄,本質上全表掃描沒啥區別。
範圍查詢後的列沒法使用。
仍是用上圖的例子,假設查詢 column1 <= 4的狀況
SELECT some_column FROM table_name WHERE column1 <= 4
複製代碼
由於column1是排了序的,索引聯合索引column1仍是可使用上的,但column1是範圍數據,在這範圍內column2並不有序。
以通配符開頭的模糊查詢(LIKE "%string"
)。
值得一提的是,LIKE "string%"
是能用上索引的,相似於範圍查詢,查詢從string開頭的最小字符串
到stirng開頭的最大字符串
。知道了LIKE "string%"
是能用上索引的就能理解爲何LIKE "%string"
爲何用不上索引了。
還有其餘狀況的狀況,可用MySQL的查詢分析器進行分析。
InooDB使用的鎖是行鎖,但若是在更新時索引失效了,行鎖會變成表鎖,在開發中應該避免。
經常使用來分組和排序的字段可創建索引。
索引的做用是查詢和排序,order by
和group by
是能夠用上索引的,若是排序的有多字段,也是按照最左前綴原則。
常常用來查詢的字段可建索引。
更新頻繁的字段不要創建索引。
頻繁更新的字段若是創建來索引,更新時不只更新數據,並且索引的B+樹也會發生變化,開銷比較大,得不償失。
選擇性小的列不要創建索引。
好比說性別字段,只有男或女或未知,百萬數據裏只有這三個值,創建索引毫無心義。
索引儘可能使用等值匹配。
儘可能使用覆蓋索引。
經過創建索引,能夠有效的加速數據庫的查詢和排序。當談及的數據庫優化時,索引優化確定跑不了。索引的使用有各業界大佬總結的技巧,但不少東西不是絕對的,不能以偏概全,在大數據以及複雜業務下,索引的維護算是玄學,須要不斷尋找最佳的索引方案。