接口性能优化思路

目录

前言

优化思路

缓存

异步

合并

拆分

单例

压缩


前言

日常开发中设计接口,响应时间是衡量一个接口质量的重要指标。

接口响应时间这里粗糙地分为三种:

  1. 即时响应:毫秒级,小于500毫秒
  2. 快速响应:秒级,大于500毫秒且小于2秒
  3. 长时间操作:大于2秒,甚至是分钟级别的操作

一般接口都是需要快速响应的,在不考虑任何优化策略的情况下,如果整个业务逻辑走下来,响应时间大于了2秒,那么就应该考虑对这个接口进行性能优化了,以免影响了用户的体验。

优化思路

缓存

毫无疑问,使用缓存是对性能提升最明显的方法。

若是缓存中存在,直接返回结果,否则再走接口逻辑,最后将结果放入缓存中,以便下次查询时使用。

缓存还可以分为本地缓存和分布式缓存

本地缓存:

由于数据是在本地内存,它的访问速度是最快的,同时它的容量也受限于运行本地内存,以及在分布式系统中,本地缓存的数据很可能和其他节点的数据不一致。所以本地缓存一般用来存储一些系统级别上基本上不会改变的内容。

在 Java 实现中,最简单的本地缓存其实可以用一个 ConcurrentHashMap来实现,不过在实际开发中有更好的选择,可以选择 Caffeine来做本地缓存,性能更好。

分布式缓存:

分布式缓存相对本地缓存,多了一次网络交互,所以速度会慢一些,但是大小则不会受制于本地服务机器内存了,内存不够用了还可以通过水平扩容来解决,而且在分布式系统中, 数据的一致性能够得以保证。

常用的分布式缓存为 RedisMemCached,MemCached 更加轻量级一些,纯粹的缓存中间件,Redis 不仅仅可以作为缓存,它还支持更多的功能(分布式锁,分布式限流,地理位置应用,布隆过滤器等等)

异步

如果是单线程执行业务逻辑,那么可以考虑对业务逻辑进行拆分,将其中能够并行执行的部分分解出来,然后使用多线程的方式去同时运行,理论上能够将该部分的运行性能提升(提升的大小取决于能够同时运行的线程数)

Java 中可以使用线程池 ThreadPool来并发执行任务,进阶一点还可以使用 CompletableFuture来编排异步任务。

合并

合并其实是指将批量操作,将多个操作合并成一个去执行

例如数据库中的批量插入,同一个表的多条插入语句,其实可以优化成一个插入语句,这样可以减少数据库的交互,避免重复地创建数据库连接。Mybatis 中的 BatchExecutor就是这个思路,将相同的 sql 语句添加到同一个 Satement 对象中等待执行,可以有效地减少 PrepareStatement 地编译操作

Redis 的 Pipeline也是将多个请求合并,最后一起发送,这样可以将多次网络交互优化成一次网络交互,减少网络交互的时间,从而提升性能。

拆分

拆分其实是针对多线程编程中,对共享资源的一个拆分,避免因为竞争激烈,导致多线程并发执行性能反而比单线程还慢了

代码中如果使用到了锁,可以从两方面考虑

减少锁的持有时间:将不必要的操作尽可能地放到锁外面去执行,避免其他线程等待锁的释放时间过长

减少锁的粒度:参考JUC下并发类的设计

ConcurrentHashMap是一个线程安全的Map,虽然可以直接通过synchronized修饰 put 和 get 方法来得到一个线程安全的HashMap,但是这样显然十分影响性能。

在JDK 1.7中,ConcurrentHashMap采用了**分段锁(Segment Lock)**的机制来提高并发性能。这种设计将整个哈希表分割成多个段(Segment),每个段都维护着自己独立的锁。这样,当多个线程并发访问ConcurrentHashMap时,它们可以并行地访问不同的段,从而减少了锁的竞争。

在JDK 1.8中,ConcurrentHashMap的锁机制发生了重大变化,它放弃了JDK 1.7中的分段锁设计,转而采用了一种基于CAS(Compare-And-Swap)操作+synchronized锁的细粒度锁机制。

除了ConcurrentHashMap,在 JUC 包下还有许多 Adder 类(IntegerAdderLongAdder等等),当线程竞争不激烈时,直接采用CAS来实现数量的原子递增,如果竞争激励,则使用数组来维护元素个数(将单个资源的竞争拆分成多个),先从数组中随机选择一个,再通过CAS实现原子递增,最后再一起汇总。

单例

单例其实也可以看作是缓存的一种实现方式,本质是避免重复创建对象,直接服用现有的对象,从而减少重复创建对象的时间,提升性能。

在 spring 框架下开发,依赖注入默认就是单例的

压缩

接口的响应时间除了接口本身执行业务逻辑的时间,还有网络传输的时间。在其他条件都不变的情况下,减少网络传输内容的大小,也可以提升接口的性能。

如果接口的返回数据字段很多,可以考虑压缩字段的大小,比如说将返回的json中的有实际意思的单词字段名直接改成简单的f1,f2,f3;这样在返回数据量大的情况下也可以减少网络传输的内容大小。

相关推荐
不会c+1 分钟前
Spring详解(二)IOC控制反转
java·开发语言
ujainu3 分钟前
Flutter + OpenHarmony 垂直列表:ListView 组件在手机上的性能优化实践
flutter·智能手机·性能优化
小锋学长生活大爆炸6 分钟前
【工具】免费的文本读写API
java·数据库·mysql
csgo打的菜又爱玩14 分钟前
数仓整体架构和建模架构
java·大数据·开发语言·架构
翱翔的苍鹰16 分钟前
多Agent智能体系统设计思路
java·python·深度学习·神经网络·机器学习·tensorflow
小花17 分钟前
java后端框架之spring
java·开发语言·spring
小王不爱笑13219 分钟前
Spring Boot 配置文件核心用法与加载优先级
java·spring boot·后端
小北方城市网25 分钟前
Spring Cloud Gateway 动态路由进阶:基于 Nacos 配置中心的热更新与版本管理
java·前端·javascript·网络·spring boot·后端·spring
小北方城市网26 分钟前
Spring Cloud Gateway 生产问题排查与性能调优全攻略
redis·分布式·缓存·性能优化·mybatis
橙露28 分钟前
Docker 容器化运维:镜像优化、容器编排与持久化存储方案
java·运维·docker