mysql 索引 最左前綴原則


參考1
參考2
最佳左前綴法則:帶頭大哥不能死、中間兄弟不能斷
索引法則--最佳左前綴法則

最佳左前綴法則學習和Demo演示

1 准備數據

1.1 建表
復制代碼

DROP TABLE IF EXISTS staff;
CREATE TABLE IF NOT EXISTS staff (
id INT PRIMARY KEY auto_increment,
name VARCHAR(50),
age INT,
pos VARCHAR(50) COMMENT '職位',
salary DECIMAL(10,2)
);

復制代碼

1.2 插入數據

INSERT INTO staff(name, age, pos, salary) VALUES('Alice', 22, 'HR', 5000);
INSERT INTO staff(name, age, pos, salary) VALUES('Bob', 22, 'RD', 10000);
INSERT INTO staff(name, age, pos, salary) VALUES('David', 22, 'Sale', 120000);

2 測試&Explain分析

2.1 創建索引

CREATE INDEX idx_nameAgePos ON staff(name, age, pos);

創建了一個基於 name, age, pos 三個字段的索引

2.2 索引測試

Case#1:只根據 name 字段來查詢

EXPLAIN SELECT * FROM staff WHERE name = 'Alice';

結果:

type=ref
key=索引
ref=const
ken_len=53

Case#2:只根據 name & age 字段來查詢

EXPLAIN SELECT * FROM staff WHERE name = 'Alice' AND age = 22;

結果:和 Case#1 差不多,但是:

key_len=58
ref=const, const

Case#3:根據 name & age & pos 來查詢

EXPLAIN SELECT * FROM staff WHERE name = 'Alice' AND age = 22 AND pos = 'HR';

結果:索引仍然生效,同時,key_len & ref 比 Case#2 中的結果更豐富

Case#4:根據 age & pos 來查詢

EXPLAIN SELECT * FROM staff WHERE age = 22 AND pos = 'HR';

結果:沒有索引,全表掃描

Case#5:根據 name & pos 來查詢

EXPLAIN SELECT * FROM staff where name = 'Alice' AND pos = 'HR';

結果:和 Case#1 相同(說明 pos 字段沒有用上索引)

3 總體分析

Case1,2,3都用了上索引,且使用索引長度依次增加(key_len=53,58,111 且 ref=1個const,2個const,3個const),符合最佳左前綴法則;

Case4中沒有帶頭大哥(火車頭),於是,全表掃描;

Case5中只有 name 字段使用上了索引,中間兄弟(中間車廂)age 斷了,於是,后面的兄弟(車廂)pos 掛了;

4 總結

最佳左前綴法則:帶頭大哥不能死、中間兄弟不能斷

當 select * from T where a = 1 and b = 3 的時候, 數據庫系統可以直接從索引文件中直接二分法找到 A = 1 的記錄,然后再 B = 3 的記錄
但如果你 where b = 3 則需要遍歷這個索引表的全部

mysql 建立多列索引(聯合索引)有最左前綴的原則,即最左優先,如:

如果有一個 2 列的索引 (col1, col2),則已經對 (col1)、(col1, col2) 上建立了索引;
如果有一個 3 列索引 (col1, col2, col3),則已經對 (col1)、(col1, col2)、(col1, col2, col3) 上建立了索引;
原理

b+ 樹的數據項是復合的數據結構,比如 (name,age,sex) 的時候,b+ 樹是按照從左到右的順序來建立搜索樹的,比如當 (張三,20,F) 這樣的數據來檢索的時候,b+ 樹會優先比較 name 來確定下一步的所搜方向,如果 name 相同再依次比較 age 和 sex,最后得到檢索的數據;但當 (20,F) 這樣的沒有 name 的數據來的時候,b+ 樹就不知道第一步該查哪個節點,因為建立搜索樹的時候 name 就是第一個比較因子,必須要先根據 name 來搜索才能知道下一步去哪里查詢

比如當 (張三, F) 這樣的數據來檢索時,b+ 樹可以用 name 來指定搜索方向,但下一個字段 age 的缺失,所以只能把名字等於張三的數據都找到,然后再匹配性別是 F 的數據了, 這個是非常重要的性質,即索引的最左匹配特性。(這種情況無法用到聯合索引)

mysql 查詢優化器

如果建的索引是 (name, cid)。而查詢的語句是 cid=1 AND name=’小紅’。為什么還能利用到索引?

當按照索引中所有列進行精確匹配(“=” 或 “IN”)時,索引可以被用到,並且 type 為 const。理論上索引對順序是敏感的,但是由於 MySQL 的查詢優化器會自動調整 where 子句的條件順序以使用適合的索引,所以 MySQL 不存在 where 子句的順序問題而造成索引失效
注意事項

范圍查詢
mysql 會一直向右匹配直到遇到范圍查詢(>、<、between、like)就停止匹配。范圍列可以用到索引,但是范圍列后面的列無法用到索引。即,索引最多用於一個范圍列,因此如果查詢條件中有兩個范圍列則無法全用到索引

like 語句的索引問題
如果通配符 % 不出現在開頭,則可以用到索引,但根據具體情況不同可能只會用其中一個前綴
在 like “value%” 可以使用索引,但是 like “%value%” 不會使用索引,走的是全表掃描

不要在列上進行運算
如果查詢條件中含有函數或表達式,將導致索引失效而進行全表掃描
例如 select * from user where YEAR(birthday) < 1990
可以改造成 select * from users where birthday <’1990-01-01′

索引不會包含有 NULL 值的列
只要列中包含有 NULL 值都將不會被包含在索引中,復合索引中只要有一列含有 NULL 值,那么這一列對於此復合索引就是無效的。所以在數據庫設計時不要讓字段的默認值為 NULL

盡量選擇區分度高的列作為索引,區分度的公式是 count(distinct col)/count(*),表示字段不重復的比例,比例越大我們掃描的記錄數越少,唯一鍵的區分度是 1,而一些狀態、性別字段可能在大數據面前區分度就是 0。一般需要 join 的字段都要求區分度 0.1 以上,即平均 1 條掃描 10 條記錄

覆蓋索引的好處
如果一個索引包含所有需要的查詢的字段的值,我們稱之為覆蓋索引。覆蓋索引是非常有用的工具,能夠極大的提高性能。因為,只需要讀取索引,而無需讀表,極大減少數據訪問量

鏈接:https://www.jianshu.com/p/9b3406bcb199


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM