前言
性能优化是提升用户体验和降低资源成本的关键。Spring Boot 应用性能优化涉及 JVM 调优、数据库优化、缓存策略等多个方面。本文将介绍 Spring Boot 性能优化的完整方案。
JVM 调优
1. 内存配置
# 生产环境推荐配置
java -Xms4g -Xmx4g \
-XX:MetaspaceSize=256m \
-XX:MaxMetaspaceSize=512m \
-XX:+UseG1GC \
-XX:MaxGCPauseMillis=200 \
-XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=/var/log/heapdump.hprof \
-jar app.jar
2. GC 选择
# G1 GC(推荐)
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:G1HeapRegionSize=16m
-XX:InitiatingHeapOccupancyPercent=45
# ZGC(JDK 15+,低延迟)
-XX:+UseZGC
-XX:ZCollectionInterval=5
-XX:ZAllocationSpikeTolerance=5
# Parallel GC(高吞吐)
-XX:+UseParallelGC
-XX:ParallelGCThreads=8
-XX:MaxGCPauseMillis=100
3. 虚拟线程配置
# JDK 21+ 虚拟线程
spring:
threads:
virtual:
enabled: true
pool:
core-pool-size: 100
max-pool-size: 1000
keep-alive: 30s
4. 监控 GC
# 启用 GC 日志
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
-Xloggc:/var/log/gc.log
-XX:+UseGCLogFileRotation
-XX:NumberOfGCLogFiles=5
-XX:GCLogFileSize=10M
# 使用 GCViewer 分析
# https://github.com/chewiebug/GCViewer
数据库优化
1. 连接池配置
spring:
datasource:
hikari:
maximum-pool-size: 20 # 最大连接数
minimum-idle: 5 # 最小空闲
connection-timeout: 30000 # 连接超时
idle-timeout: 600000 # 空闲超时
max-lifetime: 1800000 # 最大生命周期
leak-detection-threshold: 60000 # 泄漏检测
2. SQL 优化
// ✅ 推荐 - 批量操作
@Service
public class UserService {
public void batchInsert(List<User> users) {
int batchSize = 100;
for (int i = 0; i < users.size(); i += batchSize) {
List<User> batch = users.subList(
i,
Math.min(i + batchSize, users.size())
);
userRepository.saveAll(batch);
}
}
}
// ❌ 不推荐 - 循环插入
@Service
public class UserService {
public void batchInsert(List<User> users) {
for (User user : users) {
userRepository.save(user); // N 次查询
}
}
}
3. 索引优化
-- 添加索引
CREATE INDEX idx_user_email ON user(email);
CREATE INDEX idx_user_status_created ON user(status, created_at);
-- 复合索引
CREATE INDEX idx_order_user_status ON orders(user_id, status);
-- 覆盖索引
CREATE INDEX idx_user_id_name ON user(id, name);
-- 查看慢查询
SHOW SLOW QUERIES;
EXPLAIN SELECT * FROM user WHERE email = 'test@example.com';
4. JPA 优化
// ✅ 推荐 - 使用@EntityGraph
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
@EntityGraph(attributePaths = {"orders"})
Optional<User> findById(Long id);
}
// ✅ 推荐 - 使用@Query
@Query("SELECT u FROM User u LEFT JOIN FETCH u.orders WHERE u.id = :id")
Optional<User> findByIdWithOrders(@Param("id") Long id);
// ❌ 不推荐 - N+1 问题
User user = userRepository.findById(id).get();
user.getOrders().size(); // 额外查询
缓存策略
1. 本地缓存
@Configuration
public class CacheConfig {
@Bean
public CacheManager cacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCaffeine(Caffeine.newBuilder()
.initialCapacity(100)
.maximumSize(10000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.recordStats()
);
return cacheManager;
}
}
2. Redis 缓存
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
private final RedisTemplate<String, Object> redisTemplate;
/**
* 缓存穿透防护
*/
public UserDTO findById(Long id) {
String key = "user:" + id;
// 从缓存获取
Object cached = redisTemplate.opsForValue().get(key);
if (cached != null) {
if (cached.equals("__NULL__")) {
return null; // 缓存空值
}
return (UserDTO) cached;
}
// 从数据库查询
User user = userRepository.findById(id).orElse(null);
if (user == null) {
// 缓存空值,防止穿透
redisTemplate.opsForValue().set(key, "__NULL__", 5, TimeUnit.MINUTES);
return null;
}
UserDTO dto = convertToDTO(user);
// 存入缓存
redisTemplate.opsForValue().set(key, dto, 30, TimeUnit.MINUTES);
return dto;
}
/**
* 缓存击穿防护
*/
public UserDTO findByIdWithLock(Long id) {
String key = "user:" + id;
Object cached = redisTemplate.opsForValue().get(key);
if (cached != null) {
return (UserDTO) cached;
}
// 分布式锁
RLock lock = redissonClient.getLock("lock:" + key);
try {
if (lock.tryLock(3, 10, TimeUnit.SECONDS)) {
// 双重检查
cached = redisTemplate.opsForValue().get(key);
if (cached != null) {
return (UserDTO) cached;
}
// 从数据库查询
User user = userRepository.findById(id).orElse(null);
if (user != null) {
UserDTO dto = convertToDTO(user);
redisTemplate.opsForValue().set(key, dto, 30, TimeUnit.MINUTES);
return dto;
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
return null;
}
}
3. 多级缓存
@Service
public class MultiLevelCacheService {
private final Cache localCache; // L1
private final RedisTemplate redis; // L2
private final UserRepository repo; // DB
public UserDTO findById(Long id) {
// L1 缓存
UserDTO cached = (UserDTO) localCache.getIfPresent(id);
if (cached != null) {
return cached;
}
// L2 缓存
String key = "user:" + id;
cached = (UserDTO) redis.opsForValue().get(key);
if (cached != null) {
localCache.put(id, cached); // 回写 L1
return cached;
}
// 数据库
User user = userRepository.findById(id).orElse(null);
if (user != null) {
UserDTO dto = convertToDTO(user);
localCache.put(id, dto);
redis.opsForValue().set(key, dto, 30, TimeUnit.MINUTES);
return dto;
}
return null;
}
}
异步处理
1. 异步任务
@Service
public class AsyncTaskService {
@Async
public CompletableFuture<Void> sendEmail(String to, String content) {
// 异步发送邮件
emailService.send(to, content);
return CompletableFuture.completedFuture(null);
}
@Async
public CompletableFuture<Void> sendSms(String phone, String message) {
// 异步发短信
smsService.send(phone, message);
return CompletableFuture.completedFuture(null);
}
/**
* 并行执行
*/
public CompletableFuture<Void> notifyUser(
String email,
String phone,
String content
) {
return CompletableFuture.allOf(
sendEmail(email, content),
sendSms(phone, content)
);
}
}
2. 消息队列
@Service
public class MessageQueueService {
private final RocketMQTemplate rocketMQTemplate;
/**
* 异步解耦
*/
public void createOrder(Order order) {
// 保存订单
orderRepository.save(order);
// 异步发送消息
rocketMQTemplate.sendAsync("order-created", order);
}
/**
* 削峰填谷
*/
@RocketMQMessageListener(
topic = "order-created",
consumerGroup = "order-processor"
)
public class OrderProcessor implements RocketMQListener<Order> {
@Override
public void onMessage(Order order) {
// 异步处理订单
processOrder(order);
}
}
}
接口优化
1. 分页查询
@GetMapping("/api/users")
public R<PageResult<UserDTO>> getUsers(
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int size
) {
Pageable pageable = PageRequest.of(page - 1, size);
Page<User> userPage = userRepository.findAll(pageable);
return R.ok(PageResult.of(
userPage.getContent().stream()
.map(this::convertToDTO)
.collect(Collectors.toList()),
userPage.getTotalElements(),
page,
size
));
}
2. 投影查询
// DTO 投影
public interface UserProjection {
Long getId();
String getUsername();
String getEmail();
}
// 使用
@GetMapping("/api/users/simple")
public List<UserProjection> getUsersSimple() {
return userRepository.findAllBy();
}
3. 压缩响应
server:
compression:
enabled: true
min-response-size: 1024
mime-types: application/json,text/html,text/css,application/javascript
监控分析
1. 性能监控
@Component
public class PerformanceMonitor {
private final MeterRegistry meterRegistry;
@Around("@annotation(org.springframework.web.bind.annotation.GetMapping)")
public Object monitor(ProceedingJoinPoint pjp) throws Throwable {
long startTime = System.currentTimeMillis();
try {
Object result = pjp.proceed();
long cost = System.currentTimeMillis() - startTime;
meterRegistry.timer("api.response.time",
"method", pjp.getSignature().getName()
).record(cost, TimeUnit.MILLISECONDS);
return result;
} catch (Throwable e) {
meterRegistry.counter("api.error.count",
"method", pjp.getSignature().getName()
).increment();
throw e;
}
}
}
2. 慢查询日志
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl
logging:
level:
com.example.demo.mapper: DEBUG
3. APM 工具
- SkyWalking - 链路追踪
- Prometheus - 指标监控
- Grafana - 可视化
- Arthas - 诊断工具
最佳实践
1. 性能检查清单
- [ ] JVM 内存配置合理
- [ ] GC 参数优化
- [ ] 数据库连接池配置
- [ ] SQL 有索引
- [ ] 避免 N+1 查询
- [ ] 使用缓存
- [ ] 异步处理 IO
- [ ] 接口分页
- [ ] 响应压缩
- [ ] 监控告警
2. 性能测试
# JMeter 压测
jmeter -n -t test.jmx -l result.jtl
# wrk 压测
wrk -t12 -c400 -d30s http://localhost:8080/api/users
# 分析结果
cat result.jtl | jmeter-plugins.cmd --tool Reporter
3. 优化优先级
1. 数据库优化(影响最大)
2. 缓存策略(效果明显)
3. 异步处理(提升吞吐)
4. JVM 调优(最后优化)
总结
性能优化要点:
- ✅ JVM 调优 - 内存、GC、虚拟线程
- ✅ 数据库优化 - 连接池、索引、SQL
- ✅ 缓存策略 - 本地、Redis、多级
- ✅ 异步处理 - 任务、消息队列
- ✅ 接口优化 - 分页、投影、压缩
- ✅ 监控分析 - APM、慢查询、压测
性能优化是一个持续的过程。