11 个接口性能优化技巧(上)【送源码】
接口性能优化对于从事后端开发的同学来说,肯定再熟悉不过了,因为它是一个跟开发语言无关的公共问题。
该问题说简单也简单,说复杂也复杂。
有时候,只需加个索引就能解决问题。
有时候,需要做代码重构。
有时候,需要增加缓存。
有时候,需要引入一些中间件,比如mq。
有时候,需要需要分库分表。
有时候,需要拆分服务。
等等。。。
导致接口性能问题的原因千奇百怪,不同的项目不同的接口,原因可能也不一样。
本文我总结了一些行之有效的,优化接口性能的办法,给有需要的朋友一个参考。
1.索引
接口性能优化大家第一个想到的可能是:优化索引
。
没错,优化索引的成本是最小的。
你通过查看线上日志或者监控报告,查到某个接口用到的某条sql语句耗时比较长。
这时你可能会有下面这些疑问:
-
该sql语句加索引了没?
-
加的索引生效了没?
-
mysql选错索引了没?
1.1 没加索引
sql语句中where
条件的关键字段,或者order by
后面的排序字段,忘了加索引,这个问题在项目中很常见。
项目刚开始的时候,由于表中的数据量小,加不加索引sql查询性能差别不大。
后来,随着业务的发展,表中数据量越来越多,就不得不加索引了。
可以通过命令:
show index from `order`;
能单独查看某张表的索引情况。
也可以通过命令:
show create table `order`;
查看整张表的建表语句,里面同样会显示索引情况。
通过ALTER TABLE
命令可以添加索引:
ALTER TABLE `order` ADD INDEX idx_name (name);
也可以通过CREATE INDEX
命令添加索引:
CREATE INDEX idx_name ON `order` (name);
不过这里有一个需要注意的地方是:想通过命令修改索引,是不行的。
目前在mysql中如果想要修改索引,只能先删除索引,再重新添加新的。
删除索引可以用DROP INDEX
命令:
ALTER TABLE `order` DROP INDEX idx_name;
用DROP INDEX
命令也行:
DROP INDEX idx_name ON `order`;
1.2 索引没生效
通过上面的命令我们已经能够确认索引是有的,但它生效了没?此时你内心或许会冒出这样一个疑问。
那么,如何查看索引有没有生效呢?
答:可以使用explain
命令,查看mysql的执行计划,它会显示索引的使用情况。
例如:
explain select * from `order` where code='002';
结果:
通过这几列可以判断索引使用情况,执行计划包含列的含义如下图所示:
说实话,sql语句没有走索引,排除没有建索引之外,最大的可能性是索引失效了。
下面说说索引失效的常见原因:
如果不是上面的这些原因,则需要再进一步排查一下其他原因。
1.3 选错索引
此外,你有没有遇到过这样一种情况:明明是同一条sql,只有入参不同而已。有的时候走的索引a,有的时候却走的索引b?
没错,有时候mysql会选错索引。
必要时可以使用force index
来强制查询sql走某个索引。
至于为什么mysql会选错索引,后面有专门的文章介绍的,这里先留点悬念。
2. sql优化
如果优化了索引之后,也没啥效果。
接下来试着优化一下sql语句,因为它的改造成本相对于java代码来说也要小得多。
下面给大家列举了sql优化的15个小技巧:
3. 远程调用
很多时候,我们需要在某个接口中,调用其他服务的接口。
比如有这样的业务场景:
在用户信息查询接口中需要返回:用户名称、性别、等级、头像、积分、成长值等信息。
而用户名称、性别、等级、头像在用户服务中,积分在积分服务中,成长值在成长值服务中。为了汇总这些数据统一返回,需要另外提供一个对外接口服务。
于是,用户信息查询接口需要调用用户查询接口、积分查询接口 和 成长值查询接口,然后汇总数据统一返回。
调用过程如下图所示:
调用远程接口总耗时 530ms = 200ms + 150ms + 180ms
显然这种串行调用远程接口性能是非常不好的,调用远程接口总的耗时为所有的远程接口耗时之和。
那么如何优化远程接口性能呢?
3.1 并行调用
上面说到,既然串行调用多个远程接口性能很差,为什么不改成并行呢?
如下图所示:
调用远程接口总耗时 200ms = 200ms(即耗时最长的那次远程接口调用)
在java8之前可以通过实现Callable
接口,获取线程返回结果。
java8以后通过CompleteFuture
类实现该功能。我们这里以CompleteFuture为例:
public UserInfo getUserInfo(Long id) throws InterruptedException, ExecutionException {final UserInfo userInfo = new UserInfo();CompletableFuture userFuture = CompletableFuture.supplyAsync(() -> {getRemoteUserAndFill(id, userInfo);return Boolean.TRUE;}, executor);CompletableFuture bonusFuture = CompletableFuture.supplyAsync(() -> {getRemoteBonusAndFill(id, userInfo);return Boolean.TRUE;}, executor);CompletableFuture growthFuture = CompletableFuture.supplyAsync(() -> {getRemoteGrowthAndFill(id, userInfo);return Boolean.TRUE;}, executor);CompletableFuture.allOf(userFuture, bonusFuture, growthFuture).join();userFuture.get();bonusFuture.get();growthFuture.get();return userInfo;
}
温馨提醒一下,这两种方式别忘了使用线程池。示例中我用到了executor,表示自定义的线程池,为了防止高并发场景下,出现线程过多的问题。
3.2 数据异构
上面说到的用户信息查询接口需要调用用户查询接口、积分查询接口 和 成长值查询接口,然后汇总数据统一返回。
那么,我们能不能把数据冗余一下,把用户信息、积分和成长值的数据统一存储到一个地方,比如:redis,存的数据结构就是用户信息查询接口所需要的内容。然后通过用户id,直接从redis中查询数据出来,不就OK了?
如果在高并发的场景下,为了提升接口性能,远程接口调用大概率会被去掉,而改成保存冗余数据的数据异构方案。
但需要注意的是,如果使用了数据异构方案,就可能会出现数据一致性问题。
用户信息、积分和成长值有更新的话,大部分情况下,会先更新到数据库,然后同步到redis。但这种跨库的操作,可能会导致两边数据不一致的情况产生。
4. 重复调用
重复调用
在我们的日常工作代码中可以说随处可见,但如果没有控制好,会非常影响接口的性能。
不信,我们一起看看。
4.1 循环查数据库
有时候,我们需要从指定的用户集合中,查询出有哪些是在数据库中已经存在的。
实现代码可以这样写:
public List<User> queryUser(List<User> searchList) {if (CollectionUtils.isEmpty(searchList)) {return Collections.emptyList();}List<User> result = Lists.newArrayList();searchList.forEach(user -> result.add(userMapper.getUserById(user.getId())));return result;
}
这里如果有50个用户,则需要循环50次,去查询数据库。我们都知道,每查询一次数据库,就是一次远程调用。
如果查询50次数据库,就有50次远程调用,这是非常耗时的操作。
那么,我们如何优化呢?
具体代码如下:
public List<User> queryUser(List<User> searchList) {if (CollectionUtils.isEmpty(searchList)) {return Collections.emptyList();}List<Long> ids = searchList.stream().map(User::getId).collect(Collectors.toList());return userMapper.getUserByIds(ids);
}
提供一个根据用户id集合批量查询用户的接口,只远程调用一次,就能查询出所有的数据。
这里有个需要注意的地方是:id集合的大小要做限制,最好一次不要请求太多的数据。要根据实际情况而定,建议控制每次请求的记录条数在500以内。
4.2 死循环
有些小伙伴看到这个标题,可能会感到有点意外,死循环也算?
代码中不是应该避免死循环吗?为啥还是会产生死循环?
有时候死循环是我们自己写的,例如下面这段代码:
while(true) {if(condition) {break;}System.out.println("do samething");
}
这里使用了while(true)的循环调用,这种写法在CAS自旋锁
中使用比较多。
当满足condition等于true的时候,则自动退出该循环。
如果condition条件非常复杂,一旦出现判断不正确,或者少写了一些逻辑判断,就可能在某些场景下出现死循环的问题。
出现死循环,大概率是开发人员人为的bug导致的,不过这种情况很容易被测出来。
还有一种隐藏的比较深的死循环,是由于代码写的不太严谨导致的。如果用正常数据,可能测不出问题,但一旦出现异常数据,就会立即出现死循环。
4.3 无限递归
如果想要打印某个分类的所有父分类,可以用类似这样的递归方法实现:
public void printCategory(Category category) {if(category == null || category.getParentId() == null) {return;} System.out.println("父分类名称:"+ category.getName());Category parent = categoryMapper.getCategoryById(category.getParentId());printCategory(parent);
}
正常情况下,这段代码是没有问题的。
但如果某次有人误操作,把某个分类的parentId指向了它自己,这样就会出现无限递归的情况。导致接口一直不能返回数据,最终会发生堆栈溢出。
建议写递归方法时,设定一个递归的深度,比如:分类最大等级有4级,则深度可以设置为4。然后在递归方法中做判断,如果深度大于4时,则自动返回,这样就能避免无限循环的情况。
5. 异步处理
有时候,我们接口性能优化,需要重新梳理一下业务逻辑,看看是否有设计上不太合理的地方。
比如有个用户请求接口中,需要做业务操作,发站内通知,和记录操作日志。为了实现起来比较方便,通常我们会将这些逻辑放在接口中同步执行,势必会对接口性能造成一定的影响。
接口内部流程图如下:
这个接口表面上看起来没有问题,但如果你仔细梳理一下业务逻辑,会发现只有业务操作才是核心逻辑
,其他的功能都是非核心逻辑
。
在这里有个原则就是:核心逻辑可以同步执行,同步写库。非核心逻辑,可以异步执行,异步写库。
上面这个例子中,发站内通知和用户操作日志功能,对实时性要求不高,即使晚点写库,用户无非是晚点收到站内通知,或者运营晚点看到用户操作日志,对业务影响不大,所以完全可以异步处理。
通常异步主要有两种:多线程
和 mq
。
5.1 线程池
使用线程池
改造之后,接口逻辑如下:
发站内通知和用户操作日志功能,被提交到了两个单独的线程池中。
这样接口中重点关注的是业务操作,把其他的逻辑交给线程异步执行,这样改造之后,让接口性能瞬间提升了。
但使用线程池有个小问题就是:如果服务器重启了,或者是需要被执行的功能出现异常了,无法重试,会丢数据。
那么这个问题该怎么办呢?
5.2 mq
使用mq
改造之后,接口逻辑如下:
对于发站内通知和用户操作日志功能,在接口中并没真正实现,它只发送了mq消息到mq服务器。然后由mq消费者消费消息时,才真正的执行这两个功能。
这样改造之后,接口性能同样提升了,因为发送mq消息速度是很快的,我们只需关注业务操作的代码即可。
6. 避免大事务
很多小伙伴在使用spring框架开发项目时,为了方便,喜欢使用@Transactional
注解提供事务功能。
没错,使用@Transactional注解这种声明式事务的方式提供事务功能,确实能少写很多代码,提升开发效率。
但也容易造成大事务,引发其他的问题。
下面用一张图看看大事务引发的问题。
从图中能够看出,大事务问题可能会造成接口超时,对接口的性能有直接的影响。
我们该如何优化大事务呢?
-
少用@Transactional注解
-
将查询(select)方法放到事务外
-
事务中避免远程调用
-
事务中避免一次性处理太多数据
-
有些功能可以非事务执行
-
有些功能可以异步处理
——EOF——
福利:
扫码回复【酒店】可免费领取酒店管理系统源码
相关文章:

