MySQL SQL语句分析与优化(下)
这是我参与8月更文挑战的第6天,活动详情查看:8月更文挑战
前言:上篇讲解了关于MySQL的explain分析结果的一些关键字,如有未了解的可以去了解MySQL SQL语句分析与优化(下),这篇主要为大家讲解下关于SQL优化的一些小技巧.
前置准备
CREATE TABLE `employees` (
`id` INT ( 11 ) NOT NULL AUTO_INCREMENT,
`name` VARCHAR ( 24 ) NOT NULL DEFAULT '' COMMENT '姓名',
`age` INT ( 11 ) NOT NULL DEFAULT '0' COMMENT '年龄',
`position` VARCHAR ( 20 ) NOT NULL DEFAULT '' COMMENT '职位',
`hire_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '入职时间',
PRIMARY KEY ( `id` ),
KEY `idx_name_age_position` ( `name`, `age`, `position` ) USING BTREE
) ENGINE = INNODB AUTO_INCREMENT = 4 DEFAULT CHARSET = utf8 COMMENT = '员工表';
# 插入数据
INSERT INTO employees ( NAME, age, position, hire_time ) VALUES('LiLei',22,'manager',NOW());
INSERT INTO employees ( NAME, age, position, hire_time ) VALUES ('HanMeimei',23,'dev',NOW());
INSERT INTO employees ( NAME, age, position, hire_time ) VALUES('Lucy',23,'dev',NOW());
上方SQL语句中,我们创建了一张员工表,并且建立了一个联合索引,字段为name,age,position,索引名称为idx_name_age_position
最左前缀原则
当我们查询使用到联合索引的时候,需要遵循最左前缀原则,意思是查询从索引的最左列开始,并且不跳过索引中的列 列子:
EXPLAIN SELECT * FROM employees WHERE name = 'LiLei' and age = 22;
大家可以看到,显示结果告诉我们是走了索引的,两个const代表两个常量,正好对应了name与age,那么我们如果跳过name会怎么样
EXPLAIN SELECT * FROM employees WHERE age = 22 AND position = 'manager';
可以看到它是没有走索引的,type是ALL表示全表扫描了,因为我们跳过了name字段,因此无法走索引,那么我们不跳过name,跳过age的话会怎么样呢?
EXPLAIN SELECT * FROM employees WHERE name = 'LiLei' AND position = 'manager';
是不是走了索引,但是又没完全走索引?没错!因为我们跳过了age字段,但是没跳过name,因此name是可以走索引的,但是position由于age字段被跳过,因此无法走索引,注意如果age是范围查询,那么也会导致position无法走索引!
全值匹配
全值匹配的前提是得遵守最左前缀原则,其实就是遵循最左前缀原则,指查询的按索引创建的顺序,如下面这些SQL
EXPLAIN SELECT * FROM employees WHERE name= 'LiLei';
EXPLAIN SELECT * FROM employees WHERE name= 'LiLei' and age = 22;
EXPLAIN SELECT * FROM employees WHERE name= 'LiLei' and and age = 22 position = manager;
以上的都会走索引,具体结果就不演示了!
尽量使用覆盖索引
尽量使用覆盖索引的意思是,访问索引的查询包含索引列,尽量不用select * 语句
EXPLAIN SELECT name,age FROM employees WHERE name= 'LiLei' AND age = 23 AND position='manager';
尽量不在索引列上做任何操作
计算、函数、(自动or手动)类型转换),会导致索引失效而转向全表扫描
EXPLAIN SELECT * FROM employees WHERE left(name,3) = 'LiLei';
少使用不等于
mysql在使用not in ,not exists 的时候大部分无法使用索引会导致全表扫描,
但是在使用!=或者<>,< 小于、 > 大于、 <=、>= 这些,mysql内部优化器会根据检索比例、表大小等多个因素整体评估是否使用索引,主要跟数据量有关,很有可能只是因为不等于的数据占比很高,走索引不如全表扫描。
EXPLAIN SELECT * FROM employees WHERE name != 'LiLei';
同样一条SQL,只是因为数据量不同,导致的情况不同,走索引的时候,表只有几条数据,而没走索引的时候表中的数据虽然只有几十条,但也相对几条来说多了十倍左右,因此有的范围是否走索引跟数据量有关
is null,is not null 一般情况下也无法使用索引
EXPLAIN SELECT * FROM employees WHERE name is null
like以通配符%开头 mysql索引失效会变成全表扫描操作
EXPLAIN SELECT * FROM employees WHERE name like '%LiLei';
EXPLAIN SELECT * FROM employees WHERE name like '%LiLei%';
上方两个SQL,无论是哪个都无法走索引,但是我们可以用索引覆盖的方法,让它走索引
使用覆盖索引,查询字段必须是建立覆盖索引字段
EXPLAIN SELECT name,age,position FROM employees WHERE name like '%Lei%';
** 范围查询优化
# 首先添加年龄的单个索引
ALTER TABLE `employees` ADD INDEX `idx_age` (`age`) USING BTREE ;
# 分析语句
explain select * from employees where age >=1 and age <=2000;
没走索引原因:mysql内部优化器会根据检索比例、表大小等多个因素整体评估是否使用索引。比如这个例子,可能是由于单次数据量查询过大导致优化器最终选择不走索引
优化方法:可以将大的范围拆分成多个小范围
explain select * from employees where age >=1 and age <=1000;
explain select * from employees where age >=1001 and age <=2000;
总结
还有很多SQL优化的小结,不一一演示了,再下方给大家总结起来:
- 字符串不加单引号索引失效:如 name = '1000'; name = 1000; 前者会走索引,后者不会
- 少用or或in,用它查询时,mysql不一定使用索引,mysql内部优化器会根据检索比例、表大小等多个因素整体评估是否使用索引,in需要理解小表驱动大表的原则,不懂的可以上网查找
优化总结口诀:
全值匹配我最爱,最左前缀要遵守;
带头大哥不能死,中间兄弟不能断;
索引列上少计算,范围之后全失效;
Like百分写最右,覆盖索引不写星;
不等空值还有or,索引失效要少用;
VAR引号不可丢,SQL高级也不难!