接口优化的常见方案实战经验

一、背景

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

二、接口优化方案总结

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.rhkb.cn/news/220131.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【漏洞复现】CVE-2023-6848 kodbox远程命令执行

漏洞描述 kodbox 是一个网络文件管理器。它也是一个网页代码编辑器,允许您直接在网页浏览器中开发网站。您可以在基于 Linux、Windows 或 Mac 的平台上在线或本地运行 kodbox。唯一的要求是要有 PHP 5及以上。 kalcaddle kodbox 中发现漏洞,最高版本为 1.48。它已被宣布为关…

【算法与数据结构】455、LeetCode分发饼干

文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引,可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析:因为大饼干可以满足大胃口的孩子也必然可以满足小胃口的孩子,如果要尽可能的满足孩子的胃口…

processon使用及流程图和泳道图的绘画(登录界面流程图,门诊流程图绘制门诊泳道图,住院泳道图,OA会议泳道图),Axure自定义元件

目录 一.processon图形的使用场景介绍 二.流程图绘画 三.泳道图的绘画 1.绘制门诊流程图绘制门诊泳道图 2. 绘制住院泳道图​编辑 3.绘制药库采购入库流程图 4.绘制OA会议泳道图 四.Axure自定义元件 1.Axure载入元件库 一.processon图形的使用场景介绍 二.流程图绘画 示例&…

1846_安全SPI

Grey 全部学习内容汇总:GitHub - GreyZhang/g_embedded: some embedded basic knowledge. 1846_安全SPI SPI是一种常见的通信方式,在汽车电子中比较常用。但是如果涉及到安全相关的设计,可能得考虑更多。而SPI协议本身没有很好的标准化&am…

GO的sql注入盲注脚本

之间学习了go的语法 这里就开始go的爬虫 与其说是爬虫 其实就是网站的访问如何实现 因为之前想通过go写sql注入盲注脚本 发现不是那么简单 这里开始研究一下 首先是请求网站 这里貌似很简单 package mainimport ("fmt""net/http" )func main() {res, …

【C语言】SCU安全项目1-FindKeys

目录 前言 命令行参数 16进制转字符串 extract_message1 process_keys12 extract_message2 main process_keys34 前言 因为这个学期基本都在搞CTF的web方向,C语言不免荒废。所幸还会一点指针相关的知识,故第一个安全项目做的挺顺利的&#xff0c…

verilog基础语法-计数器

概述: 计数器是FPGA开发中最常用的电路,列如通讯中记录时钟个数,跑马灯中时间记录,存储器中地址的控制等等。本节给出向上计数器,上下计数器以及双向计数器案例。 内容 1. 向上计数器 2.向下计数器 3.向上向下计数…

第一个程序(STM32F103点灯)

点亮LED 看原理图确定控制LED的引脚看主芯片手册确定如何设置/控制引脚写程序 LED有很多种,像插脚的,贴片的。 它们长得完全不一样,因此我们在原理图中将它抽象出来。 嵌入式系统中,一个LED的电阻非常低,I U/R&…

GZ015 机器人系统集成应用技术样题5-学生赛

2023年全国职业院校技能大赛 高职组“机器人系统集成应用技术”赛项 竞赛任务书(学生赛) 样题5 选手须知: 本任务书共 24页,如出现任务书缺页、字迹不清等问题,请及时向裁判示意,并进行任务书的更换。参赛队…

04_Web框架之Django一

Web框架之Django一 学习目标和内容 1、能够描述Django的作用 2、能够使用Django创建应用 3、能够使用GET和POST请求方式进行传参 4、能够使用Django的函数式方法定义视图 5、能够进行Django的配置文件修改 6、能够基本使用Django的路由定义 一、Django相关介绍 1、什么是Djan…

[Kubernetes]3. k8s集群Service详解

在上一节讲解了k8s 的pod,deployment,以及借助pod,deployment来部署项目,但会存在问题: 每次只能访问一个 pod,没有负载均衡自动转发到不同 pod访问还需要端口转发Pod重创后IP变了,名字也变了针对上面的问题,可以借助Service来解决,下面就来看看Service怎么使用 一.Service详…

IDEA中alt enter不显示创建实现类快捷键

alt enter不显示创建实现类快捷键是因为idea中的设置没打开,按照一下设置打开就可以了。 点击setting-->>editor-->>intentions-->>java下的declaration 如下图所示:

【后端学前端】第四天 css动画 垂直轮播效果(css变量、位移缩放动画、动画延迟)

1、学习信息 视频地址&#xff1a;css动画 垂直轮播效果&#xff08;css变量、位移缩放动画、动画延迟&#xff09;_哔哩哔哩_bilibili 2、源码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title…

Go环境安装

目录 下载地址 安装 macos环境 window及其他环境 GOPROXY 非常重要 Go开发编辑器 下载地址 Go官网下载地址&#xff1a;https://golang.org/dl/ Go官方镜像站&#xff08;推荐&#xff09;&#xff1a;https://golang.google.cn/dl/ 选择要下载的系统版本&#xff1a; 安装 注意…

SpringIOC之作用域Scope

博主介绍:✌全网粉丝5W+,全栈开发工程师,从事多年软件开发,在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战,博主也曾写过优秀论文,查重率极低,在这方面有丰富的经验✌ 博主作品:《Java项目案例》主要基于SpringBoot+MyBatis/MyBatis-plus+…

4.docker镜像及相关命令

目录 1 查看所有镜像 docker images 1.1 基本用法 1.2 docker images -q 只显示所有镜像ID 1.3 docker images -f [筛选条件] -q 只显示符合条件的所有镜像ID 1.4 docker images --no-trunc 显示完整的IMAGE ID 1.5 docker images --format [模板] 使用模板 2 从…

得帆信息创始人-张桐,受邀出席 BV百度风投AIGC主题论坛

近日&#xff0c;得帆信息创始人兼CEO张桐&#xff0c;作为百度风投被投代表企业创始人受邀出席“向未来&#xff0c;共成长” BV百度风投AIGC主题论坛。 与包括上海市徐汇区相关部门领导、百度集团相关事业部负责人及代表&#xff0c;以及来自国寿资本、中网投、麦顿投资的投资…

什么是关键词排名蚂蚁SEO

关键词排名是指通过搜索引擎优化&#xff08;SEO&#xff09;技术&#xff0c;将特定的关键词与网站相关联&#xff0c;从而提高网站在搜索引擎中的排名。关键词排名对于网站的流量和用户转化率具有至关重要的影响&#xff0c;因此它是SEO工作中最核心的部分之一。 如何联系蚂…

Windows mysql5.7 执行查询/开启/测试binlog---简易记录

前言&#xff1a;基于虚拟机mysql版本为5.7&#xff0c;增量备份测试那就要用到binlog… 简述&#xff1a;二进制日志&#xff08;binnary log&#xff09;以事件形式记录了对MySQL数据库执行更改的所有操作。 binlog是记录所有数据库表结构变更&#xff08;例如CREATE、ALTER…

Docker部署MinIO对象存储服务器结合内网穿透实现远程访问

文章目录 前言1. Docker 部署MinIO2. 本地访问MinIO3. Linux安装Cpolar4. 配置MinIO公网地址5. 远程访问MinIO管理界面6. 固定MinIO公网地址 前言 MinIO是一个开源的对象存储服务器&#xff0c;可以在各种环境中运行&#xff0c;例如本地、Docker容器、Kubernetes集群等。它兼…