文章目录
建表sql
CREATE TABLE staffs (
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR (24) 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 '入职时间'
) CHARSET utf8 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());
INSERT INTO staffs(NAME,age,pos,add_time) VALUES(null,23,'dev',NOW());
SELECT * FROM staffs;
ALTER TABLE staffs ADD INDEX idx_staffs_nameAgePos(name, age, pos);
(案例)索引失效
全值匹配我最爱。
最佳左前缀法则。
不在索引列上做任何操作(计算、函数、(自动or手动)类型转换),会导致索引失效而转向全表扫描。
索引中范围条件右边的字段会全部失效。
尽量使用覆盖索引(只访问索引的查询,索引列和查询列一致),减少SELECT *。
MySQL在使用!=或者<>的时候无法使用索引会导致全表扫描。
is not null无法使用索引,但是is null是可以使用索引的
like以通配符开头%abc索引失效会变成全表扫描。
字符串不加单引号索引失效。
少用or,用它来连接时会索引失效。
全值匹配我最爱。
- 索引 idx_staffs_nameAgePos 建立索引时 以 name ,age,pos 的顺序建立的。全值匹配表示 按顺序匹配的
EXPLAIN SELECT * FROM staffs WHERE NAME = 'July';
Using index condition指的是查找使用了索引,但是需要回表查询数据
EXPLAIN SELECT * FROM staffs WHERE NAME = 'July' AND age = 25;
EXPLAIN SELECT * FROM staffs WHERE NAME = 'July' AND age = 25 AND pos = 'dev';
最佳左前缀法则
- and忽略左右关系。既即使没有没有按顺序 由于优化器的存在,会自动优化。
EXPLAIN SELECT * FROM staffs WHERE age = 25 AND name = 'July';
- 当使用覆盖索引的方式时,(select name/age/pos/id from staffs where age=10 (后面没有其他没有索引的字段条件)),即使不是以name开头,也会使用idx_nameAge索引。
EXPLAIN SELECT id FROM staffs WHERE age = 25
EXPLAIN SELECT pos FROM staffs WHERE age = 25
- 既select后的字段 有索引,where 后的字段也有索引,则无关执行顺序。
- 除开上述条件 才满足最左前缀法则。
EXPLAIN SELECT * FROM staffs WHERE age = 25 #没用到索引
EXPLAIN SELECT * FROM staffs WHERE name = 'July' and age = 25 # 用到索引
- 左前缀"失效"的情况
有种情况可能会忽略,当表的字段比较少(只有3,4个左右),恰好建索引把所有的字段包含进去了,select *会走覆盖索引的规则
特殊情况
EXPLAIN select * from staffs where name > '33'
不在索引列上做任何操作(计算、函数、(自动or手动)类型转换),会导致索引失效而转向全表扫描
EXPLAIN SELECT * FROM staffs WHERE left(name,4)='July'
索引中范围条件右边的字段会全部失效
EXPLAIN SELECT * FROM staffs WHERE name = 'July' and age=14 and pos = 'dev'
EXPLAIN SELECT * FROM staffs WHERE name = 'July' and age>14 and pos = 'dev'
说明没用到pos字段的索引
尽量使用覆盖索引(只访问索引的查询,索引列和查询列一致),减少SELECT *
EXPLAIN select name,age,pos from staffs where name = 'July' and age = 25 and pos = 'dev'
EXPLAIN select * from staffs where name = 'July' and age = 25 and pos = 'dev'
EXPLAIN select name,age,pos from staffs where name = 'July' and age > 25 and pos = 'dev'
EXPLAIN select name,age,pos from staffs where name = 'July' and age = 25
EXPLAIN select name from staffs where name = 'July' and age = 25
EXPLAIN select pos from staffs where age = 25
MySQL在使用!=或者<>的时候无法使用索引会导致全表扫描
EXPLAIN select * from staffs where name <> 'July'
EXPLAIN select * from staffs where name = 'July' and age !=1
EXPLAIN select * from staffs where name = 'July' and age =1 and pos<>'dev'
is not null无法使用索引,但是is null是可以使用索引的
EXPLAIN select * from staffs where name is null
EXPLAIN select * from staffs where name is null and age =1 and pos='dev'
EXPLAIN select * from staffs where name is not NULL
EXPLAIN select * from staffs where name = 'July' and age = 1 and pos is not null
上面有个问题不太懂,type的类型不知道为啥变成range,希望有懂的朋友在评论区说一下
like以通配符开头%abc索引失效会变成全表扫描。
EXPLAIN select * from staffs where name like '%July%'
EXPLAIN select * from staffs where name like '%July'
EXPLAIN select * from staffs where name like 'July%'
like ‘abc%’ type 类型为 range ,算是范围,可以使用索引
EXPLAIN select * from staffs where name like 'July%' and age=1
问题:解决like’%字符串%'时索引不被使用的方法
- 如果一定要使用%like,而且还要保证索引不失效,那么使用覆盖索引来编写SQL
字符串不加单引号索引失效
底层隐式转换导致索引失效
EXPLAIN select * from staffs where name = 1
EXPLAIN select * from staffs where name = '1'
少用or,用它来连接时会索引失效
EXPLAIN select * from staffs where name = '1' or name = 'July'
EXPLAIN select * from staffs where name = 'July' and age = 11 or age = 23
EXPLAIN select pos from staffs where name = 'July' and age = 11 or age = 23 # 覆盖索引
热身case
- 建表语句
create table test03(
id int primary key not null auto_increment,
c1 char(10),
c2 char(10),
c3 char(10),
c4 char(10),
c5 char(10)
);
insert into test03(c1,c2,c3,c4,c5) values('a1','a2','a3','a4','a5');
insert into test03(c1,c2,c3,c4,c5) values('b1','b2','b3','b4','b5');
insert into test03(c1,c2,c3,c4,c5) values('c1','c2','c3','c4','c5');
insert into test03(c1,c2,c3,c4,c5) values('d1','d2','d3','d4','d5');
insert into test03(c1,c2,c3,c4,c5) values('e1','e2','e3','e4','e5');
select * from test03;
【建索引】
create index idx_test03_c1234 on test03(c1,c2,c3,c4);
show index from test03;
例子
explain select * from test03 where c1='a1' and c2='a2' and c3='a3' and c4='a4';
explain select * from test03 where c1='a1' and c2='a2' and c4='a4' and c3='a3';
explain select * from test03 where c1='a1' and c2='a2' and c3>'a3' and c4='a4';
explain select * from test03 where c1='a1' and c2='a2' and c4>'a4' and c3='a3';
explain select * from test03 where c1='a1' and c2='a2' and c4='a4' order by c3;
c3作用在排序而不是查找
explain select * from test03 where c1='a1' and c2='a2' order by c3;
explain select * from test03 where c1='a1' and c2='a2' order by c4;
出现了filesort
explain select * from test03 where c1='a1' and c5='a5' order by c2,c3;
只用c1一个字段索引,但是c2、c3用于排序,无filesort
explain select * from test03 where c1='a1' and c5='a5' order by c3,c2;
出现了filesort,我们建的索引是1234,它没有按照顺序来,3 2 颠倒了
explain select * from test03 where c1='a1' and c2='a2' order by c2,c3;
explain select * from test03 where c1='a1' and c2='a2' and c5='a5' order by c2,c3;
用c1、c2两个字段索引,但是c2、c3用于排序,无filesort
explain select * from test03 where c1='a1' and c2='a2' and c5='a5' order by c3,c2;
因为c2为常量,所以order by后面不会出现filesort
explain select * from test03 where c1='a1' and c5='a5' order by c3,c2;
explain select * from test03 where c1='a1' and c4='a4' group by c2,c3;
explain select * from test03 where c1='a1' and c4='a4' group by c3,c2;
一般性建议
- 对于单值索引,尽量选择针对当前query过滤性更好的索引。
- 在选择复合索引的时候,当前query中过滤性最好的字段在索引字段顺序中,位置越靠前越好。(避免索引过滤性好的索引失效)
- 在选择复合索引的时候,尽量选择可以能够包含当前query中的where子句中更多字段的索引。
- 尽可能通过分析统计信息和调整query的写法来达到选择合适索引的目的。
口诀:
- 带头大哥不能死。
- 中间兄弟不能断。
- 索引列上不计算。
- 范围之后全失效。
- 覆盖索引尽量用。
- 不等有时会失效。
- like百分加右边。
- 字符要加单引号。
- 一般SQL少用or。