接口优化的常见方案实战总结

news2024/12/25 12:55:03

一、背景

针对老项目,去年做了许多降本增效的事情,其中发现最多的就是接口耗时过长的问题,就集中搞了一次接口性能优化。本文将给小伙伴们分享一下接口优化的通用方案。







二、接口优化方案总结

1. 批处理

批量思想:批量操作数据库,这个很好理解,我们在循环插入场景的接口中,可以在批处理执行完成后一次性插入或更新数据库,避免多次 IO。

//for循环单笔入库
list.stream().forEatch(msg->{
    insert();
});
//批量入库
batchInsert();

2. 异步处理

异步思想:针对耗时比较长且不是结果必须的逻辑,我们可以考虑放到异步执行,这样能降低接口耗时。

例如一个理财的申购接口,入账写入申购文件是同步执行的,因为是 T+1 交易,后面这两个逻辑其实不是结果必须的,我们并不需要关注它的实时结果,所以我们考虑把入账写入申购文件改为异步处理。如图所示:



至于异步的实现方式,可以用线程池,也可以用消息队列,还可以用一些调度任务框架。

3. 空间换时间

一个很好理解的空间换时间的例子是合理使用缓存,针对一些频繁使用且不频繁变更的数据,可以提前缓存起来,需要时直接查缓存,避免频繁地查询数据库或者重复计算。

需要注意的事,这里用了合理二字,因为空间换时间也是一把双刃剑,需要综合考虑你的使用场景,毕竟缓存带来的数据一致性问题也挺令人头疼。

这里的缓存可以是 R2M,也可以是本地缓存、memcached,或者 Map。

举一个股票工具的查询例子:

因为策略轮动的调仓信息,每周只更新一次,所以原来的调接口就去查库的逻辑并不合理,而且拿到调仓信息后,需要经过复杂计算,最终得出回测收益和跑赢沪深指数这些我们想要的结果。如果我们把查库操作和计算结果放入缓存,可以节省很多的执行时间。如图:



4. 预处理

也就是预取思想,就是提前要把查询的数据,提前计算好,放入缓存或者表中的某个字段,用的时候会大幅提高接口性能。跟上面那个例子很像,但是关注点不同。

举个简单的例子:理财产品,会有根据净值计算年化收益率的数据展示需求,利用净值去套用年化收益率计算公式计算的逻辑我们可以采用预处理,这样每一次接口调用直接取对应字段就可以了。

5. 池化思想

我们都用过数据库连接池,线程池等,这就是池思想的体现,它们解决的问题就是避免重复创建对象或创建连接,可以重复利用,避免不必要的损耗,毕竟创建销毁也会占用时间。

池化思想包含但并不局限于以上两种,总的来说池化思想的本质是预分配与循环使用,明白这个原理后,我们即使是在做一些业务场景的需求时,也可以利用起来。

比如:对象池

6. 串行改并行

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

比如,理财的持仓信息展示接口,我们既需要查询用户的账户信息,也需要查询商品信息和 banner 位信息等等来渲染持仓页,如果是串行,基本上接口耗时就是累加的。如果是并行,接口耗时将大大降低。

如图:





7. 索引

加索引能大大提高数据查询效率,这个在接口设计之出也会考虑到,这里不再多赘述,随着需求的迭代,我们重点整理一下索引不生效的一些场景,希望对小伙伴们有所帮助。

具体不生效场景不再一一举例,后面有时间的话,单独整理一下。



8. 避免大事务

所谓大事务问题,就是运行时间较长的事务,由于事务一致不提交,会导致数据库连接被占用,影响到别的请求访问数据库,影响别的接口性能。

举个例子:

    @Transactional(value = "taskTransactionManager", propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED, rollbackFor = {RuntimeException.class, Exception.class})
    public BasicResult purchaseRequest(PurchaseRecord record) {
        BasicResult result = new BasicResult();
        //插入账户任务
        taskMapper.insert(ManagerParamUtil.buildTask(record, TaskEnum.Task_type.pension_account.type(), TaskEnum.Account_bizType.purchase_request.type()));
        //插入同步任务
        taskMapper.insert(ManagerParamUtil.buildTask(record, TaskEnum.Task_type.pension_sync.type(), TaskEnum.Sync_bizType.purchase.type()));
        //插入影像件上传任务
        taskMapper.insert(ManagerParamUtil.buildTask(record, TaskEnum.Task_type.pension_sync.type(), TaskEnum.Sync_bizType.cert.type()));
        result.setInfo(ResultInfoEnum.SUCCESS);
        return result;
    }

上面这块代码主要是申购申请完成后,执行一系列的后续操作,如果现在新增申购完成后,发送 push 通知用户的需求。很有可能我们会在后面直接追加,如下图所示:事务中嵌套 RPC 调用,即非 DB 操作,这些非 DB 操作如果耗时较大的话,可能会出现大事务问题。大数据引发的问题主要有:死锁、接口超时、主从延迟等。

    @Transactional(value = "taskTransactionManager", propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED, rollbackFor = {RuntimeException.class, Exception.class})
    public BasicResult purchaseRequest(PurchaseRecord record) {
        BasicResult result = new BasicResult();
        ...
        pushRpc.doPush(record);        
        result.setInfo(ResultInfoEnum.SUCCESS);
        return result;
    }

