前言
国内项目普遍出现工期紧、工作量大、多人协作开发编码习惯不同,能力也参差不齐等多种因素,导致接口在上线后性能不满足预期。
本文从接口性能需求分析、接口性能准出标准、接口性能常见问题以及性能优化策略等多个方面,系统地完成接口性能优化的工作。
同时在项目交付过程中,可以通过 Code Review 、 技术债务等方式提前进行一部分接口性能的识别和优化,因为越复杂的项目拖到最后调整的代价越大。
接口性能需求分析流程
分析要点:
- 列举系统中核心场景,梳理出 API 接口清单,询问外部系统是否支持压测,防止将其它系统压崩
- 结合系统用户数、请求量、现有数据量以及未来几年的增长量,确定接口的性能指标,比如:TPS 等
- 准备预生产环境,进行 API 接口的压测
- 统计接口的 TPS、响应时间、CPU 占有率、内存占有率、接口错误率等数据指标
- 筛选不达标的接口,通过 APM 应用性能工具和调用链路查看耗时情况
- 根据具体情况进行 API 接口优化
接口性能的准出标准
以一个中等复杂软件系统为样例的接口性能准出标准:
接口性能优化方案
接口性能常见问题
代码结构不合理
- 循环调用数据库或者外部系统,导致耗时长
- 查询复杂单据详情时,不考虑实际情况,一股脑的把所有关联的数据查询出来
- 一次性加载巨量数据到内存中进行处理,耗时长或者 OOM 内存溢出
- 接口返回大量数据,接口处理时间长,网络传输时间长,前端页面处理数据时间长
- 执行数据库保存时,经常使用默认的 saveAll,导致一条条插入到数据,耗时长
- 大量非必要、不阻塞主流程的业务逻辑写在一起导致长事务、数据一致性等问题
- 不阻塞主流程或着可延迟处理的外部系统调用,都使用同步调用的方式
SQL 编写不合理
- SQL 执行语句没有新建对应的索引
- SQL 执行语句包含隐式类型转换、最左匹配失效、使用函数或表达式等导致慢 SQL
- SQL join 了超过了三张及以上表
- 小表连大表,left join 无索引,导致全量扫描大表情况
- 深分页问题
- order by 走文件排序
- group by 使用了临时表
- 海量数据时count很慢,因 MySQL InnoDB count会遍历所有数据,计数准确但是性能较低
其他
- 请求高频、数据变更频率低,比如:基础数据、权限数据等,高并发下延迟大,可考虑使用缓存
- MySQL 数据库并发顶不住的时候,比如:点赞等,可考虑使用 Redis 等先缓存扛着后同步到 MySQL
- ......
性能优化策略
1️⃣ 配置优化
- JVM 参数
- 数据库连接池参数
- 硬件参数
2️⃣ 代码优化
- 减少外部资源重复调用
- 按需查询所关联的数据资源
- 批量操作数据库
3️⃣ 池化技术
- 使用线程池处理业务逻辑
- 使用对象池复用已有对象
- HTTP 连接池访问外部系统
4️⃣ 数据库优化
- 使用索引 、 现有索引优化
- SQL 编写不合理,避免太多表 Join、临时表、全表扫描
- 避免大事务,减少死锁
- 锁力度避免过粗,减少锁占用超时
- 深分页问题:1)通过ID记录标签 2)先深分页查询id,再inner join
5️⃣ 异步思想
- 耗时操作,考虑异步执行
- 同步远程调用由同步改为异步
- 非阻塞主流程业务改为异步
6️⃣ 缓存策略
- Nginx 缓存
- Redis 缓存 / 分布式缓存
- 本地缓存
- 注意数据一致性、缓存穿透、缓存击穿、大 Key 等问题
7️⃣ 大数据量处理
- 接口返回的数据量过大:分层分级返回、分批次返回、分页返回、压缩数据、延迟加载等
- 大量数据加载到内存,导致 OOM,可考虑分批次处理
- 海量数据时,count 查询慢,建议和分页列表接口分离
- 千万级别数据,可考虑冷热数据分离、数据库表分区、分库分表等
- 海量数据处理,考虑使用 NoSQL
8️⃣ 可观测性 & 工具
- 日志平台
- 分布式追踪
- APM & 告警平台:CPU、内存监控
- 数据库:Explain语句(MySQL)
- 性能诊断工具:Jstack 查看进程信息
小结
API 接口优化是一个持续的过程,需要根据实际的业务需求和系统负载不断进行调整和优化。通过以上方法,可以显著提高API的性能、可用性和用户体验。