15个SQL优化的技巧

1.不要使用select *,要使用 select 指定列

  • 直接使用select * 会导致将数据表中所有的列都查询出来,多查询出来的数据会浪费CPU、内存等资源,多查出来的数据通过网络进行传输,也会增加网络IO的传输时间。
  • select * 不会走覆盖索引(除非一个表中所有的字段都建立了索引,当然这种情况一般不可能) ,如果select *走了非主键索引的情况下,那就只能通过二级索引查找到主键上的值,再根据查找到的主键值,去回表查询数据,也就是会将会发生大量的回表操作。
  • 而且select * 比select 指定列在解析时,要多解析出查询的是哪些列这一步骤
  • 重构数据库时(对数据库的结构进行改动),可能与应用程序中的字段产生不一致。例如,mybatis中的resultMap结果集映射或者是与实体类去映射。解决方法:可以利用视图来解决。
  • 可能对数据的安全有影响。假如我们有一个类包含账户、密码等,使用select *可能导致用户的信息泄露。或者是后期添加了一些私密的信息。解决方法:可以利用视图来解决。
    如何优化呢?那就是查询时,只查使用的列,不使用的列,不需要去查询,如:
select name, age from person where age = 18

2.使用union all,不要使用union

因为使用union操作,会将数据结果进行去重,在去重时,会将所有的数据进行排序、遍历和比较,如果数据本身就没有重复的话,那么这个步骤就是多余的步骤,或者说如果业务场景下,不在乎数据是否重复,那么就尽可能的使用union all操作
如:

(select name from person where age = 19)
union all
(select name from person where age = 20)

3. 小表驱动大表

用小表数据集去驱动大表数据集
口诀:

  • in适用于左边大表,右边小表。因为执行该类查询时,会先执行in里面的子查询,再执行外面的查询,所以应该让in里面的子查询是一个小表。
  • exists适用于左边小表,右边大表。因为执行该类查询时,会先执行exists左边的查询,用查询的结果去挨个匹配右边的条件,所以exists应该左边的查询是小表。

Demo: Order表中有10000行数据,User表中有100行数据
查询所有有效的用户,下过的订单列表,则应该使用如下sql:

select * from order where user_id in (select user_id from user where status = 1);

而不应该使用下面查询语句:

select * from order where exists (select 1 from user where order.user_id = user.user_id and status = 1)

4.批量操作

如果有一批数据需要插入到数据库中,不要分别的单条插入,而是使用批量插入的方法
如:

insert into person (age, name) values (18, '张三'), (19, '李四'), (20, '王五');

而一次批量操作的数据也不要太多,建议一次批量插入500行数据就可以

5. 多用limit

如果查询一个用户下的第一笔订单的时间,则使用limit 1,而不是查询全部的订单,再在代码中获取第一个
如下:

select * from order where user_id = 1 order by create_time desc limit 1;

6. in中的条件值不要太多

如 select * from person where id in (1, 2, 3, 4, 5…100000)
in后面跟的值太多了,不建议这样做
可以的做法是select * from person where id in (1, 2, 3, 4, 5…100000) limit 500
提示客户端一次性查询的条数,不能超过500条,或者是使用多线程,将这批值使用多线程的方式查询出来,然后进行数据的汇总合并。

7. 增量查询

有时候需要同步数据到别的数据库表中,就会想到使用select * from person 将表中的所有数据查询出来之后,再导入到别的表,这种做法虽然简便, 但是查询性能却会非常差。
正确的做法是:

select * from person
where id > #{lastId}
and create_time > #{lastCreateTime}
limit 100

使用这种增量查询的方式,每次查询100条数据,然后进行插入到数据库表中,每一次的lastId都是上一批查询的数据的最大值

8. 使用limit进行分页

使用分页时,如果能知道上一页的id值,而查询下一页的时候,可以使用

# 使用下面的sql要求该id是连续的,并且有序的
select * from person where id > 100000 limit 20;
# 不要使用下面sql,下面的sql会查询出100020条数据,然后丢弃前100000条数据,会导致资源的浪费
select * from person limit 100000, 20;

9.用连接查询,代替子查询

如果查询sql如下:这样查询,会导致先执行子查询,在执行子查询的时候,会创建临时表,然后在查询完的时候,又会删除临时表,这样会导致性能的浪费

select * from order where user_id in (select user_id from user where status = 1);

可以使用下面的sql进行查询

select * from order o left join user u on o.user_id = u.user_id where u.status = 1;

10.join表的数量不能过多

根据阿里巴巴开发手册规定,join表的数量,最好不要超过三个
数据库在选择索引的时候会非常复杂,会导致选错索引,
反例:

select a.name, b.name, c.name, d.name
from a
inner join b on a.id = b.id
inner join c on c.id = b.id
inner join d on d.id = c.id
inner join e on e.id = d.id
inner join f on f.id = e.id;

但是在一些ERP系统重,查询的数据要join的表的数量非常多,所以不能一概而论,尽量越少越好。

11. join的选择问题

如果两张表使用inner join进行关联,数据库会自动选择较小的表去驱动较大的表。
但如果使用left join进行关联,则数据库会使用左边的表去驱动右边的表,所以如果使用left join的时候,尽量让左边的表是小表。

12.控制索引的数量

虽然索引能提高查询的速度,但是表中新增数据时,维护索引也是需要消耗性能的。
阿里巴巴开发手册中规定,单表的索引数量不要超过5个,单个索引的字段数量不要超过5个,数据库中使用b+树的结构来保存索引。在insert update 和delete时,都需要更新b+树索引。
如果你的表中的索引超过了5个怎么办,那也没关系,你的系统的并发量不多,超过5个也可以。
对于一些高并发的系统,最好遵守这个规范。
那么高并发系统如何优化索引呢,可以对于索引建立联合索引,将多个索引列放一起,可以删除部分无用的单个索引,将部分查询功能迁移到其他类型的数据库中,如Elastic Search中

13. 选择合理的字段类型

能用varchar就不要使用char字段
varchar可变长字段
char不可变长字段
金额字段用:decimal
长度可变的字符串用:varchar
长度不可变的字符串用:char

14.group by 效率提升

不要使用下面的sql:

select * from person
group by name
having age > 30

使用下面的sql:

select * from person
where age > 30
group by name

应先将数据进行缩小范围,再进行数据的批量操作

15.索引的优化

很多时候,sql走没走索引,执行的时间差距会很大,
可以使用explain命令查看是否走索引。
索引失效的常见原因:

  • 不满足最左前缀匹配原则
  • 范围索引列没有放在最后
  • 使用了select *
  • 索引列上有计算
  • 索引列上使用了函数
  • 字符类型没有加上引号,既字符串类型的字段,传参时,传入了数字类型的参数,导致类型不匹配
  • 用is null和is not null没注意字段是否允许为空
  • like查询左边有%
  • 使用or关键字没有注意,使用or关键字时,所有的字段都要加上索引,不然所有的索引都会失效
  • 两个单独建立了索引的列,进行列对比时,索引失效
  • 对于数据库有时会选错索引,可以强制sql使用某个索引,force index

16. MySQL在使用反向查询(!=, <>, NOT LIKE)的时候无法使用索引,会导致全表扫描,覆盖索引除外。

EXPLAIN SELECT * FROM user_innodb WHERE name != '123';

文章学自于b站视频:苏三说技术
部分补充自其它地方

  • 16
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值