MySQL避免索引失效

create table 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 '入职时间'
)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());

select * from staffs;

alter table staffs add index idx_staffs_nameAgePos(name,age,pos);

这里我建立索引的顺序是name,age,pos。name是大哥,在最前边

  • 全值匹配我最爱
  • 最佳左前缀法则,如果索引了多列,要遵守最左前缀法则。指的是查询从索引的最左前列开始并且不跳过索引中的列。(带头大哥不能死,中间兄弟不能断)
-- 有带头大哥
EXPLAIN select * from staffs where name='July';
EXPLAIN select * from staffs where name='July' AND age=25;
EXPLAIN select * from staffs where name='July' AND age=25 and pos='dev';

这里写图片描述

可以看到在有带头大哥的情况下,第一条记录用到了name索引,第二条记录用到了name和age索引,第三条记录用到了name,age,pos索引。可以看到key_len的长度越来越长。

-- 没有带头大哥
EXPLAIN SELECT * FROM staffs WHERE age=23 AND pos='dev';
EXPLAIN SELECT * FROM staffs WHERE pos='dev';

这里写图片描述

在没有带头大哥的情况下,索引都没有用到。

-- 有带头大哥但中间断了
EXPLAIN select * from staffs where name='July' AND pos='dev';

这里写图片描述

在有带头大哥的情况下,中间断了。可以看到只用到了name索引。因为如果name和pos索引都用到了的话,key_len应该比74大。

  • 不在索引列上做任何操作(计算、函数、(自动or手动)类型转换),会导致索引失效而转向全表扫描

15-20k的程序员可以做出符合需求的功能。20k以上的程序员要考虑性能。

EXPLAIN select * from staffs where name='July';
-- 其中left(name,4)这个函数的意思是找name字段中从左数4个字符的名字
EXPLAIN select * from staffs where left(name,4)='July';

其实这两条SQL达到的效果是一样的,但第二条用到了函数进行计算,导致了索引失效

这里写图片描述

  • 存储引擎不能使用索引范围条件右边的列
EXPLAIN select * from staffs where name='July' AND age>25 and pos='dev';

这里写图片描述
可以发现其中name和age的索引用到了,但pos的索引没有用到。这里type为range。

  • 尽量使用覆盖索引(只访问索引的查询(索引列和查询列一致)),减少select *
EXPLAIN select name,age,pos from staffs where name='July' AND age=25 and pos='dev';

这里写图片描述

这里我没有用select *,而是改成了具体的字段,可以发现Extra中多了个Using index,有using index是比较好的。

EXPLAIN select name,age,pos from staffs where name='July' AND age>25 and pos='dev';

这里写图片描述

可以发现这里的type由range变成了ref性能更好了。

EXPLAIN select name,age,pos from staffs where name='July' AND age=25;

这里写图片描述

这里的Extra中多了Using index

  • mysql在使用不等于(!=或者<>)的时候无法使用索引会导致全表扫描

  • is null,is not null也无法使用索引

这里写图片描述

  • like以通配符开头(‘%abc…’)mysql索引失效会变成全表扫描的操作。问题:解决like‘%字符串%’时索引不被使用的方法?
EXPLAIN select * from staffs where name like '%July%';
EXPLAIN select * from staffs where name like '%July';
EXPLAIN select * from staffs where name like 'July%';

结论:这三条语句中前两条的索引都会失效。而第三条在name上建立的索引不会失效,为range类型的索引。索引使用like的时候%分号要加在右边,不要加在左边。

但如果我非要在左边加%号呢?怎么解决?

create table tbl_user(
	id int(11) not null auto_increment,
	name varchar(20) default null,
	age int(11) default null,
	email varchar(20) default null,
	primary key(id)
)engine=innodb auto_increment=1 default charset=utf8;

#drop table tbl_user

insert into tbl_user(name,age,email) values('1aa1',21,'b@163.com');
insert into tbl_user(name,age,email) values('2aa2',222,'a@163.com');
insert into tbl_user(name,age,email) values('3aa3',265,'c@163.com');
insert into tbl_user(name,age,email) values('4aa4',21,'d@163.com');
insert into tbl_user(name,age,email) values('aa',121,'e@163.com');

用覆盖索引来解决

我对name和age建立索引之后

EXPLAIN SELECT id from tbl_user WHERE name LIKE '%aa%';
EXPLAIN SELECT name from tbl_user WHERE name LIKE '%aa%';
EXPLAIN SELECT age from tbl_user WHERE name LIKE '%aa%';
EXPLAIN SELECT id,name from tbl_user WHERE name LIKE '%aa%';
EXPLAIN SELECT id,name,age from tbl_user WHERE name LIKE '%aa%';
EXPLAIN SELECT name,age from tbl_user WHERE name LIKE '%aa%';

执行这些语句都不会全表扫描,因为要查询的字段都在索引里边

这里写图片描述
而执行

EXPLAIN SELECT * from tbl_user WHERE name like '%aa%';
EXPLAIN SELECT id,name,age,email from tbl_user WHERE name LIKE '%aa%';

就会全表扫描,因为email字段不在索引里边

  • 字符串不加单引号索引失效
SELECT * from staffs where name='2000';
SELECT * from staffs where name=2000;

这两条语句都会查询出正确结果,但第二条没有用到索引

因为mysql会在底层对其进行隐式的类型转换

  • 少用or,用它来连接时会索引失效
EXPLAIN SELECT * FROM staffs WHERE name='July' or name='z3';

这条语句不会使用索引,会全表扫描

练习

假设index(a,b,c)

where语句索引是否被使用
where a=3使用到a
where a=3 and b=5使用到a和b
where a=3 and b=5 and c=4使用到a,b,c
where b=3 或者 where b=3 and c=4 或者where c=4没有使用到
where a=3 and c=5使用到a,但是c不可以,b中间断了
where a=3 and b>4 and c=5使用到a和b,c不能用在范围之后
where a=3 and b like ‘kk%’ and c=4使用到a和b和c,这里like为范围但和大于号的那个范围不一样
where a=3 and b like ‘%kk’ and c=4使用到a
where a=3 and b like ‘%kk%’ and c=4使用到a
where a=3 and b like ‘k%kk%’ and c=4使用到a和b和c

一般性建议

  • 对于单键索引,尽量选择针对当前query过滤性更好的索引
  • 在选择组合索引的时候,当前Query中过滤性最好的字段在索引字段顺序中,位置越靠前越好。
  • 在选择组合索引的时候,尽量选择可以能够包含当前query中的where子句中更多字段的索引
  • 尽可能通过分析统计信息和调整query的写法来达到选择合适索引的目的
  • mysql一次查询只能使用一个索引。如果要对多个字段使用索引,建立复合索引

口诀

  • 全职匹配我最爱,最左前缀要遵守;
  • 带头大哥不能死,中间兄弟不能断
  • 索引列上少计算,范围之后全失效
  • like百分写最右,覆盖索引不写星
  • 不等空值还有or,索引失效要少用
  • var引号不可丢,SQL高级也不难
  • 9
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 13
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值