本文主要說的是索引失效問題
創建表
create table if not exists staffs(
id int primary key auto_increment,
name varchar(24) not null default '' comment '姓名',
age int not null default 0 comment '年齡',
pos varchar(20) not null default '' comment '職位',
add_time timestamp not null default current_timestamp comment '入職時間'
)comment '員工記錄表';
insert into staffs(name, age, pos, add_time) values ('z3', 22, 'manager', NOW());
insert into staffs(name, age, pos, add_time) values ('July', 23, 'dev', NOW());
insert into staffs(name, age, pos, add_time) values ('2000', 23, 'dev', NOW());
添加索引
alter table staffs add index idx_staffs_nameAgePos(name, age, pos);
1、左前綴法則
先看一個正常的
explain select * from staffs where name = 'July' and age = 23 and pos = 'dev';
結果如下
由ref下的三個const可知,三個索引都被使用到了,key_len的長度也可以體現。說明這個是正常的sql查詢。
1)帶頭索引不能少
name字段的索引是帶頭索引,這裏不使用name字段的索引。
explain select * from staffs where age = 23 and pos = 'dev';
結果如下
可知type類型變成了最糟糕的ALL,且key_len的長度爲null,說明三個索引都沒有用到。
2)中間索引不能丟
age字段的索引爲中間索引,所以使用age字段的索引。
explain select * from staffs where name = 'July' and pos = 'dev';
結果如下
由ref下的一個const可知,索引只使用到了一個,也就是帶頭索引。
3)結論
綜上,如果可以全值匹配就全值匹配(條件字段和索引一樣),要遵守左前綴法則,查詢從索引的最左前列開始並且不跳過索引中的列。
2、 不在索引列上做任何操作
這裏的操作包括計算、函數、類型轉換,會導致索引失效而轉向全表掃描。
select * from staffs where name = 'July';
select * from staffs where left(name, 4) = 'July';
執行這兩條sql都可以得到想要的結果。
執行
explain select * from staffs where left(name, 4) = 'July';
結果如下
會發現索引失效了。
3、 少用範圍查詢
執行
explain select * from staffs where name = 'July' and age = 23;
explain select * from staffs where name = 'July' and age > 23;
結果如下
type類型由ref降低爲range。
4、減少使用select *
儘量使用覆蓋索引(只訪問索引的查詢(索引列和查詢列一致)),減少select *
explain select * from staffs where name = 'July' and age = 23;
只取想要的數據。
explain select name, age, pos from staffs where name = 'July' and age = 23;
結果如下
Extra出現了Using index,表明性能更優了。
5、不等於
使用不等於(!=或者<>)的時候無法使用索引會導致全表掃描。
explain select * from staffs where name = 'July' and age != 23;
type變爲ALL,索引失效了。
6、is null,is not null
is null,is not null也無法使用索引。
explain select * from staffs where name = 'July' and age is null;
創建字段的時候,儘量避免空值null。
6、like和通配符
like以通配符開頭(%abc)索引也會失效,變成全表掃描。
分別執行
explain select * from staffs where name = 'July';
explain select * from staffs where name like 'July';
explain select * from staffs where name like '%July';
結果如下
可以看見第三條sql的索引失效。所以like的時候,百分號加右邊。
如何解決like ‘%字符串%’ 時索引不會被使用?
使用覆蓋索引即可解決(只要查詢的字段是索引字段即可)。
可以看到type變爲了index,性能比ALL好了不少。
7、 字符串不加單引號
字符串不加單引號索引失效。
explain select * from staffs where name = 2000;
結果如下
type變成了ALL,這裏也說明了MySQL底層會自動進行類型轉換,隱性自動類型轉換,也是我們需要避免的。
字符串不加單引號還會導致一個很嚴重的問題,就是行鎖會升級爲表鎖。
8、 少用or
or也會導致索引失效。
explain select * from staffs where name = 'July' or name = 'z3';
可見索引失效了。