java后端接口API性能优化技巧

news2024/12/24 2:51:54

微信公众号访问链接:java后端接口API性能优化技巧

推荐文章:

    1、springBoot对接kafka,批量、并发、异步获取消息,并动态、批量插入库表;

    2、SpringBoot用线程池ThreadPoolTaskExecutor异步处理百万级数据;

    3、SpringBoot用线程池ThreadPoolExecutor处理百万级数据;

    4、SpringBoot+MyBatis流式查询,处理大规模数据,提高系统的性能和响应能力


 

一、概述

      要想成为一名优秀的java后端程序员,编写出高性能的后端服务接口是一个重要指标。针对公司项目,做了些降本增效的事情,其中发现接口耗时过长的问题,就集中搞了一次接口性能优化,顺便整理了这份文章,希望给大家在日后的项目工作中提供一丝帮助。

二、接口性能优化方案总结

2.1、async异步执行

      思想:针对一些耗时较长且不影响主要业务的逻辑,可以采用异步执行,能降低接口耗时,来提升性能。具体可参考:SpringBoot使用@Async实现多线程异步。

常见的异步实现:线程池、消息队列MQ、Spring注解@Async、异步框架CompletableFuture、Spring ApplicationEvent事件。

2.2、批量入库

      思想:批量数据插入数据库时,可以在批处理执行完成后一次性插入或更新数据库,避免多次 IO。具体使用可参考文章:springBoot对接kafka,批量、并发、异步获取消息,并动态、批量插入库表。

//for循环单笔入库list.stream().forEatch(msg->{    insert();});//使用批量入库代替上面的for循环入库batchInsert();

2.3、池化技术

       思想:池化技术最常见的是线程池、数据库连接池,解决的问题就是避免重复创建对象或创建连接,可重复利用,避免不必要损耗,毕竟创建回收也会占用时间。

      例如:线程池使用:若你每次需要用到线程,都去创建,就会有增加一定的耗时,线程池可以重复利用线程,避免不必要的耗时,让任务并行处理。

      具体案例参考:SpringBoot用线程池ThreadPoolTaskExecutor异步处理百万级数据。

2.4、索引

    思想:数据库加索引能大大提高数据查询效率,但索引是否生效?索引设计是否合理?这些问题也需要我们同时去考虑。注意:在数据量很大的表中创建索引,最好选择在业务不繁忙时间段,避免影响线上业务正常使用。

2.4.1、索引不生效场景

有时候虽然添加了索引,但是索引可能会失效,失效场景如下:

2.4.2、索引设计是否合理?

      索引不是设计越多越好,设计必须要合理

           1、优先考虑设计联合索引,适当使用覆盖索引;

           2、索引个数尽量不要超过5个;

           3、索引最好选择数据区分度较高的字段(最好是唯一字段),如:血型太多重复字段就不适合创建索引。

2.5、慢SQL优化

    还可以进一步优化慢SQL语句,如:

2.6、使用缓存

      思想:在适当的业务场景,恰当地使用缓存,是可以大大提高接口性能的。缓存其实就是一种空间换时间的思想,就是你把要查的数据,提前放好到缓存里面,需要时,直接查缓存,而避免去查数据库或者计算的过程。常见的缓存包括:Redis缓存(推荐),JVM本地缓存,memcached,或者Map等。注意:缓存数据的一致性。常把一些变化频率不高的或不会发生变化的数据放入缓存中。具体使用可参考文章:springBoot对接kafka,批量、并发、异步获取消息,并动态、批量插入库表。

2.7、串行改并行

        思想串行:当前执行逻辑必须等上一个执行逻辑结束之后才执行,并行:两个执行逻辑互不干扰。所以并行相对来说就比较节省时间,当然是建立在没有结果参数依赖的前提下

例如:串行:

并行:

具体代码如下:可以使用CompletableFuture 并行调用提高性能,类似也可以使用多线程异步处理。

