
之前我们团队有个项目,上线初期 RT(响应时间)是 30ms 左右,QPS 能到 1000。但随着业务功能越来越多,RT 一路飙到 120ms,甚至有几次直接超时。当时大家以为是数据库出了问题,结果排查下来,发现其实很多问题都藏在了代码里。
说白了,SpringBoot 项目“越写越慢”不是因为框架本身有问题,而是我们在开发过程中忽略了一些细节。比如线程池配置、缓存使用、数据库查询方式、GC 策略等等。这些问题慢慢积累,最终导致系统整体性能下降。
记得有一次线上故障,系统 CPU 负载飙到 90%+,而数据库连接池几乎被打满。那时候我们用的是默认的 HikariCP 配置,没有设置 maxPoolSize,结果大量请求堆积在连接池中,导致整个服务卡顿。
错误做法:默认配置不动
spring:
datasource:
hikari:
maxPoolSize: 10 # 默认值,非常容易被打满正确做法:依据 QPS 设置连接池大小
spring:
datasource:
hikari:
minimum-idle: 10
maximum-pool-size: 50
idle-timeout: 30000
max-lifetime: 1800000
connection-timeout: 2000有一次我优化一个接口,发现每次请求都要做大量的 DTO 转换,从 Request 到 VO 再到 Entity,中间用了多个工具类。虽然看起来没什么问题,但实际测试的时候,发现这个转换过程占了整个请求的 20% 左右。
错误做法:频繁使用 BeanUtils
UserVO vo = new UserVO();
BeanUtils.copyProperties(entity, vo);正确做法:使用 MapStruct,速度快 10–15 倍
@Mapper
public interface UserMapper {
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
UserVO toVO(UserEntity entity);
}调用:
UserVO vo = UserMapper.INSTANCE.toVO(entity);之前有一个订单服务,里面逻辑特别复杂,各种 if-else,还有嵌套循环。每次处理一个订单,都要走几十步操作,结果导致接口 RT 直接翻倍。
错误做法:超大方法
public void processOrder(Order order) {
// 几百行代码...
}正确做法:职责拆分 + 策略模式
public interface PayStrategy {
void pay(Order order);
}@Service("wechatPay")
public class WechatPayStrategy implements PayStrategy {
public void pay(Order order) { ... }
}优雅调用:
payStrategyMap.get(order.getPayType()).pay(order);有一次我们发现一个查询接口 RT 超过 100ms,但 SQL 执行却只有 5ms。后来发现是 MyBatis 没有正确使用缓存,导致每次请求都重新查数据库。
开启 MyBatis 二级缓存:
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>Mapper 中:
<cache eviction="LRU" flushInterval="60000" size="512"/>另一个典型坑:N+1 查询问题
错误写法:
List<User> users = userMapper.list();
for (User u : users) {
u.setOrders(orderMapper.getByUserId(u.getId()));
}正确写法:一次 JOIN 查完。
SELECT u.*, o.*
FROM user u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.status = 1;有一次我们发现 Redis 的命中率不到 60%,后来发现是 key 的 TTL 过短、高频数据没预热等导致的。
检查慢查询:
redis-cli SLOWLOG GET 100检查 BigKey:
redis-cli MEMORY USAGE user:info:123缓存预热示例:
@PostConstruct
public void warmUp() {
List<User> hotUsers = userMapper.listHot();
hotUsers.forEach(u -> redisTemplate.opsForValue()
.set("user:" + u.getId(), u, Duration.ofHours(12)));
}我们有个服务 Full GC 每分钟一次,导致系统抖动严重。
后来调整 JVM:
-Xms4g
-Xmx4g
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-Xlog:gc*:file=./logs/gc.log:time设置 Xms = Xmx 能避免堆动态扩容带来的 STW。
我们团队后来做了个简单的性能监控方案,包括以下几个部分:
/actuator/metrics 接口获取系统指标;有了这套体系后,我们能更快地发现性能异常,而不是等到线上出问题才去排查。
有一次我们优化了一个用户查询接口,原本 RT 是 120ms,经过一系列调整后,最终稳定在 30ms 以内。
具体做了以下几件事:
整个过程花了大概一周时间,但效果非常明显,用户反馈也好了很多。
说实话,性能调优不是一蹴而就的事情,它需要你不断观察、分析、实验。很多时候你以为自己已经做得很好了,但实际运行中还是会有隐藏的问题。别怕犯错,别怕调试,多写点日志,多看点 GC 日志,多用点工具。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。