我是肥哥,一名不專業的面試官!
我是囧囧,一名積極找作業的小菜鳥!
囧囧表示:小白面試最怕的就是面試官問的知識點太籠統,自己無法快速定位到關鍵問題點!!!
本期主要面試考點
面試官考點之什么情況下會索引失效?
本期驗證以下索引失效的常見場景
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)

轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/288710.html
標籤:其他
上一篇:一個SQLServer中JSON檔案型資料的查詢問題
下一篇:萊姆達表達試——查詢篇
