接口性能优化思路

目录

前言

优化思路

缓存

异步

合并

拆分

单例

压缩


前言

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

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

  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;这样在返回数据量大的情况下也可以减少网络传输的内容大小。

相关推荐
WaaTong4 分钟前
《重学Java设计模式》之 原型模式
java·设计模式·原型模式
m0_743048444 分钟前
初识Java EE和Spring Boot
java·java-ee
AskHarries6 分钟前
Java字节码增强库ByteBuddy
java·后端
小灰灰__26 分钟前
IDEA加载通义灵码插件及使用指南
java·ide·intellij-idea
夜雨翦春韭30 分钟前
Java中的动态代理
java·开发语言·aop·动态代理
程序媛小果1 小时前
基于java+SpringBoot+Vue的宠物咖啡馆平台设计与实现
java·vue.js·spring boot
追风林1 小时前
mac m1 docker本地部署canal 监听mysql的binglog日志
java·docker·mac
芒果披萨1 小时前
El表达式和JSTL
java·el
duration~2 小时前
Maven随笔
java·maven
zmgst2 小时前
canal1.1.7使用canal-adapter进行mysql同步数据
java·数据库·mysql