性能优化 - 案例篇:11种优化接口性能的通用方案
文章目录
- Pre
- 1. 加索引:最低成本,最大收益
- 常见问题:
- 工具命令:
- 建议:
- 2. SQL 优化:比加索引再进阶一步
- 常见 5 类问题:
- 实用建议:
- 3. 远程调用:从串行改并行,性能翻倍不止
- 场景复现:
- 优化:并行调用
- 建议:
- 4. 数据异构:跨服务请求太慢?同步一份!
- 缺点:
- 5. 避免重复调用
- 案例 1:循环查数据库
- 优化写法
- 案例 2:死循环自旋
- 6. 异步处理
- 什么情况适合异步?
- 推荐工具:
- 注意:
- 7. 锁粒度控制:避免全表级锁
- 优化方式:
- 8. 分页优化
- 大偏移问题:
- 推荐:
- 9. 加缓存:请求再多,读缓存照样稳
- 场景:
- 推荐策略:
- 注意:
- 10. 分库分表
- 适用场景:
- 常见方案:
- 注意:
- 11. 辅助功能控制:接口不是万金油
- 总结:性能优化是一门系统工程
Pre
性能优化 - 理论篇:常见指标及切入点
性能优化 - 理论篇:性能优化的七类技术手段
性能优化 - 理论篇:CPU、内存、I/O诊断手段
性能优化 - 工具篇:常用的性能测试工具
性能优化 - 工具篇:基准测试 JMH
性能优化 - 案例篇:缓冲区
性能优化 - 案例篇:缓存
性能优化 - 案例篇:数据一致性
性能优化 - 案例篇:池化对象_Commons Pool 2.0通用对象池框架
性能优化 - 案例篇:大对象的优化
性能优化 - 案例篇:使用设计模式优化性能
性能优化 - 案例篇:并行计算
性能优化 - 案例篇:多线程锁的优化
性能优化 - 案例篇:CAS、乐观锁、分布式锁和无锁
性能优化 - 案例篇: 详解 BIO NIO AIO
性能优化 - 案例篇: 19 条常见的 Java 代码优化法则
性能优化 - 案例篇:JVM垃圾回收器
性能优化 - 案例篇:JIT
性能优化不是一蹴而就的,它是一次“认知 + 实践”的双向奔赴。
你是不是也遇到过这样的场景:
- 某核心接口,TPS(每秒事务数)一上来就拉垮;
- 一个无害的查询,突然把数据库搞宕了;
- 接口明明逻辑简单,但响应就是卡成 PPT。
接下来我将总结出的 11 条接口性能优化法则,其中不少优化点只需几行代码,就能把接口响应从 几秒 → 毫秒。
1. 加索引:最低成本,最大收益
有没有用索引?用了索引有没有生效?MySQL 选对了索引吗?
这三个问题必须要会回答。
常见问题:
WHERE
条件没加索引;ORDER BY
字段无索引或顺序不匹配;- 字段上存在函数、类型不一致导致索引失效;
- 多个索引冲突,MySQL 选错了路径。
工具命令:
SHOW INDEX FROM `table_name`;
EXPLAIN SELECT * FROM `table_name` WHERE cloumn= 1;
建议:
- 别忘了联合索引的最左前缀原则;
- 必要时用
FORCE INDEX
; - 注意:添加、删除索引都要评估业务负载,避免高峰期操作。
2. SQL 优化:比加索引再进阶一步
索引优化完了,执行慢?那就看 SQL 本身。
常见 5 类问题:
- SELECT *(无脑全查)
- IN 太长或嵌套子查询
- 关联字段未加索引
- 过多 JOIN + 复杂嵌套
- 分页偏移过大(OFFSET)
实用建议:
- 明确列名,不要
SELECT *
; - 拆子查询,用临时表换 IN;
- 使用
LIMIT
时避免大偏移(“延迟分页”); - COUNT(*) 太大可用缓存兜底。
3. 远程调用:从串行改并行,性能翻倍不止
场景复现:
某接口需要:
- 用户服务:昵称、头像、等级
- 积分服务:积分
- 成长值服务:成长值
如果这样写:
A → 用户服务(200ms)
A → 积分服务(150ms)
A → 成长值服务(180ms)
总耗时:530ms
优化:并行调用
CompletableFuture.allOf(userFuture, bonusFuture, growthFuture
).join();
三路并发,总耗时 = 最长的一次,约 200ms。
建议:
- 使用自定义线程池,避免高并发下线程爆炸;
- 注意业务合并时机和异常兜底。
4. 数据异构:跨服务请求太慢?同步一份!
高并发接口建议使用缓存异构数据,减少远程依赖。
- 用户信息 Redis 缓存结构:
{"userId": 123,"nickname": "Artisan","score": 87,"growth": 42
}
-
更新策略:
- 数据变更后同步更新 Redis;
- 可选:异步 MQ 通知,或双写机制。
缺点:
- 一致性需关注;
- 数据过期和失效策略要设计清晰。
5. 避免重复调用
案例 1:循环查数据库
// 低效写法
for (User u : list) {userMapper.getUserById(u.getId());
}
优化写法
List<Long> ids = list.stream().map(User::getId).collect(toList());
userMapper.getUserByIds(ids);
减少 N 次 DB 请求 → 只要 1 次。
案例 2:死循环自旋
while (true) {if (flag) break;
}
- 慎用!在并发条件不明的场景下容易让 CPU 打满;
- 推荐用
LockSupport.park()
或CountDownLatch
等并发工具类。
6. 异步处理
什么情况适合异步?
- 写操作完成后通知其他服务;
- 接口响应不需要等待的操作。
推荐工具:
@Async
CompletableFuture
- 消息队列(RocketMQ/Kafka)
注意:
- 异步任务一定要兜底(失败重试、幂等保障);
- 核心链路要保障同步结果,不建议异步。
7. 锁粒度控制:避免全表级锁
比如订单取消逻辑:
synchronized(lock) {cancelOrder();
}
这种是“粗粒度锁”。
优化方式:
- 基于订单号加锁(细粒度)
- Redis 分布式锁(Redisson)
- 乐观锁:加版本号
version
8. 分页优化
分页是最常见的接口之一,但最容易踩坑。
大偏移问题:
SELECT * FROM order LIMIT 100000, 20;
扫描 10w 行,慢得要命!
推荐:
- 延迟分页:记录上次最大 id
- 游标分页:基于上次的主键分页
SELECT * FROM order WHERE id > lastId ORDER BY id LIMIT 20;
9. 加缓存:请求再多,读缓存照样稳
场景:
- 热门xx详情
- 用户画像、配置数据
- 访问频率高但更新低的数据
推荐策略:
- Redis String/Hash
- 结合 Guava Cache 做本地一级缓存
- 使用 CacheAside 模式:先查缓存 → 缓存失效再查库
注意:
- 缓存过期 + 主动刷新;
- 防止缓存雪崩/穿透/击穿(布隆过滤器 + 限流)。
10. 分库分表
适用场景:
- 单表数据超千万;
- 读写压力过大。
常见方案:
- 垂直拆分:按业务分库
- 水平拆分:按 ID 取模、按时间分表
- 中间件:ShardingSphere、MyCat
注意:
- JOIN 语句需要格外处理;
- 跨分片分页/排序要小心。
11. 辅助功能控制:接口不是万金油
很多接口包含了不必要的逻辑,尤其是在企业后台管理系统中:
- 复杂日志打印;
- 权限、数据范围过滤;
- 多租户、国际化处理;
- 无用字段/参数传递。
建议:
- 精简接口:核心功能优先返回;
- 将辅助功能放到独立服务或中间件;
- 用 AOP + 过滤器统一处理非业务逻辑。
总结:性能优化是一门系统工程
接口优化,不是追求“代码完美”,而是要用尽可能少的改动,达到可接受的性能表现。
提到的这 11 点优化手段,从数据库 → 接口逻辑 → 缓存中间件 → 分布式架构全面覆盖,几乎涵盖了主流系统中所有性能问题的“对症下药”。