MySQL之索引性能分析与优化
简介
MySQL 中索引的本质是一种特殊的数据结构,可以简单理解为排好序的快速查找结构,通过索引可以提高数据检索的效率,降低数据库的 IO 成本,而通过索引列对数据进行排序,可以在降低数据排序成本的同时,降低 CPU 的消耗,故索引在 MySQL 中起着举足轻重的作用,本文将通过 EXPLAIN 关键字模拟优化器执行 SQL 查询语句,从而分析索引的性能来进行优化。
# 建表语句
CREATE TABLE student (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(24) NOT NULL DEFAULT '' COMMENT '姓名',
`age` INT NOT NULL DEFAULT 0 COMMENT '年龄',
`major` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '专业',
`time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '入学时间'
) CHARSET utf8 COMMENT '学生信息表' ;
# 插入数据
INSERT INTO student(`name`, `age`, `major`, `time`) VALUES('Tom', 18, '计算机科学与技术', NOW());
INSERT INTO student(`name`, `age`, `major`, `time`) VALUES('Amy', 20, '软件工程', NOW());
# 创建索引
ALTER TABLE student ADD INDEX idx_student_nameAgeMajor(`name`, `age`, `major`);
创建完成之后首先执行如下的 SQL 语句进行测试:
SELECT * FROM student
执行结果如下图,可以正常查询到表中的数据:
1. 全值匹配的情况
分别执行如下 SQL 语句,分析性能方面的异同:
EXPLAIN SELECT * FROM student WHERE `name` = 'Amy' AND `age` = 20 AND `major` = '软件工程';
EXPLAIN SELECT * FROM student WHERE `age` = 20 AND `major` = '软件工程';
EXPLAIN SELECT * FROM student WHERE `name` = 'Amy' AND `major` = '软件工程';
程序执行结果如图所示:
分析:通过执行结果可以看出,第一个查询物尽其用,三个字段均成功匹配索引;第二个查询索引失效,未能成功匹配到索引,执行了一次全表扫描;而第三个查询只有第一个字段成功匹配到了索引,第二个字段未能成功匹配。所以我们可以得到如下结论:全值匹配性能最优,但是索引多列时需要遵循最佳左前缀法则,即查询从索引的最左前列开始并且不跳过索引中的列,否则会导致索引失效
2. 索引列操作的情况
分别执行如下 SQL 语句,分析性能方面的异同:
SELECT * FROM student WHERE LEFT(`name`, 3) = 'AMY';
EXPLAIN SELECT * FROM student WHERE LEFT(`name`, 3) = 'AMY';
SELECT * FROM student WHERE name = 'AMY';
EXPLAIN SELECT * FROM student WHERE name = 'AMY';
程序执行结果如图所示:
分析:通过执行结果可以看出,两条 SQL 语句的执行结果完全相同,但是第一个查询使用到了库函数,导致索引失效,执行了一次全表扫描,而第二个查询完美用到了索引。所以建议不要在索引列上做任何操作(计算、函数、类型转换),这样会导致索引失效而转向全表扫描,有时遇到需要使用库函数但数据量不是大到严重影响速度时,一般可以先查出来,再在程序中去筛选
3. 范围匹配的情况
分别执行如下 SQL 语句,分析性能方面的异同:
SELECT * FROM student WHERE `name` = 'AMY' AND age > 18 AND `major` = '软件工程';
EXPLAIN SELECT * FROM student WHERE `name` = 'AMY' AND age > 18 AND `major` = '软件工程';
程序执行结果如图所示:
分析:通过执行结果可以看出,与全值匹配的情况相比,虽然查询到的结果相同,但是 SQL 的性能明显有所下降,且范围之后的字段查询时并未使用到索引。所以我们可以得到如下结论:范围之后索引全部失效,存储引擎不能使用索引中范围条件右边的列
4. 覆盖索引的情况
分别执行如下 SQL 语句,分析性能方面的异同:
SELECT * FROM student WHERE `name` = 'AMY' AND age = 20;
EXPLAIN SELECT * FROM student WHERE `name` = 'AMY' AND age = 20;
SELECT `name`, `age` FROM student WHERE `name` = 'AMY' AND age = 20;
EXPLAIN SELECT `name`, `age` FROM student WHERE `name` = 'AMY' AND age = 20;
程序执行结果如图所示:
分析:通过执行结果可以看出,第二条 SQL 相比于第一条,只查询到了所需要的字段,且性能更优。所以我们在查询时尽量使用覆盖索引,即只访问索引的查询,索引列和查询列保持一致,尽可能减少 SELECT * 的查询
5. 模糊查询的情况
分别执行如下 SQL 语句,分析性能方面的异同:
EXPLAIN SELECT * FROM student WHERE `name` LIKE '%my';
EXPLAIN SELECT * FROM student WHERE `name` LIKE 'Am%';
EXPLAIN SELECT `name` FROM student WHERE `name` LIKE '%my';
程序执行结果如图所示:
分析:通过执行结果可以看出,当使用 LIKE 关键字进行模糊查询时,以通配符开头 ‘%’ 会导致索引失效变成全表扫描,即 LIKE 百分加右边,当遇到必须使用开头的情况时,可以通过覆盖索引来解决索引失效的问题,且性能要更优
6. 其他可能导致索引失效的情况
分别执行如下 SQL 语句,分析性能方面的异同:
EXPLAIN SELECT * FROM student WHERE `name` != 'Amy';
EXPLAIN SELECT * FROM student WHERE `name` IS NOT NULL;
EXPLAIN SELECT * FROM student WHERE `name` = 'Amy' OR `name` = 'Tom';
EXPLAIN SELECT * FROM student WHERE `name` = 123;
程序执行结果如图所示:
分析:通过执行结果可以看出,当语句中出现不等号、NULL 值、关键字 OR 时都会导致索引失效,从而进行全表扫描,除此之外还要特别注意,字符串的单引号不能省略,MySQL 底层会自动进行类型转换从而导致索引失效。
总结
本文介绍了常见索引失效的情况与优化方案,可能导致索引失效的情况有许多,数据库的性能对整个项目工程的影响也是不容忽视的,在实际开发场景中应该尽量避免出现这样的情况,谢谢观看!