1. 程式人生 > 其它 >《MySQL面試小抄》索引失效場景驗證

《MySQL面試小抄》索引失效場景驗證

我是肥哥,一名不專業的面試官!

我是囧囧,一名積極找工作的小菜鳥!

囧囧表示:小白麵試最怕的就是面試官問的知識點太籠統,自己無法快速定位到關鍵問題點!!!


本期主要面試考點

面試官考點之什麼情況下會索引失效?

本期驗證以下索引失效的常見場景

1、like萬用字元,左側開放情況下,全表掃描
2、or條件篩選,可能會導致索引失效
3、where中對索引列使用mysql的內建函式,一定失效
4、where中對索引列進行運算(如,+、-、*、/),一定失效
5、型別不一致,隱式的型別轉換,導致的索引失效
6、where語句中索引列使用了負向查詢,可能會導致索引失效。負向查詢包括:NOT、!=、<>、!<、!>、NOT IN、NOT LIKE等,其中:!< !> SQLServer語法。
7、索引欄位可以為null,使用is null或is not null時,可能會導致索引失效
8、隱式字元編碼轉換導致的索引失效
9、聯合索引中,where中索引列違背最左匹配原則,一定會導致索引失效
10、MySQL優化器的最終選擇,不走索引

驗證準備

準備資料表,同時建立普通索引 idx_user_name

