MySQL爲什麼沒有走索引?是這些原因在搞鬼

1.列的離散性

  你知道嗎?即使你對數據庫中你要查詢的列添加了索引,它也有可能不會走索引。 這其實和一個叫 列的離散性 相關的問題。在數據庫表中,MySQL 在查詢時,會對錶中查詢的列進行離散性計算。計算出的離散性結果越大,說明這一列的離散型越好,選擇性就越好。 列的離散性計算公式爲:count(distinct col) : count(col)

我們來計算一下下圖三列的離散性:
在這裏插入圖片描述
爲什麼說:離散性越好,選擇性就越好呢?

  我們將上圖 sex 列進行 B+Tree 二叉樹轉換(男:0 女:1)。:提供一個很好用的國外數據結構模擬網站:我是鏈接。轉換後的二叉樹如下圖所示:
在這裏插入圖片描述
  比如:現在我們要查詢性別爲的用戶。會取1進行層層比對。當做到上圖中標紅節點時,發現左中右哪一路都可以走,選擇性更多了。MySQL 查詢優化器會認爲搜尋這麼多的數據,離散性這麼差,可選擇性也很差,選擇優化器認爲還不如用全表掃描呢

  離散性越高,選擇性就會越好,查詢效率顯然也會越高,索引的存在也就起到了作用。

記住這個值:【10%-15%】:

  ​離散性在不超過全表的【10%-15%】的前提下索引纔可以顯示其所具有的價值。當離散度超過該值的情況下全表掃描可能反倒比索引掃描更有效。我們所追求的目標就是創建全表掃描所無法比擬的有效索引。

2.最左匹配原則

  當我們在創建數據庫時,會同步選擇字符集排序規則這些選項。
在這裏插入圖片描述

  針對排序規則,此處你或許會有一個誤區:並不是只有數字類型可以排序,字符串等也是可以排序的。比如說現在數據庫表有一個 name 字段,並且我們對該字段創建了一個name索引。該字段存在這些數據:abc 、kut 、abg 、 oop,將這些數據轉換爲二叉樹類似下圖所示(MySQL使用的是B+Tree作爲索引。此處爲了演示,下圖並未使用B+Tree)
在這裏插入圖片描述
  最左匹配原則,會對索引中的關鍵字進行計算(對比),一定是從左到右依次進行,且不可跳過。我們就以 adc這個名字爲例查詢。根據規則我們比如計算出 abc 的 ascii 碼爲 a=97,b=98,c=99adc 的 ascii 碼爲 a=97,d=100,c=99;

  根據最左匹配原則,搜索 adc時,會對關鍵字一個一個進行對比。第一個關鍵字發現 97 = 97;便會繼續對第二個關鍵字比較,發現 100 > 98,則會去右節點進行查找。這就是所謂的最左匹配原則。

  英文、字符串、中文 等,都是可以用來被對比的。具體如何對比,這就取決於你在創建數據庫、創建表時,選擇的是怎樣的一個排序規則了。

3.聯合索引

  爲了提高數據庫效率,建索引是家常便飯;那麼當查詢條件爲2個及以上時,此時我們就用到了聯合索引的概念了。切記:聯合索引 ≠ 兩個索引,它也是一個索引

單列索引:
  比如有一個 name 單列索引,即索引關鍵字只有一個 [name]
 
聯合索引:
  比如有一個 name、mobileNo 聯合索引,那麼索引關鍵字就是 [name,mobileNo]
 
你可以理解爲:單列索引,是一個特殊的聯合索引。

聯合索引列選擇原則

  1. 經常用於查詢的列優先;【最左匹配原則】
  2. 離散性高的列優先(選擇性更高,效率更好);【離散性原則】
  3. 寬度小的列優先。【最少空間原則】

來個小測試:

有如下兩條查詢語句:

  1. select * from user where name = xxx;
  2. select * from user where name = xxx and mobileNo = xxx;

  我們如果創建一個 name單列索引、再來一個name、mobileNo聯合索引。雖然沒有任何問題,但是通過最左匹配原則,我們就可以理解 name單列索引在此處其實是一個冗餘的索引。

好文附上:什麼時候走單列索引,什麼時候走聯合所有,以及它們的關聯區別,請參考:多個單列索引和聯合索引的區別詳解

4.覆蓋索引

  聯合索引的存在,就引出了覆蓋索引的概念。

  如果查詢列可通過索引節點中的關鍵字直接返回,則該索引就稱之爲覆蓋索引。覆蓋索引的出現,可減少數據庫的 I/O 操作,將隨機 I/O 變爲 順序 I/O,從而提高查詢性能。

示例:
  現在有一個 [name,mobileNo] 聯合索引,如果我們通過以下語句來查詢:select name,mobileNo from user where name = xxx。它會直接命中索引,直接從索引的結構中將數據返回,而不再需要遍歷到葉子節點去獲取數據,從而大大提高查詢效率。

  select name,mobileNo from user where name = xxx and mobileNo =xxx;這種語句也可以直接命中覆蓋索引,並直接返回數據。

  select name,mobileNo from user where name = xxx and age=xx;這種語句就不會命中覆蓋索引了。因爲它即使命中了 name 字段的索引,但是並沒有命中 age 字段的索引,所以他不會命中覆蓋索引。

索引相關總結

  1. 索引列的數據長度能少則少;
  2. 索引一定不是越多越好,越全越好,一定是建合適的;
  3. 匹配列前綴可用到索引 like 9999%,like %9999%、like %9999用不到索引;

    注意: like 9999% 並不會一定用到索引(離散型太差,就不會用到索引)
        like %9999%、like %9999 是絕對不會用到索引的

  4. Where 條件中 not in 和 <>操作無法使用索引;
  5. 匹配範圍值,order by 也可用到索引;
  6. 多用指定列查詢,只返回自己想到的數據列,少用select *;
  7. 聯合索引中如果不是按照索引最左列開始查找,無法使用索引;
  8. 聯合索引中精確匹配最左前列並範圍匹配另外一列可以用到索引;

    比如說:有個[name,phone]聯合索引
        where name=‘abc’ and phone>‘136xxxxxxxx’ 這種是可以用到索引的

  9. 聯合索引中,如果查詢中有某個列的範圍查詢,則其右邊的所有列都無法使用索引;

    比如說:有個[age,name]聯合索引
        where age>18 and name='abc’這是用不到索引的
        它只會age使用索引,name不會使用索引。還是基於最左匹配原則


博主寫作不易,加個關注唄

求關注、求點贊,加個關注不迷路 ヾ(◍°∇°◍)ノ゙

博主不能保證寫的所有知識點都正確,但是能保證純手敲,錯誤也請指出,望輕噴 Thanks♪(・ω・)ノ

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章