11 个接口性能优化技巧(上)【送源码】
接口性能优化对于从事后端开发的同学来说,肯定再熟悉不过了,因为它是一个跟开发语言无关的公共问题。 该问题说简单也简单,说复杂也复杂。 有时候,只需加个索引就能解决问题。 有时候,需要做代码重构。 有时候&…...

AIoTedge 智能边缘物联网平台
AIoTedge智能边缘物联网平台是一个创新的边云协同架构,它为智能设备和系统提供了强大的数据处理和智能决策能力。这个平台的核心优势在于其边云协同架构设计,它优化了数据处理速度,提高了系统的可靠性和灵活性,适用于多种场景&…...

深入理解CSS基础【代码审计实战指南】
文章目录 为什么需要cssCSS语法CSS的组成css注释: 快速入门示例:常用样式字体颜色和边框颜色介绍颜色示例:边框边框的宽度与高度 字体样式背景样式文本居中 字体颜色和边框颜色介绍颜色示例:边框边框的宽度与高度 字体样式背景样式…...

html改写vue日志
本人最近学了vue,想着练手的方法就是改写之前在公司开发的小系统前端,将前端的AJAXJSThymeleaf改为axiosvue。 改写html 将<html>中的<head>和<body>结构移除,将css部分移入<style>, 重新定义了全局的&…...

