欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

几种常见的 sql 优化方法

最编程 2024-06-14 11:17:00
...

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、关于索引本身

索引并不是越多越好,要根据查询有针对性的创建。
索引字段越小越好,因为数据库的存储单位是页,一页中能存下的数据越多越好。
使用多列索引时主意顺序和查询条件保持一致,同时删除不必要的单列索引。