// 查询获奖经历LambdaQueryWrapper<RewardExp> rewardExpQuery1 = new LambdaQueryWrapper<RewardExp>()                .eq(RewardExp::getResumeId, resume.getId())                .eq(RewardExp::getDelFlag, NORMAL)                .orderByDesc(RewardExp::getDate);CompletableFuture<List<RewardExp>> rewardExpFuture1 = CompletableFuture.supplyAsync(() ->                rewardExpMapper.selectList(rewardExpQuery1)        ); // 查询资格证书LambdaQueryWrapper<Credential> credentialQuery1 = new LambdaQueryWrapper<Credential>()                .eq(Credential::getResumeId, resume.getId())                .eq(Credential::getDelFlag, NORMAL)                .orderByDesc(Credential::getDate);CompletableFuture<List<Credential>> credentialFuture1 = CompletableFuture.supplyAsync(() ->                credentialMapper.selectList(credentialQuery1)        ); // 查询岗位信息LambdaQueryWrapper<ResumeJobs> jobsQuery1 = new LambdaQueryWrapper<ResumeJobs>()                .eq(ResumeJobs::getResumeId, resume.getId()).eq(ResumeJobs::getDelFlag, NORMAL);        CompletableFuture<List<ResumeJobs>> jobsFuture1 = CompletableFuture.supplyAsync(() ->                resumeJobsMapper.selectList(jobsQuery1)        );//合并结果        CompletableFuture.allOf(rewardExpFuture1, credentialFuture1, jobsFuture1).join();

2.8、优化程序逻辑

       思想:优化程序逻辑、程序代码,是可以节省接口耗时的。比如:程序创建多不必要的对象、或者程序逻辑混乱,多次重复查数据库、又或者实现逻辑算法不是最高效的等等,在多人维护一个项目时比较多见。

       解决思路:我们需要针对接口整体做重构,梳理清楚代码逻辑,评估每个代码块的作用和用途,检查是否存在不必要的对象创建、逻辑调用或者代码细节之类的,是否符合一些编码规范等。

2.9、深度分页问题

select id,name,sex from person where create_time> '2020-09-19' limit 100000,100;

      limit 100000,100 意味着会扫描 100100 行,然后返回 100 行,丢弃掉前 100000 行。所以执行速度很慢。一般可以采用如下方式优化:

2.9.1、 标签记录法

       就好像看书一样,上次看到哪里了,你就折叠一下或者夹个书签,下次来看的时候,直接就翻到了。

select id,name,sex from person where id> 100000limit 100;

      但是局限性是需要一个连续自增的字段,而且需要前端把上次最大值传给后端。

2.9.2、延迟关联法(个人推荐)

      延迟关联法,就是把条件转移到主键索引树,然后减少回表。优化对比如下:(测试数据为100万)

#延迟关联法耗时0.040sSELECT s1.s_id,s1.student_name,s1.phone FROM student s1INNER JOIN ( SELECT s.s_id FROM student s WHERE s.age > 20 LIMIT 100000, 100 ) AS s2 ON s1.s_id = s2.s_id
# 正常查询0.674sSELECT s1.s_id,s1.student_name,s1.phone FROM student s1 WHERE s1.age > 20 LIMIT 100000, 100

      优化思路:先通过idx_age二级索引树查询到满足条件的主键ID,再与原表通过主键ID内连接,这样后面直接走了主键索引了,同时也减少了回表。

 2.10、锁粒度避免过粗

       锁一般是为了在高并发场景下保护共享资源采用的一种手段。但是若锁的粒度太粗,会很影响接口性能。

        关于锁粒度:就是你要锁的范围有多大,无论是使用synchronized加锁还是redis分布式锁,只需要在共享临界资源加锁即可,不涉及共享资源的,就不必要加锁。例如:你在家里上卫生间,你只需要把卫生间锁住,而不用把家里的每个房间都锁住。参考案例如下:

误的加锁方式:

//非共享资源方法private void methodA(){}//共享资源方法private void methodB(){}//加锁private int wrong(){    synchronized(this){      methodA();      methodB();    }}

正确的加锁方式:

//非共享资源方法private void methodA(){}//共享资源方法private void methodB(){}//加锁private int right(){    methodA(); //非共享资源方法    synchronized(this){      methodB(); //共享资源方法    }}

2.11、避免长事务问题

       所谓长事务问题,就是session运行时间较长的事务,期间可能伴随cpu、内存升高,严重者可导致DB服务端整体响应缓慢,导致在线应用无法使用,并且由于事务一致不提交,也会导致数据库连接被占用,影响到别的请求访问数据库,影响别的接口性能。所以在线高并发业务中应该尽量避免长事务的发生。产生长事务的原因,除了sql本身可能存在问题外,和应用层的事务控制逻辑也有很大的关系。

例如:直接使用@Transactional 注解,Spring的声明式事务,整个方法都在事务中,而且里面存在远程RPC调用,容易出现长事务问题。

