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=99
;adc
的 ascii 碼爲 a=97,d=100,c=99
;
根據最左匹配原則,搜索 adc
時,會對關鍵字一個一個進行對比。第一個關鍵字發現 97 = 97;便會繼續對第二個關鍵字比較,發現 100 > 98,則會去右節點進行查找。這就是所謂的最左匹配原則。
英文、字符串、中文 等,都是可以用來被對比的。具體如何對比,這就取決於你在創建數據庫、創建表時,選擇的是怎樣的一個排序規則了。
3.聯合索引
爲了提高數據庫效率,建索引是家常便飯;那麼當查詢條件爲2個及以上時,此時我們就用到了聯合索引
的概念了。切記:聯合索引 ≠ 兩個索引,它也是一個索引
單列索引:
比如有一個 name 單列索引,即索引關鍵字只有一個 [name]
聯合索引:
比如有一個 name、mobileNo 聯合索引,那麼索引關鍵字就是 [name,mobileNo]
你可以理解爲:單列索引,是一個特殊的聯合索引。
聯合索引列選擇原則:
- 經常用於查詢的列優先;【最左匹配原則】
- 離散性高的列優先(選擇性更高,效率更好);【離散性原則】
- 寬度小的列優先。【最少空間原則】
來個小測試:
有如下兩條查詢語句:
- select * from user where name = xxx;
- 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 字段的索引,所以他不會命中覆蓋索引。
索引相關總結
- 索引列的數據長度能少則少;
- 索引一定不是越多越好,越全越好,一定是建合適的;
- 匹配列前綴可用到索引 like 9999%,like %9999%、like %9999用不到索引;
注意: like 9999% 並不會一定用到索引(離散型太差,就不會用到索引)
like %9999%、like %9999 是絕對不會用到索引的 - Where 條件中 not in 和 <>操作無法使用索引;
- 匹配範圍值,order by 也可用到索引;
- 多用指定列查詢,只返回自己想到的數據列,少用select *;
- 聯合索引中如果不是按照索引最左列開始查找,無法使用索引;
- 聯合索引中精確匹配最左前列並範圍匹配另外一列可以用到索引;
比如說:有個[name,phone]聯合索引
where name=‘abc’ and phone>‘136xxxxxxxx’ 這種是可以用到索引的 - 聯合索引中,如果查詢中有某個列的範圍查詢,則其右邊的所有列都無法使用索引;
比如說:有個[age,name]聯合索引
where age>18 and name='abc’這是用不到索引的
它只會age使用索引,name不會使用索引。還是基於最左匹配原則
博主寫作不易,加個關注唄
求關注、求點贊,加個關注不迷路 ヾ(◍°∇°◍)ノ゙
博主不能保證寫的所有知識點都正確,但是能保證純手敲,錯誤也請指出,望輕噴 Thanks♪(・ω・)ノ