《MySQL慢查询优化》之SQL语句及索引优化

/ mysql / 没有评论 / 1553浏览

《MySQL慢查询优化》之SQL语句及索引优化

1、慢查询优化方式

服务器硬件升级优化Mysql服务器软件优化数据库表结构优化SQL语句及索引优化本文重点关注于SQL语句及索引优化,关于其他优化方式以及索引原理等,请关注本人《MySQL慢查询优化》系列博文。优化我个人遵循的原则:积小胜为大胜,以空间换时间。-《论持久战》

2、数据源

工欲善其事必先利其器,为了测试与验证的方便,数据库可以直接采用MySQL官方提供的测试数据库employees,该数据库关系复杂度适中以及数据量较大,适合做SQL语句及索引优化分析,引用官方instruction:

The database contains about 300,000 employee records with 2.8 million salary entries. The export data is 167 MB, which is not huge, but heavy enough to be non-trivial for testing.

数据库获取方式数据库E-R关系图1

3、分析工具

采用explain指令直接模拟Mysql优化器执行SQL语句,查看SQL语句的执行计划。

示例: 2

explain命令执行结果包括若干参数:id、select_type、table、type、possible_keys、key、key_len、ref、rows、Extra;重点关注type、possible_keys、key、key_len、extra 这五个参数。

possible_keys:此次查询中可能会被选用的索引,注意这些索引不一定被查询使用到。key:此次查询中真正使用到的索引。当为复合索引时,不确定是否被充分使用。type:访问类型,表示MySQL在表中查找所需行的方式。常用的类型有: ALL, index, range, ref, eq_ref, const, system, NULL(性能从左到右逐步提升),其中: 3

key_len:表示索引中使用的字节数,用来计算索引是否被充分使用,不损失精确性的情况下,长度越短越好 ; 4

extra5

4、索引策略

索引策略是指创建使用索引所要遵循的规则,换句话说,违背了这些规则会导致索引失效或者查询效率降低。 6

测试数据表

show index from employees;

结果: 7

策略1:尽量考虑覆盖索引

覆盖索引:SQL只需要通过遍历索引树就可以返回所需要查询的数据,而不必通过辅助索引查到主键值之后再去查询数据(回表操作)。回表操作的详细介绍可以参考本人《MySQL慢查询优化》系列博文之索引。

EG

EXPLAIN SELECT emp_no,birth_date,gender FROM employees WHERE gender ='M' ;

结果: 8

****Using index:****表示已经使用了覆盖索引。

策略2:遵循最左前缀匹配

联合索引命中必须遵循“最左前缀法则”。即SQL查询Where条件字段必须从索引的最左前列开始匹配,不能跳过索引中的列。联合索引又称复合索引,类似于书籍的目录,多级的目录结构中子目录依赖于父级目录存在,也是遵循“最左前缀法则”。

联合索引结构分析,示例 9

EXPLAIN SELECT * FROM employees WHERE birth_date = '1963-06-01' AND gender ='F';

结果: 10

注:表存在多个索引时,即使Where条件满足最左前缀规则,SQL执行时也未必一定会命中联合索引,根据性能可能直接使用了主键索引。

EG:

EXPLAIN SELECT * FROM employees WHERE emp_no = 10010 AND birth_date = '1963-06-01' AND gender ='F';

结果: 11

PRIMARY KEY (emp_no)

策略3:范围查询字段放最后

联合索引定义时,尽量将范围查询字段放在最后(放在最后联合索引使用最充分,放在中间联合索引使用不充分)。使用联合索引时范围列(当前范围列索引生效)后面的索引列无法生效,同时索引最多用于一个范围列,如果查询条件中有多个范围列,也只能用到一个范围列索引。

EG1:

EXPLAIN SELECT emp_no,birth_date,gender FROM employees WHERE emp_no > 10015 AND gender ='F';

结果: 12

只是使用到了主键索引PRIMARY(emp_no),联合索引未生效idx_empno_birthdate_gender(emp_no,birth_date,gender);删除idx_empno_birthdate_gender索引,新建联合索引idx_gender_birthdate_empno(gender,birth_date,emp_no); 13

EG2:

EXPLAIN SELECT emp_no,birth_date,gender FROM employees WHERE emp_no > 10015 AND birth_date = 1953-09-02 AND gender ='F';

结果: 14

策略4:不对索引字段进行逻辑操作

在索引字段上进行计算、函数、类型转换(自动\手动)都会导致索引失效。

EG:

CREATE INDEX idx_first_name ON employees(first_name);
EXPLAIN SELECT * FROM employees WHERE LEFT(first_name,3) ='Geo';

结果: 15

策略5:尽量全值匹配

全值匹配也就是精确匹配不使用like查询(模糊匹配),使用like会使查询效率降低。

策略6:Like查询,左侧尽量不要加%

like 以%开头,当前列索引无效(当为联合索引时,当前列和后续列索引不生效,可能导致索引使用不充分);当like前缀没有%,后缀有%时,索引有效。

EG1:

EXPLAIN SELECT * FROM employees WHERE first_name like'Geo%';

结果: 16

EG2:

EXPLAIN SELECT * FROM employees WHERE first_name like'%Geo%';

结果: 17

策略7:注意NULL/NOT NULL可能对索引有影响

在索引列上使用 IS NULL 或 IS NOT NULL条件,可能对索引有所影响。字段定义默认为NULL时,NULL索引生效,NOT NULL索引不生效;字段定义明确为NOT NULL ,不允许为空时,NULL/NOT NULL索引列,索引均失效; 18

EG1:

EXPLAIN SELECT * FROM employees WHERE first_name IS NULL;

结果: 19

EG2:

EXPLAIN SELECT * FROM employees WHERE first_name IS NOT NULL;

结果: 20 可以改为:

EXPLAIN SELECT * FROM employees WHERE first_name > '';

EG3: 21

EXPLAIN SELECT * FROM employees WHERE first_name IS NOT NULL;

结果: 22

策略8:尽量减少使用不等于

不等于操作符是不会使用索引的。不等于操作符包括:not,<>,!=。

优化方法:数值型 key<>0 改为 key>0 or key<0。

EG:

EXPLAIN SELECT * FROM employees WHERE first_name != 'Georgi';

结果: 23

策略9:字符类型务必加上引号

若varchar类型字段值不加单引号,可能会发生数据类型隐式转化,自动转换为int型,使索引无效。

EG:

EXPLAIN SELECT * FROM employees WHERE first_name = 1;

结果: 24

策略10:OR关键字前后尽量都为索引列

当OR左右查询字段只有一个是索引,会使该索引失效,只有当OR左右查询字段均为索引列时,这些索引才会生效。OR改UNION效率高。

EG1:

EXPLAIN SELECT * FROM employees WHERE first_name = 'Georgi' OR emp_no = 20001;

结果: 25

EG2:

EXPLAIN SELECT * FROM employees WHERE first_name = 'Georgi' OR last_name = 'Facello';

结果: 26