我是肥哥,一名不專業的面試官!
我是囧囧,一名積極找工作的小菜鳥!
囧囧表示:小白面試最怕的就是面試官問的知識點太籠統,自己無法快速定位到關鍵問題點!!!
本期主要面試考點
面試官考點之什么情況下會索引失效?
本期驗證以下索引失效的常見場景
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;
調換索引位置,測試聯合索引書寫規則
explain select * from t_user where deposit = 5620.26 and user_name like '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)