Transformer-Bert---散装知识点---mlm,nsp
本文记录的是笔者在了解了transformer结构后嗑bert中记录的一些散装知识点,有时间就会整理收录,希望最后能把transformer一个系列都完整的更新进去。 1.自监督学习 bert与原始的transformer不同,bert是使用大量无标签的数据进行预训…...
基于术语词典干预的机器翻译挑战赛笔记 Task3 #Datawhale AI 夏令营
书接上回,上回在这捏: 基于术语词典干预的机器翻译挑战赛笔记Task2 #Datawhale AI 夏令营-CSDN博客文章浏览阅读223次,点赞10次,收藏5次。基于术语词典干预的机器翻译挑战赛笔记Task2https://blog.csdn.net/qq_23311271/article/…...

定制QCustomPlot 带有ListView的QCustomPlot 全网唯一份
定制QCustomPlot 带有ListView的QCustomPlot 文章目录 定制QCustomPlot 带有ListView的QCustomPlot摘要需求描述实现关键字: Qt、 QCustomPlot、 魔改、 定制、 控件 摘要 先上效果,是你想要的,再看下面的分解,顺便点赞搜藏一下;不是直接右上角。 QCustomPlot是一款…...

Fast Planner规划算法(一)—— Fast Planner前端
本系列文章用于回顾学习记录Fast-Planner规划算法的相关内容,【本系列博客写于2023年9月,共包含四篇文章,现在进行补发第一篇,其余几篇文章将在近期补发】 一、Fast Planner前端 Fast Planner的轨迹规划部分一共分为三个模块&…...

