如果正確合理設計並且使用索引的 MySQL 是一輛蘭博基尼的話,那麼沒有設計和使用索引的 MySQL 就是一個人力三輪車。
沒有索引的表,單表查詢可能幾十萬數據就是瓶頸,那到底該如何應對網際網路企業的上億的數據?
MySQL 中的 InnoDB 存儲引擎的核心在於索引,索引的核心在於 B + tree,所以說要想了解 MySQL 中索引,我們必須要先了解 B + tree ,而 B + tree 的說白了就是一顆平衡多叉樹。
索引是存儲引擎用於快速找到記錄的一種數據結構,可以讓伺服器快速定位到表的指定位置。在查找數據時,存儲引擎先在索引中找到對應的值,然後根據所匹配的索引記錄找到對應的數據行。索引列的順序非常重要,MySQL 只能高效的使用索引的最左前綴列。
索引的優點
1、減少伺服器需要掃描的數據量;
2、幫伺服器避免排序和臨時表;
3、將隨機 IO 變為順序 IO。
索引的缺點:
1、需要額外的維護成本,降低數據更新的速度;
2、佔用內存和磁碟的空間。
說到索引是一種數據結構,那麼都知道索引是一種 B + Tree 的結構,如下圖所示,
最外層淺藍色磁碟塊 1 裡有數據 17、35(深藍色)和指針 P1、P2、P3(黃色)。
P1 指針表示小於 17 的磁碟塊,P2 是在 17 - 35 之間,P3 指向大於 35 的磁碟塊。真實數據存在於子葉節點也就是最底下的一層 3、5、9、10、13 ...... 非葉子節點不存儲真實的數據,只存儲指引搜索方向的數據項,如 17、35。
查找過程:例如搜索 28 數據項,首先加載磁碟塊 1 到內存中,發生一次 I/O,用二分查找確定在 P2 指針。接著發現 28 在 26 和 30 之間,通過 P2 指針的地址加載磁碟塊 3 到內存,發生第二次 I/O。
用同樣的方式找到磁碟塊 8,發生第三次 I/O。
真實的情況是,上面 3 層的 B + Tree 可以表示上百萬的數據,上百萬的數據只發生了三次 I/O 而不是上百萬次 I/O,時間提升是巨大的。
上文提到的磁碟塊,那麼一個磁碟塊可以存儲多少數據呢,每個磁碟塊根據系統的不同,文件系統的不同可能會不一樣大。
可能是有的是 512 byte,或者 2 k。那麼如果知道一個索引欄位的大小是多少,就可以知道一個磁碟塊上可以有多少個索引了。
二叉樹
說到底 B + Tree 就是一棵多查平衡樹,那就我們拿二叉平衡樹,來看看樹的創建過程,轉置過程。
AVL 樹的旋轉一共有四種情形,注意所有旋轉情況都是圍繞著使得二叉樹不平衡的第一個節點展開的。
1.LL型
平衡二叉樹某一節點的左孩子的左子樹上插入一個新的節點,使得該節點不再平衡。
這時只需要把樹向右旋轉一次即可,如圖所示,原 A 的左孩子 B 變為父結點,A 變為其右孩子,而原B的右子樹變為 A 的左子樹,注意旋轉之後 Brh 是 A 的左子樹(圖上忘在 A 於 Brh 之間標實線)
2.RR 型
平衡二叉樹某一節點的右孩子的右子樹上插入一個新的節點,使得該節點不再平衡。
這時只需要把樹向左旋轉一次即可,如圖所示,原 A 右孩子 B 變為父結點,A 變為其左孩子,而原 B 的左子樹 Blh 將變為A的右子樹。
3.LR 型
平衡二叉樹某一節點的左孩子的右子樹上插入一個新的節點,使得該節點不再平衡。這時需要旋轉兩次,僅一次的旋轉是不能夠使二叉樹再次平衡。
如圖所示,在 B 節點按照 RR 型向左旋轉一次之後,二叉樹在 A 節點仍然不能保持平衡,這時還需要再向右旋轉一次。
4.RL 型
平衡二叉樹某一節點的右孩子的左子樹上插入一個新的節點,使得該節點不再平衡。
同樣,這時需要旋轉兩次,旋轉方向剛好同 LR 型相反。
看完二叉樹的轉置後,我們可以將此過程映射到 B + Tree 的轉置上。
聚簇索引和非聚簇索引
聚簇索引
看完索引,以及了解了二叉樹的轉置,接下來我們看一下索引的分類, 索引大面上分為聚簇索引 和 非聚簇索引。
B + Tree 所有索引數據都在葉子結點上,並且增加了順序訪問指針,每個葉子節點都有指向相鄰葉子節點的指針。
先來說一下聚簇索引,聚簇索引並不是一種單獨的索引類型,而是一種數據存儲方式。
InnoDB 的聚簇索引實際上在同一個結構中保存了 B + Tree 索引和數據行。
葉子節點包含行的所有數據,節點頁只包含了索引列。
如果沒有定義主鍵,InnoDB 會選擇一個唯一的非空索引代替;如果沒有這樣的索引 InnoDB 會隱式定義一個主鍵來作為聚簇索引當然聚簇索引的存儲方式也是 B + Tree。
非聚簇索引
非聚簇索引,和聚簇索引最大的區別就是數據的存儲上, 聚簇索引不僅存儲了索引的數據,還在葉子節點中存儲了對應的 table 中的數據,記錄的索引順序與物理順序相同。而非聚簇索引則存儲的是索引數據並且在葉子上存儲了聚簇索引的值。
所以說通過聚簇索引查找數據, 只需要一次便可以拿到自己想要的記錄, 而非聚簇索引聚簇索引需要兩次。
索引優化
索引的創建原則
1.選擇唯一性索引,這樣會使得索引的效率是最高的。
2.為經常需要排序、分組和聯合操作的欄位建立索引,提高索引的使用率,就能提高查詢的 sql 的效率。聯合欄位建立索引是非常必要的,所以我們大部分的表連接都是主鍵+外鍵的形式。
3.為常作為查詢條件的欄位建立索引。
4.限制索引的數目。如果把每一個欄位都建立上索引,那麼數據的更新就會非常慢,並且索引過多會佔用大量的磁碟空間。
5.儘量使用欄位短的索引,這樣提高索引的檢索效率。
6.儘量使用前綴來索引。這個是針對欄位長度比較長的欄位建立索引的原則,這樣能使的索引的查詢效率比較高。
7.刪除不再使用或者很少使用的索引。沒有用的索引,不僅會佔用空間,而且還會影響更新的速度。
索引優化
多列索引
正確的索引順序依賴於使用該索引的查詢,同時需要考慮如何更好的滿足排序和分組的需求。
在一個 Btree 索引中,索引的順序意味著索引首先按照最左列進行排序,其次是第二列,因此索引可以按照升序或是降序進行掃描,以滿足精確符合列順序的 Order By、Group By 和 Distinct 等查詢語句,因此多列索引的列順序至關重要。
當不需要考慮排序和分組時,將選擇性最高的列放在前面通常是好的,這時候索引的作用只是用於優化 Where 條件查詢。
覆蓋索引
如果一個索引包含(或者說覆蓋)所有需要查詢的欄位的值,我們就稱之為 「 覆蓋索引 」。
MySQL 可以使用索引來直接獲取列的數據,這樣就不在需要讀取數據行了。
覆蓋索引必須要存儲索引列的值,哈希索引、空間索引和全文索引都不存儲列值,MySQL 只能使用 Btree 索引作為覆蓋索引。
排序 MySQL 支持兩種方式生成排序結果:通過排序操作;通過索引順序掃描,MySQL 可以使用同一個索引既滿足查找又滿足排序,只有當索引的列順序和 Order By 子句的順序完全一致,並且所有列的排序方向也一樣時,才能使用索引進行排序,其中 Order By 子句和查詢限制一樣,需要滿足索引的最左前綴匹配,當前導列為常量時,可以不滿足最左前綴的要求。
冗餘和重複索引
重複索引是指在相同的列上按照相同的順序創建的相同類型的索引,一旦發現重複索引,應該立即移除。
冗餘索引與重複索引有些不同,如果創建了索引(A, B),再創建索引(A)就是冗餘索引,因為這只是前一個索引的前綴索引;索引(A, ID)對於 InnodDB 來說主鍵列已經包含在二級索引中,所以也是冗餘。
應儘量擴展已有索引而不是創建新的索引,但也有時候出於性能考慮需要冗餘索引,因為擴展已有的索引會導致其變得太大,從而影響其他使用該索引的查詢性能,特別是 count,group by 等統計查詢。
未使用的索引,就是永遠都用不到的索引,這種索引就是累贅,直接刪除。
前綴索引和索引選擇性
MySQL 無法使用前綴索引做 Group By 和 Order By,也無法使用前綴索引做覆蓋掃描。
MySQL 本身不支持反向索引,但可以將字符串反轉後存儲,並基於此建立前綴索引。
索引失效
1、沒有遵守最左前綴原則。
2、使用了範圍查詢(>,<,<>,!=,between and ,like),該條件查詢右邊的列都會失效。
3、如果列類型是字符串,那一定要在條件中將數據使用引號引用起來,否則不使用索引。
4、表中數據量比較小的時候,MySQL 優化引擎,會決定不實用索引。
5、使用 or 進行查詢,聯合索引會失效。
6、不在索引列上做任何操作(計算,函數,(自動或者手動)類型裝換),會導致索引失效而導致全表掃描。
索引與鎖
1、索引可以讓查詢鎖定更少的行,InnoDB 只有在訪問行的時候才會對其加鎖,而索引能夠減少 InnoDB 訪問的行數,從而減少鎖的數量。
2、如果索引無法過濾掉無效的行,那麼在 InnoDB 檢索到數據並返回給伺服器層以後,MySQL 伺服器才能應用 Where 子句,InnoDB 已經鎖定這些行,到適當的時候才能釋放。
3、如果不能使用索引查找和鎖定行的話問題會更糟糕,MySQL 會做全表掃描並鎖定所有行。
4、InnoDB 在二級索引上使用共享鎖,在主鍵索引使用排它鎖。
其他索引
hash 索引
1、hash 索引基於哈希表實現,只有精確匹配索引的所有列的查詢才會生效。
2、Hash 索引將所有哈希值存儲在索引中,同時保持指向每個數據行的指針。
3、Hash 索引結構非常緊湊,查找速度非常快。
4、Memory 引擎支持非唯一的哈希索引。
5、InnoDB 有一個功能叫 「 自適應哈希索引 」,當它注意到某些列索引值被使用的非常頻繁時,會在內存中基於 Btree 索引之上再建一個 hash 索引,以提高訪問速度
空間數據索引
MyISAM 表支持空間索引,可以作為地理數據存儲。但 MySQL 對 GIS 支持不夠全面。
全文索引
1、全文索引是一種特殊類型的索引,他查找的是文中的關鍵詞,而不是直接比較索引中的值。
2、全文索引有很多的細節需要調整,比如分詞、停用詞、詞幹、複數、布爾查詢等。
3、MySQL 對空間數據索引和全文索引支持都不是很好,如果有此功能需求,建議使用其他存儲引擎,如 monogDB 或基於 lucene 的solr、es。
總結