@Transactionalpublic int createUser(User user){    //保存用户信息    userDao.save(user);    //更新数据标识    passCertDao.updateFlag(user.getPassId());    // 该方法为远程RPC接口 发送邮件    sendEmailRpc(user.getEmail());    return user.getUserId();}

2.11.1、如何避免长事务问题?

     1、RPC远程调用不要放到事务里面;

     2、查询操作尽量放到事务之外;

     3、事务中避免处理太多数据;

     4、并发场景下,尽量避免使用@Transactional注解声明式事务粒度太大,使用TransactionTemplate的编程式事务灵活控制事务的范围。

2.12.2、如何解决长事务问题?

      1、增加对长事务的监控,记录长事务的logId,根据logId能查询到整个请求调用链日志,可以明确是哪个服务的哪个接口的哪个方法产生的;

       2、根据日志检查是否存在慢SQL;

      3、检查对应服务是否存在RPC远程调用包裹在事务中;

      4、检查是否接口中使用了@Transactional注解声明式事务。

三、总结

       解决服务接口性能问题,是程序员进阶的必经之路。找到接口性能问题出现位置,更高一级思考问题,站在接口设计者的角度去开发需求,会避免很多这样的问题,再结合以上介绍的优化方案进行处理,也是降本增效的一种行之有效的方式。

更多详细资料,请关注个人微信公众号或搜索“程序猿小杨”添加。

参考文章:

https://blog.csdn.net/m0_71777195/article/details/131018507?spm=1001.2014.3001.5502

https://blog.csdn.net/m0_71777195/article/details/130741662?spm=1001.2014.3001.5502

                                                                                                          

                                                                                                                 觉得有用,请点这里↓↓↓

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/748340.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何利用PLC远程通讯模块搭建PLC远程控制系统

随着工业自动化的快速发展&#xff0c;PLC&#xff08;可编程逻辑控制器&#xff09;作为重要的控制设备&#xff0c;被广泛应用于工厂和生产线的自动化控制系统。然而&#xff0c;传统的PLC控制系统受限于物理连接和局域网范围内的限制&#xff0c;无法实现远程监控和控制&…

RabbitMQ 常用 API

RabbitMQ 常用 API Connection 和 Channel 的创建、关闭 创建 Connection ConnectionFactory factory new ConnectionFactory(); // 方式1&#xff1a;通过设置参数创建 factory.setHost(IP_ADDRESS); factory.setPort(PORT); factory.setUsername("guest"); facto…

STM32 Proteus仿真可设置时间红绿灯-0075

STM32 Proteus仿真可设置时间红绿灯-0075 Proteus仿真小实验&#xff1a; STM32 Proteus仿真可设置时间红绿灯-0075 功能&#xff1a; 硬件组成&#xff1a;STM32F103C6单片机 74HC595串入并出芯片4个2位数码管显示十字路口红绿灯时间多个按键 1.包含机动车指示灯(红、黄、…

python实现前后端学生管理系统(前后端分离)

⭐作者介绍&#xff1a;大二本科网络工程专业在读&#xff0c;持续学习Java&#xff0c;努力输出优质文章 ⭐作者主页&#xff1a;逐梦苍穹 ⭐所属专栏&#xff1a;项目。 目录 1、前言2、思路总览首页注册登录管理员 3、详细代码3.1、项目目录3.2、static3.3、templates3.3.1、…

STM32 Proteus仿真全自动洗衣机洗涤脱水-0074

STM32 Proteus仿真全自动洗衣机洗涤脱水-0074 Proteus仿真小实验&#xff1a; STM32 Proteus仿真全自动洗衣机洗涤脱水-0074 功能&#xff1a; 硬件组成&#xff1a;STM32F103R6单片机LCD1602显示器 L298N驱动电机正反转蜂鸣器LED指示灯多个按键(标准洗&#xff0c;快速洗&a…

从一个bug开始,理解Fragment和ViewPager2的状态恢复流程

作者&#xff1a;西片 在使用Fragment和ViewPager2时遇到了一个奇怪的bug&#xff0c;于是顺藤摸瓜学习了一下Fragment和View的状态保存恢复流程&#xff0c;解决方法在最后面 首先看一下崩溃调用栈 java.lang.IllegalStateException: Expected the adapter to be fresh while…

RWKV系列2-ChatRWKV

注意使用最新的版本 提示词 ##### 步骤4.1英文对话指令say something --> chat with bot. use \\n for new line. --> alternate chat replyreset --> reset chatgen YOUR PROMPT --> free single-round generation with any prompt. use \\n for new line.i YOUR…

DRF框架中的GenericAPIView类

一、GenericAPIView类源码 class GenericAPIView(views.APIView):"""Base class for all other generic views."""# Youll need to either set these attributes,# or override get_queryset()/get_serializer_class().# If you are overriding …

Java中List的使用方法简单介绍

Java中List的使用方法简单介绍 java中的List就是一种集合对象&#xff0c;将所有的对象集中到一起存储。List里面可以放任意的java对象&#xff0c;也可以直接放值。 使用方法很简单&#xff0c;类似于数组。 使用List之前必须在程序头引入java.util.* import java.util.*; pub…

记一次 .NET 某游戏服务后端 内存暴涨分析

一&#xff1a;背景 1. 讲故事 前几天有位朋友找到我&#xff0c;说他们公司的后端服务内存暴涨&#xff0c;而且CPU的一个核也被打满&#xff0c;让我帮忙看下怎么回事&#xff0c;一般来说内存暴涨的问题都比较好解决&#xff0c;就让朋友抓一个 dump 丢过来&#xff0c;接…

Flowable边界事件-错误边界事件

错误边界事件 错误边界事件一、定义1. 图形标记2. 设置错误 选择错误3. XML标记 二、测试用例2.1 错误边界事件xml文件2.2 错误边界事件测试用例 总结 错误边界事件 一、定义 接收到错误编码触发事件 1. 图形标记 2. 设置错误 选择错误 自动审批的配置类配置javaDelegate和抛…

OpenCV安装及案例

目录 常见国内源: 一、简介 二、opencv安装 2.1在虚拟环境中安装 Original error was: No module named ‘numpy.core._multiarray_umath‘ 2.2在conda中安装 三、基本API opencv入门案例: 常见国内源: 清华大学: https://pypi.tuna.tsinghua.edu.cn/simple/ 阿里云:…

opencv 图像基础处理_灰度图像

opencv 学习2_灰度图像 二值图像表示起来简单方便&#xff0c;但是因为其仅有黑白两种颜色&#xff0c;所表示的图像不够细腻。如果想要表现更多的细节&#xff0c;就需要使用更多的颜色。例如&#xff0c;图 2-3 中的 lena 图像是一幅灰度图像&#xff0c; 它采用了更多的数值…

HarmonyOS学习路之方舟开发框架—基于ArkTS的声明式开发范式

UI开发&#xff08;ArkTS声明式开发范式&#xff09;概述 基于ArkTS的声明式开发范式的方舟开发框架是一套开发极简、高性能、支持跨设备的UI开发框架&#xff0c;提供了构建HarmonyOS应用UI所必需的能力&#xff0c;主要包括&#xff1a; ArkTS ArkTS是UI开发语言&#xff0…

Apache Doris (二十五):Doris 数据导入(三)Broker Load-1

目录 1. 基本原理 2. Broker Load语法 进入正文之前&#xff0c;欢迎订阅专题、对博文点赞、评论、收藏&#xff0c;关注IT贫道&#xff0c;获取高质量博客内容&#xff01; 宝子们订阅、点赞、收藏不迷路&#xff01;抓紧订阅专题&#xff01; Apache Doris架构中除了有BE和…

华南农业大学|图像处理与分析技术综合设计|题目解答:定位数显区域并分离电表数字

设计任务 图 28 是一幅正在运行的数字电表图像&#xff08;ipa28.jpg&#xff09;&#xff0c;试采用图像处理与分析 技术&#xff0c;设计适当的算法和程序&#xff0c;找出电表的数显区域&#xff0c;计算目标区域的长宽比 和像素面积&#xff1b;并提取其中面积最大的 …

免费的云数据库:探索PlanetScale,划分分支的MySQL Serverless平台

最近我发现了一个非常有趣的国外MySQL Serverless平台&#xff0c;它叫做PlanetScale。这个平台不仅仅是一个数据库&#xff0c;它能像代码一样轻松地创建开发和测试环境。你可以从主库中拉出一个与之完全相同结构的development或staging数据库&#xff0c;并在这个环境中进行开…

使用Pycharm

本人没有单独安装python&#xff0c;而是直接安装了anaconda 使用Pycharm创建项目 项目取名为HelloWorld&#xff0c;环境使用前面安装的anaconda pycharm安装模块的方法&#xff1a; 打开Pycharm>File > Settings>Project: Python>Project Interpreter

面试题更新之-hook中setState原理

文章目录 hook是什么&#xff1f;hook中setState原理 hook是什么&#xff1f; 在React中&#xff0c;Hook是一种用于在函数组件中添加状态和其他React特性的函数。它们被引入到React 16.8版本中&#xff0c;旨在解决使用类组件编写复杂逻辑时出现的一些问题。 使用Hook&#…