JAVA代码优化,接口优化,SQL优化 (小技巧)(六)

简介: JAVA代码优化,接口优化,SQL优化 (小技巧)(六)

🟡第三章:SQL 优化



1 避免使用select *

很多时候,我们写sql语句时,为了方便,喜欢直接使用select *,一次性查出表中所有列的数据。


反例:

select * from user where id=1;

在实际业务场景中,可能我们真正需要使用的只有其中一两列。查了很多数据,但是不用,白白浪费了数据库资源,比如:内存或者cpu。


此外,多查出来的数据,通过网络IO传输的过程中,也会增加数据传输的时间。


还有一个最重要的问题是:select *不会走覆盖索引,会出现大量的回表操作,而从导致查询sql的性能很低。


那么,如何优化呢?


正例:

select name,age from user where id=1;

sql语句查询时,只查需要用到的列,多余的列根本无需查出来。


2 用union all代替union

我们都知道sql语句使用union关键字后,可以获取排重后的数据。


而如果使用union all关键字,可以获取所有数据,包含重复的数据。


反例:

(select * from user where id=1) 
union 
(select * from user where id=2);


排重的过程需要遍历、排序和比较,它更耗时,更消耗cpu资源。


所以如果能用union all的时候,尽量不用union。


正例:

(select * from user where id=1) 
union all
(select * from user where id=2);


除非是有些特殊的场景,比如union all之后,结果集中出现了重复数据,而业务场景中是不允许产生重复数据的,这时可以使用union。


3 小表驱动大表

小表驱动大表,也就是说用小表的数据集驱动大表的数据集。


假如有order和user两张表,其中order表有10000条数据,而user表有100条数据。


这时如果想查一下,所有有效的用户下过的订单列表。


可以使用in关键字实现:

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


也可以使用exists关键字实现:

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


前面提到的这种业务场景,使用in关键字去实现业务需求,更加合适。


为什么呢?


因为如果sql语句中包含了in关键字,则它会优先执行in里面的子查询语句,然后再执行in外面的语句。如果in里面的数据量很少,作为条件查询速度更快。


而如果sql语句中包含了exists关键字,它优先执行exists左边的语句(即主查询语句)。然后把它作为条件,去跟右边的语句匹配。如果匹配上,则可以查询出数据。如果匹配不上,数据就被过滤掉了。


这个需求中,order表有10000条数据,而user表有100条数据。order表是大表,user表是小表。如果order表在左边,则用in关键字性能更好。


总结一下:


in 适用于左边大表,右边小表。

exists 适用于左边小表,右边大表。

不管是用in,还是exists关键字,其核心思想都是用小表驱动大表。


4 批量操作

如果你有一批数据经过业务处理之后,需要插入数据,该怎么办?


反例:

for(Order order: list){
   orderMapper.insert(order):
}


在循环中逐条插入数据。

insert into order(id,code,user_id) 
values(123,'001',100);


该操作需要多次请求数据库,才能完成这批数据的插入。


但众所周知,我们在代码中,每次远程请求数据库,是会消耗一定性能的。而如果我们的代码需要请求多次数据库,才能完成本次业务功能,势必会消耗更多的性能。


那么如何优化呢?


正例:

orderMapper.insertBatch(list):

提供一个批量插入数据的方法。

insert into order(id,code,user_id) 
values(123,'001',100),(124,'002',100),(125,'003',101);


这样只需要远程请求一次数据库,sql性能会得到提升,数据量越多,提升越大。


但需要注意的是,不建议一次批量操作太多的数据,如果数据太多数据库响应也会很慢。批量操作需要把握一个度,建议每批数据尽量控制在500以内。如果数据多于500,则分多批次处理。


5 多用limit

有时候,我们需要查询某些数据中的第一条,比如:查询某个用户下的第一个订单,想看看他第一次的首单时间。


反例:


select id, create_date 
 from order 
where user_id=123 
order by create_date asc;


根据用户id查询订单,按下单时间排序,先查出该用户所有的订单数据,得到一个订单集合。 然后在代码中,获取第一个元素的数据,即首单的数据,就能获取首单时间。

List list = orderMapper.getOrderList();
Order order = list.get(0);


虽说这种做法在功能上没有问题,但它的效率非常不高,需要先查询出所有的数据,有点浪费资源。


那么,如何优化呢?


正例:

select id, create_date 
 from order 
where user_id=123 
order by create_date asc 
limit 1;


使用limit 1,只返回该用户下单时间最小的那一条数据即可。


此外,在删除或者修改数据时,为了防止误操作,导致删除或修改了不相干的数据,也可以在sql语句最后加上limit。


例如:

update order set status=0,edit_time=now(3) 
where id>=100 and id<200 limit 100;


这样即使误操作,比如把id搞错了,也不会对太多的数据造成影响。


6 in中值太多

