1.前言

一个优秀开发的必备技能:性能优化,包括:JVM调优、缓存、Sql性能优化等。本文主要讲基于Mysql的索引优化。

首先我们需要了解执行一条查询SQL时Mysql的处理过程:

如何做到SQL性能优化?_sql

其次我们需要知道,我们写的SQL在Mysql的执行顺序是怎么样的?sql的执行顺序对sql的性能优化很有帮助,很重要。在建立复合索引的时候需要考虑到这点。

如何做到SQL性能优化?_字段_02

2.介绍

在应用系统开发初期,由于开发数据库数据比较少,对于查询SQL语句,复杂视图的的编写等体会不出SQL语句各种写法的性能优劣,但是随着互联网大数据的兴起,随着数据库中数据的增加,系统的响应速度就成为目前系统需要解决的最主要的问题之一。

系统优化中一个很重要的方面就是SQL语句的优化。对于海量数据,劣质SQL语句和优质SQL语句之间的速度差别可以达到上百倍,可见对于一个系统不是简单地能实现其功能就可,而是要写出高质量的SQL语句,提高系统的可用性。

3.常见优化规则

3.1数据类型

数据类型的选择原则:更简单或者占用空间更小。

如果长度能够满足,整型尽量使用tinyint、smallint、medium_int而非int。

如果字符串长度确定,采用char类型。

如果varchar能够满足,不采用text类型。

精度要求较高的使用decimal类型,也可以使用BIGINT,比如精确两位小数就乘以100后保存。

尽量采用timestamp而非datetime。

如何做到SQL性能优化?_sql_03

相比datetime,timestamp占用更少的空间,以UTC的格式储存自动转换时区。

3.2避免空值

MySQL中字段为NULL时依然占用空间,会使索引、索引统计更加复杂。从NULL值更新到非NULL无法做到原地更新,容易发生索引分裂影响性能。尽可能将NULL值用有意义的值代替,也能避免SQL语句里面包含is not null的判断。微信搜索web_resource 关注获取更多推送。微信搜索web_resource 关注获取更多推送。

3.3text类型优化

由于text字段储存大量数据,表容量会很早涨上去,影响其他字段的查询性能。建议抽取出来放在子表里,用业务主键关联。

3.4表连接数

连接的表越多,性能越差

可能的话,将连接拆分成若干个过程逐一执行

优先执行可显著减少数据量的连接,既降低了复杂度,也能够容易按照预期执行

如果不可避免多表连接,很可能是设计缺陷

外链接效果差,因为必须对左右表进行表扫描

尽量使用inner join查询

4.SQL语句优化

4.1 NULL列

Null列使用索引没有意义,任何包含null值的列都不会被包含在索引中。因此where语句中的is null或is not null的语句优化器是不允许使用索引的。

4.2 concat或||

concat或||是mysql和oracle的字符串连接操作,如果对列进行该函数操作,那么也开会忽略索引的使用。比较下面的查询语句:

-- 忽律索引
select ... from .. where first_name || '' || last_name = 'bill gates' ;
-- 使用索引
select ... from .. where first_name = 'bill' and last_name = 'bill gates' ;

4.3 like

通配符出现在首位,无法使用索引,反之可以。

-- 无法使用索引
select .. from .. where name like '%t%' ;
-- 可以使用索引
select .. from .. where name like 't%' ;

4.4 order by

order by子句中不要使用非索引列或嵌套表达式,这样都会导致性能降低。

4.5 Not运算

not运算无法使用索引,可以改成其他能够使用索引的操作。如下:

-- 索引无效
select .. from .. where sal != 3000 ;
-- 索引生效
select .. from .. where sal < 3000 or sal > 3000;

4.6 where与having

select … from … on … where … group by … having … order by … limit …,以上是sql语句的语法结构,其中on、where和having是有过滤行为的,过滤行为越能提前完成就越可以减少传递给下一个阶段的数据量,因此如果在having中的过滤行为能够在where中完成,则应该优先考虑where来实现。

4.7 exists替代in

not in是最低效的,因为要对子查询的表进行全表扫描。可以考虑使用外链接或not exists。如下:

-- 正确
SELECT *
FROM EMP
WHERE
EMPNO > 0
AND EXISTS (SELECT ‘X' FROM DEPT WHERE DEPT.DEPTNO = EMP.DEPTNO AND LOC = ‘MELB')

-- 错误
SELECT *
FROM EMP
WHERE EMPNO > 0 AND DEPTNO IN(SELECT DEPTNO FROM DEPT WHERE LOC = ‘MELB')

4.8 is null & is not null

如果列可空,避免使用索引。对于多个列使用的索引,起码保证至少有个列不为空。对于多列索引,只有访问了第一个列才会启用索引,如果访问后面的列则使用的是全表扫描。

-- 低效: (索引失效) 
SELECT .. FROM DEPARTMENT WHERE DEPT_CODE IS NOT NULL;
-- 高效: (索引有效)
SELECT .. FROM DEPARTMENT WHERE DEPT_CODE >=0;

5.索引优化

5.1索引分类

普通索引:最基本的索引。

组合索引:多个字段上建立的索引,能够加速复合查询条件的检索。

唯一索引:与普通索引类似,但索引列的值必须唯一,允许有空值。

组合唯一索引:列值的组合必须唯一。

主键索引:特殊的唯一索引,用于唯一标识数据表中的某一条记录,不允许有空值,一般用primary key约束。

全文索引:用于海量文本的查询,MySQL5.6之后的InnoDB和MyISAM均支持全文索引。由于查询精度以及扩展性不佳,更多的企业选择Elasticsearch。

5.2索引优化

分页查询很重要,如果查询数据量超过30%,MYSQL不会使用索引。

单表索引数不超过5个、单个索引字段数不超过5个。

字符串可使用前缀索引,前缀长度控制在5-8个字符。

字段唯一性太低,增加索引没有意义,如:是否删除、性别。

合理使用覆盖索引,如下所示:

select login_name, nick_name from member where login_name = ?
login_name, nick_name两个字段建立组合索引,比login_name简单索引要更快。