Mysql索引优化分析(4)

359 阅读3分钟

这是我参与8月更文挑战的第4天,活动详情查看:8月更文挑战

索引失效问题

情况一:最佳左前缀

SQL :建表并添加一个复合索引,索引有三个字段name、age、pos

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());

ALTER TABLE staffs ADD INDEX index_staffs_nameAgePos(`name`,`age`,`pos`)

使用索引中的所有字段进行查询

EXPLAIN SELECT
	*
FROM
	staffs
WHERE
	`name` = "July"
AND age = 23
AND pos = "dev"

结果:

image-20200922093727180

这种情况看着一切正常,索引都使用上了。

但是如果查询条件换一下

EXPLAIN SELECT
	*
FROM
	staffs
WHERE
	age = 23
AND pos = "dev"

结果为:

image-20200922094209403

可以发现实际建立的索引没有使用到,违背了最佳左前缀法则,也就是创建的索引必须从左到右的按顺序使用到,索引才会生效。

在使用到索引的第一个和第三个字段的情况

EXPLAIN SELECT
	*
FROM
	staffs
WHERE
	name = "July"
AND pos = "dev"

结果:

image-20200922102409918

虽然使用到索引了,但是只用到了第一个索引,第三个明明使用了固定值也没有用到索引,还是因为违背了最佳左前缀的原则。

情况二:索引列使用方法函数等操作

使用EXPLAIN SELECT * FROM staffs WHERE name = "july";查询结果为

image-20200922103437558

使用EXPLAIN SELECT * FROM staffs WHERE left(name,4) = "july";查询结果为

image-20200922104125283

在索引列上使用了函数就会导致索引的失效。

情况三:存储引擎不能使用索引范围条件最右边的列

使用EXPLAIN SELECT * FROM staffs WHERE name = "july" AND age > 20 AND pos = "manager";查询结果为: image-20200922111640249

该查询从age开始到后面的索引都没有用上。索引中范围条件右边的会失效。

情况四:尽量覆盖索引

使用EXPLAIN SELECT name,age,pos FROM staffs WHERE name = "july" AND age > 20 AND pos = "manager";查询结果为

image-20200922142634501

情况五:like 以通配符开头的索引会失效

两边都有 '%' 的情况 EXPLAIN SELECT * FROM staffs WHERE name LIKE "%july%" 的结果

image-20200922144950621

只有左边有 '%' 的情况 EXPLAIN SELECT * FROM staffs WHERE name LIKE "%july" 结果

image-20200922144927036

只有右边有 '%' 的情况 EXPLAIN SELECT * FROM staffs WHERE name LIKE "july%" 结果

image-20200922144852878

只有把 '%' 加在右边索引才没有失效,但是某些场景下必须使用 '%' 。

解决方法:

建表和索引

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,'a@163.com');
INSERT INTO tbl_user(`name`,`age`,`email`)VALUES('2bb2',23,'b@163.com');
INSERT INTO tbl_user(`name`,`age`,`email`)VALUES('3cc3',24,'c@163.com');
INSERT INTO tbl_user(`name`,`age`,`email`)VALUES('4dd4',26,'d@163.com');

CREATE INDEX idx_user_nameAge ON tbl_user(`name`,age);

使用覆盖上索引避免全表扫描 EXPLAIN SELECT name ,age FROM tbl_user WHERE name LIKE "%aa%" 结果

image-20200922151239375

总结
  • 全值匹配最好
  • 最佳左前缀
  • 不在索引列上做任何操作,否则会导致索引失效而导致全表扫描
  • 存储引擎不能使用索引范围条件最右边的列
  • 尽量使用覆盖索引,减少 select *
  • MySQL 在使用不等于 (!= 或 <>) 的时候无法使用索引,会导致全表扫描
  • is、null、is not null 也无法使用索引
  • 使用 like 以通配符开头的索引也会失效
  • 字符串不加单引号索引失效
  • 尽量少用 or,or 连接也会导致索引失效