对于批量查询接口,我们通常会使用in关键字过滤出数据。比如:想通过指定的一些id,批量查询出用户信息。


sql语句如下:

select id,name from category
where id in (1,2,3...100000000);


如果我们不做任何限制,该查询语句一次性可能会查询出非常多的数据,很容易导致接口超时。


这时该怎么办呢?

select id,name from category
where id in (1,2,3...100)
limit 500;


可以在sql中对数据用limit做限制。


不过我们更多的是要在业务代码中加限制,伪代码如下:

public List getCategory(List ids) {
   if(CollectionUtils.isEmpty(ids)) {
      return null;
   }
   if(ids.size() > 500) {
      throw new BusinessException("一次最多允许查询500条记录")
   }
   return mapper.getCategoryList(ids);
}


还有一个方案就是:如果ids超过500条记录,可以分批用多线程去查询数据。每批只查500条记录,最后把查询到的数据汇总到一起返回。


不过这只是一个临时方案,不适合于ids实在太多的场景。因为ids太多,即使能快速查出数据,但如果返回的数据量太大了,网络传输也是非常消耗性能的,接口性能始终好不到哪里去。

相关文章
|
3天前
|
SQL 存储 缓存
如何优化SQL查询性能?
【10月更文挑战第28天】如何优化SQL查询性能?
21 10
|
1天前
|
SQL 存储 缓存
SQL Server 数据太多如何优化
11种优化方案供你参考,优化 SQL Server 数据库性能得从多个方面着手,包括硬件配置、数据库结构、查询优化、索引管理、分区分表、并行处理等。通过合理的索引、查询优化、数据分区等技术,可以在数据量增大时保持较好的性能。同时,定期进行数据库维护和清理,保证数据库高效运行。
|
7天前
|
安全 Java
在 Java 中使用实现 Runnable 接口的方式创建线程
【10月更文挑战第22天】通过以上内容的介绍,相信你已经对在 Java 中如何使用实现 Runnable 接口的方式创建线程有了更深入的了解。在实际应用中,需要根据具体的需求和场景,合理选择线程创建方式,并注意线程安全、同步、通信等相关问题,以确保程序的正确性和稳定性。
|
6天前
|
Java 数据库连接 数据库
优化之路:Java连接池技术助力数据库性能飞跃
在Java应用开发中,数据库操作常成为性能瓶颈。频繁的数据库连接建立和断开增加了系统开销,导致性能下降。本文通过问题解答形式,深入探讨Java连接池技术如何通过复用数据库连接,显著减少连接开销,提升系统性能。文章详细介绍了连接池的优势、选择标准、使用方法及优化策略,帮助开发者实现数据库性能的飞跃。
16 4
|
4天前
|
存储 Java 开发者
成功优化!Java 基础 Docker 镜像从 674MB 缩减到 58MB 的经验分享
本文分享了如何通过 jlink 和 jdeps 工具将 Java 基础 Docker 镜像从 674MB 优化至 58MB 的经验。首先介绍了选择合适的基础镜像的重要性,然后详细讲解了使用 jlink 构建自定义 JRE 镜像的方法,并通过 jdeps 自动化模块依赖分析,最终实现了镜像的大幅缩减。此外,文章还提供了实用的 .dockerignore 文件技巧和选择安全、兼容的基础镜像的建议,帮助开发者提升镜像优化的效果。
|
5天前
|
Java
Java基础(13)抽象类、接口
本文介绍了Java面向对象编程中的抽象类和接口两个核心概念。抽象类不能被实例化,通常用于定义子类的通用方法和属性;接口则是完全抽象的类,允许声明一组方法但不实现它们。文章通过代码示例详细解析了抽象类和接口的定义及实现,并讨论了它们的区别和使用场景。
|
5天前
|
Java 测试技术 API
Java零基础-接口详解
【10月更文挑战第19天】Java零基础教学篇,手把手实践教学!
15 1
|
9天前
|
缓存 前端开发 JavaScript
9大高性能优化经验总结,Java高级岗必备技能,强烈建议收藏
关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。本文介绍了9种性能优化方法,涵盖代码优化、数据库优化、连接池调优、架构层面优化、分布式缓存、异步化、Web前端优化、服务化、硬件升级、搜索引擎和产品逻辑优化。欢迎留言交流。
|
8天前
|
存储 缓存 Java
Java应用瘦身记:Docker镜像从674MB优化至58MB的实践指南
【10月更文挑战第22天】 在容器化时代,Docker镜像的大小直接影响到应用的部署速度和运行效率。一个轻量级的Docker镜像可以减少存储成本、加快启动时间,并提高资源利用率。本文将分享如何将一个Java基础Docker镜像从674MB缩减到58MB的实践经验。
21 1
|
9天前
|
消息中间件 监控 算法
Java性能优化:策略与实践
【10月更文挑战第21】Java性能优化:策略与实践