问题记录-SpringBoot 2.7.2 整合 Swagger 报错
详细报错如下 报错背景,我将springboot从2.3.3升级到了2.7.2,报了下面的错误: org.springframework.context.ApplicationContextException: Failed to start bean documentationPluginsBootstrapper; nested exception is java.lang.NullPo…...

【视觉SLAM】 十四讲ch5习题
1.*寻找一个相机(你手机或笔记本的摄像头即可),标定它的内参。你可能会用到标定板,或者自己打印一张标定用的棋盘格。 参考我之前写过的这篇博客:【OpenCV】 相机标定 calibrateCamera Code来源是《学习OpenCV3》18.…...
Webpack基础学习-Day01
Webpack基础学习-Day01 1.1 webpack 是什么 webpack 是一种前端资源构建工具,一个静态模块打包器(module bundler)。 在 webpack 看来, 前端的所有资源文件(js/json/css/img/less/…)都会作为模块处理。 它将根据模块的依赖关系进行静态分析,打包生成…...

如何防止热插拔烧坏单片机
大家都知道一般USB接口属于热插拔,实际任意带电进行连接的操作都可以属于热插拔。我们前面讲过芯片烧坏的原理,那么热插拔就是导致芯片烧坏的一个主要原因之一。 在电子产品的整个装配过程、以及产品使用过程经常会面临接口热插拔或者类似热插拔的过程。…...

JQuery+HTML+JavaScript:实现地图位置选取和地址模糊查询
本文详细讲解了如何使用 JQueryHTMLJavaScript 实现移动端页面中的地图位置选取功能。本文逐步展示了如何构建基本的地图页面,如何通过点击地图获取经纬度和地理信息,以及如何实现模糊查询地址并在地图上标注。最后,提供了完整的代码示例&…...
ArcGIS Pro SDK (九)几何 13 多部件
ArcGIS Pro SDK (九)几何 13 多部件 文章目录 ArcGIS Pro SDK (九)几何 13 多部件1 获取多部分要素的各个部分2 获取多边形的最外层环 环境:Visual Studio 2022 .NET6 ArcGIS Pro SDK 3.0 1 获取多部分要素的各个部分…...

