索引优化
1.索引失效(应该避免)
数据
CREATE TABLE staffs( id INT(11) PRIMARY KEY AUTO_INCREMENT, `name` VARCHAR(24) NOT NULL DEFAULT '' COMMENT '姓名', age INT(11) 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());
创建索引
CREATE INDEX index_nap ON staffs(name,age,pos)
索引失效案例
1.全值匹配我最爱(个数及顺序)
查看索引 SHOW INDEX FROM staffs
索引index_nap 顺序 Seq_in_index
name age pos
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';
从age开始查询
EXPLAIN SELECT * FROM staffs WHERE age=25 AND pos = 'dev';
没有用到索引 全表扫描
从pos开始查询 EXPLAIN SELECT * FROM staffs WHERE pos = 'dev';
没有用到索引 全表扫描
只查询name
EXPLAIN SELECT * FROM staffs WHERE `name` = 'July';
2.**最左前缀法则**
如果索引了多列,要遵守最左前缀法则。指的是查询从索引的最左前列开始并且不跳过索引中的列
口诀:带头大哥不能死,中间兄弟不能断
跳过中间索引列
EXPLAIN SELECT * FROM staffs WHERE `name` = 'July' AND pos = 'dev';
结论:只用到了一部分索引,且只用到了第一个name列的索引
3.不在索引列上做任何操作(计算、函数、(自动 or 手动)类型转换),会导致索引失效而转向全表扫描
对索引第一列name使用mysql函数
EXPLAIN SELECT * FROM staffs WHERE LEFT(`name`,4) = 'July';
结论:在索引列上使用了函数,转向了全表扫描
口诀:索引列上少计算
4.存储引擎不能使用索引中范围条件右边的列(like属于范围查询)
age为范围查询
EXPLAIN SELECT * FROM staffs WHERE `name` = 'July' AND age>25 AND pos = 'dev';
结论:name是检索,age是一个范围,主要用于排序,age之后不是精确检索
口诀:范围之后全失效
5.尽量使用覆盖索引(只访问索引的查询(索引列和查询列一致)),减少select *
不使用* 而是使用索引上的字段
EXPLAIN SELECT `name`,age,pos FROM staffs WHERE `name` = 'July' AND age=25 AND pos = 'dev';
age为范围查找
EXPLAIN SELECT `name`,age,pos FROM staffs WHERE `name` = 'July' AND age>25 AND pos = 'dev';
与之前的select * 对比
EXPLAIN SELECT * FROM staffs WHERE `name` = 'July' AND age>25 AND pos = 'dev';
截图按sql顺序排列
EXPLAIN SELECT `name` FROM staffs WHERE `name` = 'July' AND age=25 AND pos = 'dev'; EXPLAIN SELECT age FROM staffs WHERE `name` = 'July' AND age=25 AND pos = 'dev'; EXPLAIN SELECT pos FROM staffs WHERE `name` = 'July' AND age=25 AND pos = 'dev';
只查询其中一个也可以,截图如下 三个sql一样
结论:使用覆盖索引会从索引中直接查询到数据
口诀:覆盖索引查询好(自己想的)
6.mysql在使用不等于(!= 或者 <>) 的时候无法使用索引会导致全表扫描
EXPLAIN SELECT * 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 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';
结论:使用*查询 !=和<>,全部索引失效, 使用覆盖索引查询之后 !=和<>之后的索引失效
口诀:
7.is null ,is not null 也无法使用索引
EXPLAIN SELECT * FROM staffs WHERE `name` IS NULL ;
EXPLAIN SELECT * FROM staffs WHERE `name` IS NOT NULL ;
对比是否使用了覆盖索引
EXPLAIN SELECT `name`,age,pos,add_time FROM staffs WHERE `name` IS NOT NULL ;
EXPLAIN SELECT `name`,age,pos FROM staffs WHERE `name` IS NOT NULL ;
结论:注意覆盖索引,不使用覆盖索引前提下都会失效
8.like 以通配符开头('%abc')mysql索引失效会变成全表扫描的操作 (like 也相当于范围查询)
MySQL 5.7.33版本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%'
口诀:百分like加右边
问题:解决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 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');
未建立索引
EXPLAIN SELECT * FROM tbl_user WHERE `name` LIKE '%aa%'
EXPLAIN SELECT id,`name`,age,email 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 `name` FROM tbl_user WHERE `name` LIKE '%aa%'
EXPLAIN SELECT age FROM tbl_user WHERE `name` LIKE '%aa%'
EXPLAIN SELECT id FROM tbl_user WHERE `name` LIKE '%aa%'
统统都是下面的全表扫描
name与age字段创建联合索引
CREATE INDEX index_na ON tbl_user(`name`,age)
查看之前的查询
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 `name` FROM tbl_user WHERE `name` LIKE '%aa%' EXPLAIN SELECT age FROM tbl_user WHERE `name` LIKE '%aa%' EXPLAIN SELECT id FROM tbl_user WHERE `name` LIKE '%aa%'
覆盖索引上面EXPLAIN都是下面的情况
EXPLAIN SELECT * FROM tbl_user WHERE `name` LIKE '%aa%' EXPLAIN SELECT id,`name`,age,email FROM tbl_user WHERE `name` LIKE '%aa%'
无法使用覆盖索引之后 都是全表扫描
结论:覆盖索引来解决like '%字符串%'时索引不被使用的情况
9.字符串不加单引号索引失效
EXPLAIN SELECT * FROM staffs WHERE `name`=2000
EXPLAIN SELECT * FROM staffs WHERE `name`='2000'
**结论:千万不要用隐形的类型转换 会导致索引失效
口诀:字符串里有引号
10.少用or,用它来连接时会索引失效
EXPLAIN SELECT * FROM staffs WHERE `name`='July' or `name`='z3'
***口诀
全值匹配我最爱,最左前缀要遵守;
带头大哥不能死,中间兄弟不能断;
索引列上少计算,范围之后全失效;
Like百分写最右,覆盖索引不写星;