什么是索引:
索引是一種高效獲取數據的存儲結構,例:hash、 二叉、 紅黑。
B+樹是一種平衡多路查找樹, 與二叉樹、紅黑樹等最大的差別是B+樹可以擁有更多的出度(可以理解為節點的數據量), 由於B+樹的漸進時間復雜度為O(H)=O(logdN)(H為樹高, d為出度, N為數據量), 則一顆出度為200、數據量為200W的B+樹高度為3, 這樣查詢一條數據最多只需要3次磁盤IO 。 另外主存和磁盤以頁為單位交換數據, 在技術實踐中, 我們可以使節點大小為磁盤一個頁的大小,並且在新建節點時直接申請一個頁大小的空間, 這樣節點的物理存儲位置也是在一個頁里, 好處是存取一個節點只需要一次磁盤IO(在大部分操作系統中頁的大小通常為4K, 如linux)。
圖1 索引結構
MyISAM vs InnoDB
在MySQL5.5之前, 默認的數據庫引擎是MyISAM, 由早起的ISAM(Indexed Sequential Access Method)改良而成, 不過MyISAM一個最大的缺陷是不支持事務, 所以目前MySQL應用中大部分是采用InnoDB, InnoDB支持了ACID兼容的事務功能。
通常我們說MyISAM是非聚集索引, InnoDB為聚集索引, 主要原因是MyISAM的索引和數據是分開存儲的, 每個MyISAM表在磁盤上存儲為3個文件, .frm存儲表定義、.MYD存儲數據、.MYI存儲索引; InnoDB的索引文件和數據文件就是同一個, 數據是按主鍵索引來聚集的。 不同的是MyISAM頁節點的data域存儲的是數據的地址, 而InnoDB的頁節點data域存儲的數據本身。 從下述InnoDB引擎的結構可以看出,每張表用主鍵(primary key)構建一顆B+樹, 然后每個葉子節點存儲行數據。
圖2:MyISAM索引結構
圖3:InnoDB索引結構
另外在實際MySQL使用中, 除了主鍵之外, 還會根據我們的業務特征來建立其他的索引, 稱為輔組索引。對於MyISAM來說, 輔助索引跟主索引在結構上沒差別, 存儲的也是數據的地址,只是主索引要求key唯一, 而輔助索引則沒有要求; 對於InnoDB來說, 輔助索引的data域保存的是主key的值, 所以在InnoDB中從輔助索引查到主鍵key之后還需要到主索引中查詢數據。
了解了索引背后的實現原理, 在日常MySQL的使用過程我們也能明白很多Tips的原因,例如:
1: MyISAM允許沒有任何索引和主鍵的表存在,而對於InnoDB來說如果表創建者沒有指定表主鍵的話或者唯一非空索引, 會自動生成一個6字節的rowid作為主鍵(用戶不可見), 也就是說InnoDB表必須要有主鍵。
2: 對於InnoDB來說不建議使用過長的字段作為主鍵,這樣會導致輔助索引變得過大。
3: 使用自增字段作為InnoDB表主鍵是個很好的選擇, 這樣數據的插入、刪除都十分高效。 不要用隨機值或者業務相關的值, 這樣為了維護B+Tree特性而帶來額外的分裂移動操作十分昂貴, 特別是會導致不同頁之間交換數據的情況, 可能會有額外的磁盤IO。
其他一些關於MyISAM和InnoDB的差異:
1: MyISAM只支持表級鎖, InnoDB支持行級鎖。
2: 支持fulltext類型的全文索引, InnoDB不支持。
3: MyISAM保存有表的總函數, 用select count() from table可以直接取出改值。 而InnoDB沒有保存表行數, select count() from table會需要遍歷表; 另外在MyISAM中select count如果加了where就和InnoDB的處理方式一樣的。
最左前綴匹配
在講最左前綴匹配規則之前, 我們先來看看聯合索引, 上邊我們說的都是主鍵索引, 通常在實際應用中, 我們還會根據我們的查詢場景采用多個列來建立索引, 這個就是聯合索引, 如下所示建立聯合索引的SQL:ALTER TABLE table_name ADD INDEX index_name (column_1, column_2)。 在B+樹中, 所有鍵值都是有序的, 對於聯合索引來說, 建立索引時索引列的順序意味着索引首先按照最左列進行排序,其次再為第二列、第三列等等。所以一個聯合索引各節點一定是按第一列嚴格排序的, 但是后邊的列並不是整體完全有序, 只是局部有序(第一列鍵值相同的情況下)。
那么MySQL的最左前綴匹配是怎么回事呢? 我們看下邊的例子:
示例表t_student, 然后針對Fname、Fage、Fsex建立了聯合索引。然后在下邊的幾種查詢語句中, 只有在where條件包含Fname時采用用到索引, 其余的各種情況都用不到索引, 究其原因, 就是在構建索引的B+樹時, 是先按第一列(即Fname)來組織並保證有序, 一次再是Fage、Fsex, 當查詢條件中沒有Fname時, 此時就不知道從哪個節點開始了, 是用不到索引的。 索引結構簡單示例如下:
like問題
使用like模糊查詢會導致索引失效,在數據量大的時候會有性能問題
(1)盡量少以%或者_開頭進行模糊查詢
通過explain執行計划,我們發現,使用like模糊查詢時,如果不以%和_開頭查詢的話,索引還是有效的
以%或者_開頭查詢,索引失效
(2)使用覆蓋索引
當查詢的的條件和查詢的結果都是索引中的字段的時候,這個索引我們可以稱之為覆蓋索引,這個時候,使用like模糊查詢索引是有效的
InnoDB中主鍵可以不添加進索引中
注意:使用覆蓋索引,對於字段的長度是由要求限制的,一般超過長度,索引也會失效
這里如果我查詢中帶有descripition字段,則覆蓋索引也會失效(我這里的數據庫經過測試最多只支持255長度的字段)
(3)使用全文索引
給字段建立Full Text索引,然后使用match(...) against(...)進行檢索
注意:這種全文索引方式只對英文單詞起作用,對於中文漢字支持不夠友好,需要額外去mysql的配置文件做一些配置修改,讓它額外支持中文
(4)使用一些額外的全文搜索引擎來解決
Lucene,solr,elasticsearch等等
基本原理是:把mysql配置文件中的ft_min_word_len=3改為1。(沒有這項就直接添加),然后新建一個字段來保持分詞結果,給這個字段建立全文索引。然后實現一個分詞模塊,把詞語“大家好”拆分為“大 大家 大家好 家 家好 好”。然后用match .. against 來代替like %%,查詢出來的結果跟like的結果基本相同(如果分詞合理的話),但是效率比like高至少10倍以上。
聚簇索引和非聚簇索引的差異