【Node】npm i --legacy-peer-deps,解决依赖冲突问题
文章目录 🍖 前言🎶 一、问题描述✨二、代码展示🏀三、运行结果🏆四、知识点提示 🍖 前言 npm i --legacy-peer-deps,解决依赖冲突问题 🎶 一、问题描述 node执行安装指令时出现报错ÿ…...

h5点击电话号跳转手机拨号
需要使用到h5的 <a>标签 我们首先在<head>标签中添加代码 <meta name"format-detection" content"telephoneyes"/>然后再想要的位置添加代码 <a href"tel:10086"> 点击拨打:10086 </a> 这样功能就实现…...

从数据湖到湖仓一体:统一数据架构演进之路
文章目录 一、前言二、什么是湖仓一体?起源概述 三、为什么要构建湖仓一体?1. 成本角度2. 技术角度 四、湖仓一体实践过程阶段一:摸索阶段(仓、湖并行建设)阶段二:发展阶段方式一、湖上建仓(湖在下、仓在上)方式二:仓外…...
Electron 渲染进程直接调用主进程的API库@electron/remote引用讲解
背景 remote是个老库,早期Electron版本中有个remote对象,这个对象可以横跨所有进程,随意通信,后来官方认为不安全,被干掉了,之后有人利用Electron的IPC通信,底层通过Promise的await能力&#x…...
在python中使用正则表达式
正则表达式是什么?就是要寻找的数据的规律,使用正则表达式的步骤有三 第一,寻找规律,第二使用正则符号表示规律,第三,提取信息 看下面的代码 import re wenzhang (小草偷偷地从土里钻出来,嫩…...

华清数据结构day4 24-7-19
链表的相关操作 linklist.h #ifndef LINKLIST_H #define LINKLIST_H #include <myhead.h> typedef int datatype; typedef struct Node {union{int len;datatype data;};struct Node *next; } Node, *NodePtr;NodePtr list_create(); NodePtr apply_node(datatype e); …...

AI Agent与Agentic AI:原理、应用、挑战与未来展望
文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例:使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例:使用OpenAI GPT-3进…...
DockerHub与私有镜像仓库在容器化中的应用与管理
哈喽,大家好,我是左手python! Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库,用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...

【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力
引言: 在人工智能快速发展的浪潮中,快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型(LLM)。该模型代表着该领域的重大突破,通过独特方式融合思考与非思考…...

ServerTrust 并非唯一
NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...

AI书签管理工具开发全记录(十九):嵌入资源处理
1.前言 📝 在上一篇文章中,我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源,方便后续将资源打包到一个可执行文件中。 2.embed介绍 🎯 Go 1.16 引入了革命性的 embed 包,彻底改变了静态资源管理的…...

学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”
2025年#高考 将在近日拉开帷幕,#AI 监考一度冲上热搜。当AI深度融入高考,#时间同步 不再是辅助功能,而是决定AI监考系统成败的“生命线”。 AI亮相2025高考,40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕,江西、…...

【分享】推荐一些办公小工具
1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由:大部分的转换软件需要收费,要么功能不齐全,而开会员又用不了几次浪费钱,借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...

android13 app的触摸问题定位分析流程
一、知识点 一般来说,触摸问题都是app层面出问题,我们可以在ViewRootImpl.java添加log的方式定位;如果是touchableRegion的计算问题,就会相对比较麻烦了,需要通过adb shell dumpsys input > input.log指令,且通过打印堆栈的方式,逐步定位问题,并找到修改方案。 问题…...

Chrome 浏览器前端与客户端双向通信实战
Chrome 前端(即页面 JS / Web UI)与客户端(C 后端)的交互机制,是 Chromium 架构中非常核心的一环。下面我将按常见场景,从通道、流程、技术栈几个角度做一套完整的分析,特别适合你这种在分析和改…...

什么是VR全景技术
VR全景技术,全称为虚拟现实全景技术,是通过计算机图像模拟生成三维空间中的虚拟世界,使用户能够在该虚拟世界中进行全方位、无死角的观察和交互的技术。VR全景技术模拟人在真实空间中的视觉体验,结合图文、3D、音视频等多媒体元素…...