所以为避免大事务问题,我们可以通过以下方案规避:

1,RPC 调用不放到事务里面

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

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

9. 优化程序结构

程序结构问题一般出现在多次需求迭代后,代码叠加形成。会造成一些重复查询、多次创建对象等耗时问题。在多人维护一个项目时比较多见。解决起来也比较简单,我们需要针对接口整体做重构,评估每个代码块的作用和用途,调整执行顺序。

10. 深分页问题

深分页问题比较常见,分页我们一般最先想到的就是 limit ,为什么会慢,我们可以看下这个 SQL:

    select * from purchase_record where productCode = 'PA9044' and status=4 order by orderTime desc limit 100000,200 

limit 100000,200 意味着会扫描 100200 行,然后返回 200 行,丢弃掉前 100000 行。所以执行速度很慢。一般可以采用标签记录法来优化,比如:

    select * from purchase_record where productCode = 'PA9044' and status=4 and id > 100000 limit 200

这样优化的好处是命中了主键索引,无论多少页,性能都还不错,但是局限性是需要一个连续自增的字段

11.SQL 优化

sql 优化能大幅提高接口的查询性能,由于本文重点讲述接口优化的方案,具体 sql 优化不再一一列举,小伙伴们可以结合索引、分页、等关注点考虑优化方案。

12. 锁粒度避免过粗

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

关于锁粒度:就是你要锁的范围有多大,不管是 synchronized 还是 redis 分布式锁,只需要在临界资源处加锁即可,不涉及共享资源的,不必要加锁,就好比你要上卫生间,只需要把卫生间的门锁上就可以,不需要把客厅的门也锁上。

错误的加锁方式:

        //非共享资源
        private void notShare(){
        }
        //共享资源
        private void share(){
        }
        private int wrong(){
            synchronized (this) {
                share();
                notShare();
            }
        }

正确的加锁方式:

       //非共享资源
        private void notShare(){
        }
        //共享资源
        private void share(){
        }
        private int right(){
            notShare();
            synchronized (this) {
                share();

            }
        } 

三、最后

接口性能问题形成的原因思考

我相信很多接口的效率问题不是一朝一夕形成的,在需求迭代的过程中,为了需求快速上线,采取直接累加代码的方式去实现功能,这样会造成以上这些接口性能问题。

变换思路,更高一级思考问题,站在接口设计者的角度去开发需求,会避免很多这样的问题,也是降本增效的一种行之有效的方式。

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

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

相关文章

Maven下载与配置

Maven官网链接: https://maven.apache.org/ 进入后首页展示最新的下载地址 如果要下载历史版本,可以搜索 legacy archives 来快速找到这个地方,点击进入 下载完毕后,解压放到D盘(或根据个人喜好存放) 首先…

4月22日丨【云数据库技术沙龙】技术进化,让数据更智能

4月22日,云数据库技术沙龙“MySQL x ClickHouse”专场 “MySQL x ClickHouse” 技术沙龙,本次沙龙以“技术进化,让数据更智能”为主题,汇聚字节跳动、阿里云、玖章算术、华为云、腾讯云、百度等众多数据库厂商的技术大咖&#xf…

2023/4/10-2023/4/18周报

1 高德地图 运行抱错 INVALID_USER_SCODE 这里是错误信息对应原因 错误信息列表-参考手册-地图 JS API | 高德地图API 这里是高德地图api设置说明 准备-入门-教程-地图 JS API | 高德地图API 如果你自己能排查出错误 那不用看我的,如果都写的对还是抱错…

list容器

1、list容器简介 链表是以中物理存储单元上的非连续、非顺序的存储结构,数据元素的逻辑顺序都是通过链表中的指针连接次序实现的。链表由一系列的结点(链表中每一个元素被称为结点)组成,结点可以在运行时动态生成。每一个结点包括…

系统集成项目管理工程师 笔记(第三章:信息系统集成专业技术知识)

文章目录 3.1 信息系统建设 1333.1.1 信息系统的生命周期 1333.1.2 信息系统开发方法 133 3.3 软件工程 1353.3.1 软件需求分析与定义 1353.3.2 软件设计、测试与维护 1353.3.3 软件质量保证及质量评价 1363.3.4 软件配置管理 136(六大活动)3.3.5 软件过…

瑞芯微RK3568四核核心板芯片简介

引言 RK3568是瑞芯微出品的一款定位中高端的通用型SoC,采用22nm先进制程工艺,集成4核 arm 架构 A55 处理器和 Mali G52 2EE 图形处理器,支持4K解码和1080P编码。RK3568支持 SATA/PCIE/USB3.0 等各类型外围接口,内置独立的NPU&…

亚马逊云科技数字化技术撬动千亿市场,民航客机改装是好生意

很多人对庞大的民航客机的印象还停留在其天文数字般的价格,随着运营时间的推移,将客机转货机的航空维修产业应时而生。民航业客机改货机的市场不断增长,面对飞机客改货过程中的海量图纸与复杂工艺流程,汉端科技在西云数据运营的亚…

