1、explain 输出执行计划
在select语句前加上explain就可以了(MySQL 5.6开始,不仅仅支持select )能够简单分析sql的执行情况,是否走索引等。
-
type列,连接类型。一个好的SQL语句至少要达到range级别。从最好到最差的连接类型为const、eq_reg、- ref、range、index和ALL。一般来说,得保证查询至少达到range级别(范围扫描),最好能达到ref(索引访问)。
-
key列,使用到的索引名。如果没有选择索引,值是NULL。可以采取强制索引方式。
-
key_len列,索引长度。
-
rows列,扫描行数。该值是个预估值。
-
extra列,详细说明。注意,常见的不太友好的值,如下:Using filesort,Using temporary。
更多相关关内容联合索引、索引失效、索引下推优化、回表、覆盖盖索引等问题可参看:MySQL慢查询是怎么回事、MySQL索引
2、in 和 not in 要慎用
SQL语句中IN包含的值不应过多,MySQL对于IN做了相应的优化,即将IN中的常量全部存储在一个数组里面,而且这个数组是排好序的。但是如果数值较多,产生的消耗也是比较大的。再例如:
select id from t where num in(1,2,3)
对于连续的数值,能用 between 就不要用 in 了:
select id from t where num between 1 and 3
再或者使用连接来替换。
当IN的取值范围较大时会导致索引失效,走全表扫描。By the way:如果使用了 not in,则不走索引
3、少用select *
SELECT语句务必指明字段名称,select * 增加很多不必要的消耗(CPU、IO、内存、网络带宽);
4、善用limit 1
这是为了使explain中type列达到const类型。当只需要一条数据的时候,使用limit 1,如果加上limit1,查找到就不用继续往后找了。
5、 order by字段建索引
避免全表扫描,首先应考虑在 where 及 order by涉及的列上建立索引,如果排序字段没有用到索引,就尽量少排序
可以在程序中排序。
6、count(*)推荐使用
count()、count(主键 id) 和 count(1) 都表示返回满足条件的结果集的总行数;而 count(字段),则表示返回满足条件的数据行里面,参数“字段”不为 NULL 的总个数。
count() 是例外,并不会把全部字段取出来,而是专门做了优化,不取值。count(*) 肯定不是 null,按行累加。
按照效率排序的话,count(字段)<count(主键 id)<count(1)≈count(),推荐使用count()
7、where 子句中避免is null /is not null
应尽量避免在 where 子句中对字段进行 null 值判断,使用is null 或者is not null 理论上都会走索引,存在Null值会导致mysql优化器处理起来比较复杂,容易导致引擎放弃使用索引而进行全表扫描。
select id from t where num is null
所以设计表字段时尽量避免null值出现,null值很难查询优化且占用额外的索引空间,推荐默认数字0代替null。
8、应尽量避免在 where!=或<>
子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。
9、应尽量避免在 where 子句中使用 or
如果使用or那么要求or两边的条件字段都要有索引,才会走索引,如果其中一边有一个字段没索引, 另一个字段上的索引也会失效。很多时候使用union all或者是union(必要的时候)的方式来代替“or”会得到更好的效果。
应尽量避免在 where 子句中使用 or来连接条件,否则将导致引擎放弃使用索引而进行全表扫描。
select id from t where num=10 or num=20
可以这样查
select id from t where num=10
union all
select id from t where num=20
10、尽量用union all代替union
union和union all的差异主要是前者需要将结果集合并后再进行唯一性过滤操作,这就会涉及到排序,增加大量的CPU运算,加大资源消耗及延迟。当然,union all的前提条件是两个结果集没有重复数据。
11、应尽量避免在where子句中对字段进行函数操作
select id from t where substring(name,1,3)=‘abc’
不要在子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。
12、可以选择 exists 代替 in
select num from a where num in(select num from b)
用下面的语句替换:
select num from a where exists(select 1 from b where num=a.num)
13、避免%xxx式查询
在非覆盖索引场景下,大家知道MySQL索引有最左原则,所以通过 like '%xx%'查询的时候一定会造成索引失效(5.7版本覆盖索引可以走索引),一般采用like 'xx%'右边匹配的方式来索引。
当想要获取的字段多了以后,select * from t_user where name like “%xx”; 要查询的数据就不能只在索引树里找了,得需要回表操作才能完成查询的工作,再加上是左模糊匹配,无法利用索引树的有序性来快速定位数据,所以得在索引树逐一遍历,获取主键值后,再到聚簇索引树检索到对应的数据行,这样实在太累了。
优化器认为上面这样的查询过程的成本实在太高了,所以直接选择全表扫描的方式来查询数据。
所以,使用左模糊匹配(like ‘%xx’)并不一定会走全表扫描,但也容易失效,关键还是看数据表中的字段。
14、选择重复值较低的字段建索引
在创建索引时,一定要选择重复值较低的字段。离散型非常的差,优化器可能直接就选择不走索引了,因为优化器可能认为,走索引和全表扫描差不多。值分布很稀少的字段不适合建索引,例如"性别"这种只有两三个值的字段不适合做索引。
15、高效的分页
select id,name,age from user limit 10000, 20;
mysql会查询10020条,然后丢弃前面10000条,这个比较浪费资源
可以优化:
select id,name,age from user id>10000 limit 20;
找到上次分页最大id
16、join使用问题
用连接查询代替子查询、join表不易超过3个、小表驱动大表、链接字段建索引。
17、关于索引本身
使用多列索引时主意顺序和查询条件保持一致,同时删除不必要的单列索引。
#sql优化#