CREATE TABLE `t_user` (
  `id` int(11) NOT NULL,
  `user_name` varchar(32) CHARACTER DEFAULT NULL COMMENT '使用者名稱',
  `address` varchar(255) CHARACTER DEFAULT NULL COMMENT '地址',
  `create_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '建立時間',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

插入1萬條資料(注意:資料多些,mysql不走索引情況之一是資料量非常少,MySQL查詢優化器認為全表掃描比使用索引更快,導致索引失效,explain檢查是否使用索引時,發現無法走索引)

-- 建立儲存過程,插入10000使用者資訊
CREATE PROCEDURE user_insert()
-- 定義儲存過程開始
BEGIN
	-- 定義變數 i ,int 型別,預設值為 1
	DECLARE i INT DEFAULT 1;
	
	WHILE i <= 10000
		-- 定義迴圈內執行命令
		DO INSERT INTO t_user(id, user_name, address, create_time) VALUES(i, CONCAT('mayun', i), CONCAT('浙江杭州', i), now());
		SET i=i+1;
	END WHILE;
	
	COMMIT;
END;
-- 定義儲存過程結束


-- 呼叫儲存工程
CALL user_insert();

一、OR索引失效驗證

好多人說where條件中使用 or ,那麼索引一定失效,是否正確?

OR 連線的是同一個欄位,相同走索引

explain select * from t_user where user_name = 'mayun10' or user_name = 'mayun1000'

OR 連線的是兩個不同欄位,不同索引失效

explain select * from t_user where user_name = 'mayun10' or address = '浙江杭州12'

給address列增加索引

alter table t_user add index idx_address (address)

OR 連線的是兩個不同欄位,如果兩個欄位皆有索引,走索引

驗證總結

or 可能會導致索引失效,並非一定,這裡涉及到MySQL index merge 技術。

1、MySQL5.0之前,查詢時一個表一次只能使用一個索引,無法同時使用多個索引分別進行條件掃描。

2、但是從5.1開始,MySQL引入了 index merge 優化技術,對同一個表可以使用多個索引分別進行條件掃描。然後將它們各自的結果進行合併(intersect/union)。

or索引生效有哪些情況?

第一種 or兩邊連線的是同一個索引欄位

第二種 or兩邊連線的是兩個索引欄位,即兩個欄位分別都建立了索引

二、LIKE萬用字元索引失效驗證

一個最常見的查詢場景,建立idx_user_name索引

select * from t_user where user_name like '%mayun100%';

這條查詢是否走索引?

select * from t_user where user_name like 'mayun100%';

這條查詢是否走索引?

驗證總結

like 萬用字元特性是可以左右開閉匹配查詢當左邊開放使用 % 或者 _ 匹配的時候都不會走索引,會進行全表掃描

為什麼左開情況下會索引失效?請介紹一下原理!

我們知道建立索引後,MySQL會建立一棵有序的B+Tree,索引樹是有序的,索引列進行查詢匹配時是從左到右進行匹配。使用 % 和 _ 匹配,這表示左邊匹配值是不確定的。不確定,意味著充滿可能,怎麼比較?

當然只能一個一個的比較,那就相當於,全匹配了,全匹配在優化器看來,與其走索引樹查詢,再進行不斷的回表操作,還不如直接進行全表掃描划算!

三、where中對索引列使用mysql的內建函式

建立 idx_age 索引,

alter table t_user add index idx_age(age);

不使用內建函式

explain select * from t_user where age = 80

使用內建函式

explain select * from t_user where abs(age) = 80

驗證總結

如果對索引欄位做了函式操作,可能會破壞索引值的有序性,因此優化器就決定放棄走樹搜尋功能。

MySQL 無法再使用索引快速定位功能,而只能使用全索引掃描。

四、where中對索引列進行運算(如,+、-、*、/),一定失效

不涉及索引列的運算

alter table t_user add index idx_age(age);explain select * from t_user where age = 80;

索引列進行運算操作

explain select * from t_user where age + 5 = 80

五、型別不一致,隱式的型別轉換,導致的索引失效

alter table t_user add index idx_user_name(user_name);explain select * from t_user where user_name = 'mayun1';

修改資料,再次explain

update t_user set user_name = '100' where user_name = 'mayun1';explain select * from t_user where user_name = 100;

user_name = 100 ,因為user_name 欄位定義的是varchar,索引在where進行匹配時會先隱式呼叫 case() 函式進行型別轉換 將匹配條件變成,user_name = '100'

六、where語句中索引列使用了負向查詢,可能會導致索引失效。

負向查詢包括:NOT、!=、<>、!<、!>、NOT IN、NOT LIKE等,其中:!< !> SQLServer語法。

alter table t_user add index idx_age(age);explain select * from t_user where age in (100, 50);
explain select * from t_user where age not in (100, 50);

七、索引欄位可以為null,使用is null或is not null時,可能會導致索引失效

第一種情況,表結構規定允許user_name 欄位可以為null

explain select * from t_user where user_name is null;
explain select * from t_user where user_name is not null;

第二種情況,表結構規定user_name 欄位不可以為null

explain select * from t_user where user_name is null;
explain select * from t_user where user_name is not null;

八、隱式字元編碼轉換導致的索引失效

當兩個表進行連線JOIN 時,如果兩張表的字元編碼不同,可能會導致索引失效。

這個索引失效場景尚未遇到,網上很多文章說會導致索引失效,查閱發現大量的部落格說UTF8mb4字符集的表mb4與UTF8字符集的表utf8 關聯會產生索引失效的問題,但是我根據大量博文所述操作,發現暫時還是無法復現,讀者可自行查閱。

如果讀者復現到此場景,歡迎評論討論或關注如果讀者復現到此場景,歡迎評論或關注公眾號囧麼肥事討論

九、聯合索引中,where中索引列違背最左匹配原則,一定會導致索引失效

建立聯合做引 idx_user_name_deposit, 遵循最左匹配原則

alter table t_user add index idx_user_name_deposit(user_name, deposit);explain select * from t_user where user_name like 'mayun86%'

遵循最左匹配之 a b 型別

explain select * from t_user where user_name like 'mayun86%' and deposit = 5620.26;

調換索引位置,測試聯合索引書寫規則

explainselect*fromt_userwheredeposit=5620.26anduser_namelike'mayun86%';

違反最左匹配原則

explain select * from t_user where deposit = 5620.26;

驗證總結

聯合索引依據最左匹配原則建立索引樹,在查詢時依據聯合索引順序依次匹配索引值,查詢時如果違背最左匹配原則,將導致索引失效。

假設建立索引 idx_a_b_c,相當於建立了 (a), (a,b), (a,b,c)三個索引查詢匹配時匹配順序是 a b c 查詢時如果沒有 a 欄位篩選,那麼索引將失效

舉栗子,走索引情況

select * from test where a=1 select * from test where a=1 and b=2 select * from test where a=1 and b=2 and c=3

索引失效呢?

select * from test where b=2 and c=3

聯合索引如果要走索引,查詢條件中必須要包含第一個索引,否則索引失效

select * from test where b=1 and a=1select * from test where m='222' and a=1

這兩條查詢走索引的原因是什麼?

最左字首指的是查詢時匹配索引列要按照聯合索引建立的順序,但是在書寫時不需要嚴格按照聯合索引建立的順序,MySQL優化器會自動調整,所以上面兩條查詢索引有效!

十、MySQL優化器的最終選擇,不走索引

explain select * from t_user where age > 59;
explain select * from t_user where age > 99;

驗證總結

MySQL查詢索引失效的情況有很多,即使其他情況都規避,但是在經過了優化器的確定查詢方案的時候,依然可能索引失效。

優化器會考慮查詢成本,來確認它認為的最佳方案來執行查詢

當資料量較少,或者需要訪問行很多的時候

優化器會認為走索引樹來進行回表,還不如直接進行全表掃描的時候,優化器將會拋棄走索引樹。

隨緣更新,大神請繞路!

更多精彩內容,歡迎關注微信公眾號:囧麼肥事 (或搜尋:jiongmefeishi)