darknet_ros+yolo+realsenseD455+Ubuntu18.04+ROS

很想实现ros和yolo的联合使用,所以找到了darknet_ros这个包,我感觉难点不在于工程,而在于yolo的环境配置。本文将从环境入手逐步实现功能。 1.设备 相机:realsenseD455 笔记本:T440P 显卡:GT-730M 算力&am…

03-漏洞发现API接口服务等

漏洞发现-API接口服务之漏洞探针类型利用修复 一、思维导图 二、测试思路 1、信息收集之信息利用 第一步:首先识别网站是否有cdn,waf等产品,有则需要绕过。 第二步:扫描收集到的网站的端口信息,真实ip地址。ip绑定…

再学一下Feign的原理

简介 Feign是Spring Cloud Netflix组件中的一个轻量级Restful的HTTP服务客户端,它简化了服务间调用的方式。 Feign是一个声明式的web service客户端.它的出现使开发web service客户端变得更简单.使用Feign只需要创建一个接口加上对应的注解, 比如FeignClient注解。…

Vue实现自动化平台(二)--实现登录页面首页

上一章,vue项目的创建: Vue脚手架Vue CLI 使用_做测试的喵酱的博客-CSDN博客 github地址:https://github.com/18713341733/vuemiaotest 这个目前只是用来练手的,项目还没成型。等以后我写完了,再更新一下项目链接。…

python基于机器学习的姓名预测性别网页app开发

前言 做这个项目的起因是之前csdn给我推荐了一个问答:基于机器学习的姓名预测性别的手机app开发。我点进去发现已经有人回答了,链接点进去一看,好家伙,这不是查表算概率吗,和机器学习有半毛钱关系。而且我觉得用姓名预…

《Kubernetes部署篇:Ubuntu20.04基于containerd部署kubernetes1.24.12单master集群》

一、架构图 如下图所示: 二、环境信息 主机名K8S版本系统版本内核版本IP地址备注k8s-master-621.24.12Ubuntu 20.04.5 LTS5.15.0-69-generic192.168.1.62master节点k8s-worker-631.24.12Ubuntu 20.04.5 LTS5.15.0-69-generic192.168.1.63worker节点k8s-worker-641…

Fast DDS 介绍

前面已经简要介绍过DDS协议规范了,接下来我们来看一个它的C实现----Fast DDS。 eProsima Fast-DDS是eprosima对于DDS的C实现,这是一个免费开源软件,遵循Apache License 2.0。eProsima Fast DDS在性能,功能和对最新版本RTPS标准&a…

Day17-对象

文章目录一 函数作业讲解二 函数版图书管理系统三 对象一 引入思考二 对象的创建和使用案例1案例2案例3案例4案例5-描述手机案例6-描述一组手机案例7-把对象作为函数的参数一 函数作业讲解 <script>//1编写函数&#xff0c;计算圆的面积和周长&#xff0c;在函数外由用户…

俄罗斯电商平台ozon的崛起,卖家可以使用测评补单方式打造爆款吗?

OZON俗称俄罗斯亚马逊&#xff0c;1998年成立&#xff0c;是俄罗斯唯一的多品类综合B2C电商平台&#xff0c;也是目前欧洲第四大电商市场。 作为俄罗斯互联网公司五强的OZON平台&#xff0c;拥有庞大的消费者群体&#xff0c;从2018年仅为480万人&#xff0c;到2021年就增长到…

【C++进阶】Makefile基础(一)

文章目录1. 环境2. 规则3. 原理4. 伪目标Makefile 其实只是一个指示 make 程序&#xff08;后面简称 make 或有时称之为 make 命令&#xff09;如何为我们工作的命令文件&#xff0c;我们说 Makefile 其实是在说 make&#xff0c;这一点要有很清晰的认识。而对于我们的项目来说…

21-CSS

目录 1.CSS是什么&#xff1f; 2.CSS基本语法 3.CSS类型 3.1.行内样式&#xff08;适用范围最小&#xff09; 3.2.内部样式&#xff08;适用范围适中&#xff09; 3.3.外部样式&#xff08;适用范围最大&#xff09; PS&#xff1a;关于缓存 3.4.多种样式优先级 4.代码…

浅析Linux Socket套接字编程中的 5 个隐患

隐患 1&#xff0e;忽略返回状态 第一个隐患很明显&#xff0c;但它是开发新手最容易犯的一个错误。如果您忽略函数的返回状态&#xff0c;当它们失败或部分成功的时候&#xff0c;您也许会迷失。反过来&#xff0c;这可能传播错误&#xff0c;使定位问题的源头变得困难。 捕…

XTDrone PX4 仿真平台|Ubuntu20.04 环境搭建失败记录

XTDrone PX4 仿真平台|Ubuntu20.04 环境搭建失败记录仿真环境搭建依赖安装ROS安装Gazebo安装MAVROS安装PX4配置仿真环境搭建 依赖安装 在使用apt安装的过程中&#xff08;包括之后ROS的安装&#xff09;&#xff0c;如果出现难以解决的依赖问题&#xff0c;可以使用aptitude …