一、应用层优化 (Spring Boot & Spring Framework)
1. 合理使用 Spring 核心特性
@Transactional 事务优化
- 将事务范围控制在最小必要单元,避免在只读操作上开启事务。
- 明确指定
readOnly = true
用于只读事务。 - 避免在事务方法中执行耗时操作(如远程调用、复杂计算)。
java
@Service
public class OrderService {
private final OrderRepository orderRepository;
// 构造函数注入
public OrderService(OrderRepository orderRepository) {
this.orderRepository = orderRepository;
}
// 只读事务优化
@Transactional(readOnly = true)
public List<Order> findAllOrders() {
return orderRepository.findAll();
}
// 常规事务 - 最小化事务范围
@Transactional
public Order createOrder(Order order) {
// 仅在必要操作上开启事务
Order savedOrder = orderRepository.save(order);
// 非事务性操作应放在事务外执行
return savedOrder;
}
// 明确指定传播行为和隔离级别(根据实际需求)
@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_COMMITTED)
public void updateOrderStatus(Long orderId, OrderStatus status) {
Order order = orderRepository.findById(orderId)
.orElseThrow(() -> new ResourceNotFoundException("Order not found"));
order.setStatus(status);
orderRepository.save(order);
}
}
懒加载与Bean作用域优化
- 对于启动时不立即需要的大对象或依赖,使用
@Lazy
延迟初始化,加速应用启动。 - 正确使用作用域(
singleton
,prototype
,request
,session
)。
java
@Configuration
public class AppConfig {
// 懒加载示例 - 仅在首次使用时初始化
@Bean
@Lazy
public HeavyComponent heavyComponent() {
return new HeavyComponent(); // 假设这是一个初始化耗时的组件
}
// 原型作用域Bean - 每次请求创建新实例
@Bean
@Scope("prototype")
public RequestScopedComponent requestScopedComponent() {
return new RequestScopedComponent();
}
}
AOP切面优化
- 精确指定切点表达式 (
@Pointcut
),避免拦截不必要的 JoinPoint。评估 AOP 带来的性能开销是否可接受。
java
@Aspect
@Component
public class OptimizedAspect {
// 精确的切点表达式,避免过度拦截
@Pointcut("execution(* com.yourcompany.service.*Service.*(..)) && !execution(* com.yourcompany.service.*Service.get*(..))")
public void serviceMethodPointcut() {}
@Around("serviceMethodPointcut()")
public Object optimizeServiceCalls(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
try {
return joinPoint.proceed();
} finally {
long duration = System.currentTimeMillis() - startTime;
// 仅记录慢操作
if (duration > 500) { // 超过500ms视为慢操作
log.warn("Slow method: {} took {}ms", joinPoint.getSignature(), duration);
}
}
}
}
2. 组件扫描
- 使用
@SpringBootApplication(scanBasePackages = "包路径")
或@ComponentScan
明确指定扫描的基础包路径,避免扫描整个类路径 (classpath:*
),减少启动时间和内存占用。
java
// 优化组件扫描 - 明确指定基础包而非扫描整个根包
@SpringBootApplication(scanBasePackages = {
"com.yourcompany.service",
"com.yourcompany.controller.api"
})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
3. 启动优化配置
- Spring Boot 2.2+ 支持。将所有 Bean 设置为懒加载,可以显著减少启动时间(尤其是有大量 Bean 时)。注意: 这可能导致首次请求延迟变高,需权衡。
- 确保类路径干净,移除无用的 JAR 包。使用 Spring Boot 的
spring-boot-maven-plugin
打包为可执行 JAR(内嵌容器)或 WAR(部署到外部容器)时,依赖的组织方式会影响启动速度。
yaml
# application.yml
spring:
main:
lazy-initialization: true # 全局懒加载
web-application-type: servlet # 明确指定应用类型,避免自动检测耗时
# 仅在需要时激活相关自动配置
spring.autoconfigure.exclude:
- org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration
- org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration
# 启动时排除不需要的自动配置类
4.日志优化
- 使用异步日志框架(如 Logback 的
AsyncAppender
或 Log4j2 的异步 Logger)减少 I/O 阻塞。 - 合理设置日志级别,生产环境避免
DEBUG
/TRACE
。 - 控制日志输出量,避免过于冗长的日志格式或记录不必要的信息。
二、JVM 层优化
1. JVM参数优化示例
针对不同场景的JVM配置:
1. 常规Web应用(4核8G内存服务器):
bash
java -jar app.jar \
-Xms6g -Xmx6g \
-XX:+UseG1GC \
-XX:MaxGCPauseMillis=200 \
-XX:InitiatingHeapOccupancyPercent=75 \
-XX:+UseStringDeduplication \
-XX:+UseContainerSupport \
-XX:MaxRAMPercentage=75.0 \ # 容器环境中使用75%的可用内存
-XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=/var/log/app/heapdump.hprof
2. 微服务应用(小内存环境):
bash
java -jar app.jar \
-Xms2g -Xmx2g \
-XX:+UseSerialGC \ # 小内存环境下SerialGC可能更高效
-XX:NewRatio=3 \ # 老年代与年轻代比例1:3
-XX:SurvivorRatio=4 \ # Eden:Survivor = 4:1:1
-XX:MaxTenuringThreshold=15 \ # 对象晋升老年代的年龄阈值
-XX:+UseCompressedOops \ # 压缩对象指针
-XX:+UseCompressedClassPointers
2. 虚拟线程配置(Java 21+)
Spring Boot 3.2+ 支持虚拟线程:
yaml
# application.yml
server:
tomcat:
threads:
virtual:
enabled: true # 启用虚拟线程
三、数据库层优化
1. HikariCP连接池配置
- 选择合适的连接池: HikariCP (Spring Boot 默认,性能优异) 是首选。Druid 功能强大(监控、防御 SQL 注入等)也是好选择。
- 关键参数:
maximum-pool-size
:根据数据库处理能力和应用并发需求设置。不是越大越好! 过大会导致数据库连接耗尽或资源争抢。minimum-idle
:维持的最小空闲连接数。connection-timeout
:获取连接的超时时间,设置合理值避免长时间等待。idle-timeout
/max-lifetime
:连接空闲超时和最大生命周期,防止连接泄漏或数据库端主动断开导致的问题。- 监控连接池指标(活跃连接、空闲连接、等待线程数) 是调优的关键。
yaml
# application.yml
spring:
datasource:
type: com.zaxxer.hikari.HikariDataSource
url: jdbc:mysql://${DB_HOST:localhost}:3306/mydb?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
username: ${DB_USERNAME:root}
password: ${DB_PASSWORD:secret}
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
# 核心配置
maximum-pool-size: 15 # 最大连接数,根据CPU核心数和数据库性能调整
minimum-idle: 5 # 最小空闲连接数
idle-timeout: 300000 # 空闲超时时间(ms),默认600000(10分钟)
connection-timeout: 20000 # 连接超时时间(ms)
max-lifetime: 1800000 # 连接最大生命周期(ms),建议比数据库wait_timeout小30秒
connection-test-query: SELECT 1 # 连接测试查询
pool-name: MyAppHikariCP # 线程池名称,便于监控
auto-commit: false # 禁用自动提交,在事务中手动控制
# 高级配置
leak-detection-threshold: 60000 # 连接泄漏检测阈值(ms)
data-source-properties:
cachePrepStmts: true # 启用预处理语句缓存
prepStmtCacheSize: 250 # 预处理语句缓存大小
prepStmtCacheSqlLimit: 2048 # 预处理语句最大长度
useServerPrepStmts: true # 使用服务器端预处理
2. JPA/Hibernate优化配置
yaml
# application.yml
spring:
jpa:
hibernate:
ddl-auto: validate # 生产环境禁用update/create/drop
naming:
physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
properties:
hibernate:
jdbc.batch_size: 30 # 批量操作大小
order_inserts: true # 启用插入排序优化批量操作
order_updates: true # 启用更新排序优化批量操作
jdbc.batch_versioned_data: true # 批量操作时处理版本控制
show_sql: false # 生产环境禁用SQL显示
format_sql: false
generate_statistics: false # 生产环境禁用统计
cache:
use_second_level_cache: true # 启用二级缓存
region.factory_class: org.hibernate.cache.jcache.JCacheRegionFactory
use_query_cache: true # 启用查询缓存
open-in-view: false # 关闭Open Session In View反模式!解决N+1问题和连接泄漏风险
3. SQL优化示例
JPA查询优化(避免N+1问题):
- N+1 查询问题: 这是 ORM 最常见性能问题。使用
JOIN FETCH
(JPA),@Fetch(FetchMode.JOIN)
(Hibernate), 或@NamedEntityGraph
来一次性加载关联数据。避免在循环中触发懒加载。
java
@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
// 正确:使用JOIN FETCH一次性加载关联
@Query("SELECT o FROM Order o JOIN FETCH o.items WHERE o.id = :id")
Optional<Order> findByIdWithItems(@Param("id") Long id);
// 分页查询优化
@Query("SELECT o FROM Order o WHERE o.status = :status ORDER BY o.createTime DESC")
Page<Order> findByStatus(@Param("status") OrderStatus status, Pageable pageable);
// 投影查询 - 只查询需要的字段
@Query("SELECT new com.example.dto.OrderSummaryDTO(o.id, o.orderNo, o.createTime, o.status) " +
"FROM Order o WHERE o.customerId = :customerId")
List<OrderSummaryDTO> findOrderSummariesByCustomerId(@Param("customerId") Long customerId);
}
MyBatis批量操作示例:
- MyBatis 优化: 合理使用一级/二级缓存;避免动态 SQL 过于复杂;使用
foreach
进行批量操作;注意#{}
和${}
的区别(防注入)。
xml
<!-- OrderMapper.xml -->
<insert id="batchInsertOrders">
INSERT INTO orders (order_no, customer_id, status, create_time)
VALUES
<foreach collection="orders" item="order" separator=",">
(#{order.orderNo}, #{order.customerId}, #{order.status}, #{order.createTime})
</foreach>
</insert>
四、缓存优化
- 本地缓存 (Caffeine, Ehcache): 速度快,适合缓存少量、不易变、对一致性要求不高的数据。注意单机缓存的一致性和内存限制。
- 分布式缓存 (Redis, Memcached): 适合共享缓存、大量缓存数据、高可用场景。Redis 功能更丰富(数据结构、持久化等)。
1. Redis缓存集成与配置
java
@Configuration
@EnableCaching
public class RedisCacheConfig {
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
// 默认配置
RedisCacheConfiguration defaultConfig = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(30)) // 默认30分钟过期
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(
new GenericJackson2JsonRedisSerializer()))
.disableCachingNullValues(); // 不缓存null值
// 特定缓存配置
Map<String, RedisCacheConfiguration> configs = new HashMap<>();
// 产品缓存时间较长
configs.put("products", defaultConfig.entryTtl(Duration.ofHours(24)));
// 用户缓存时间中等
configs.put("users", defaultConfig.entryTtl(Duration.ofHours(1)));
// 热点数据缓存时间短,但使用随机过期避免缓存雪崩
configs.put("hotData", defaultConfig.entryTtl(Duration.ofMinutes(10)));
return RedisCacheManager.builder(connectionFactory)
.cacheDefaults(defaultConfig)
.withInitialCacheConfigurations(configs)
.transactionAware() // 支持事务缓存
.build();
}
}
2. 缓存使用示例
java
@Service
public class ProductService {
private final ProductRepository productRepository;
// 构造函数注入...
// 基本缓存示例
@Cacheable(value = "products", key = "#id", unless = "#result == null")
public Product findById(Long id) {
return productRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Product not found"));
}
// 条件缓存示例
@Cacheable(value = "products", key = "#code", condition = "#code.length() > 3")
public Product findByCode(String code) {
return productRepository.findByCode(code)
.orElseThrow(() -> new ResourceNotFoundException("Product not found"));
}
// 缓存更新
@CachePut(value = "products", key = "#product.id")
public Product updateProduct(Product product) {
return productRepository.save(product);
}
// 缓存删除
@CacheEvict(value = "products", key = "#id")
public void deleteProduct(Long id) {
productRepository.deleteById(id);
}
// 清空缓存
@CacheEvict(value = "products", allEntries = true)
@Scheduled(fixedRate = 86400000) // 每天凌晨清空一次缓存
public void clearProductCache() {
log.info("Clearing product cache");
}
}
3. 缓存问题解决方案示例
缓存穿透防护(空值缓存+布隆过滤器):
java
@Service
public class ProductServiceImpl implements ProductService {
private final ProductRepository productRepository;
private final BloomFilter<Long> productIdBloomFilter;
// 构造函数注入...
@Override
@Cacheable(value = "products", key = "#id", unless = "#result == null")
public Product findById(Long id) {
// 先检查布隆过滤器,如果不存在直接返回null
if (!productIdBloomFilter.mightContain(id)) {
return null;
}
Product product = productRepository.findById(id).orElse(null);
// 缓存空值防止缓存穿透
if (product == null) {
// 缓存空值5分钟
redisTemplate.opsForValue().set("products::" + id, null, 5, TimeUnit.MINUTES);
}
return product;
}
}
缓存雪崩防护(添加随机过期时间):
java
@Configuration
public class CacheConfig {
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
// 默认配置
RedisCacheConfiguration defaultConfig = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(30))
// 其他配置...
return RedisCacheManager.builder(connectionFactory)
.cacheDefaults(defaultConfig)
.withCacheConfiguration("products",
defaultConfig.entryTtl(Duration.ofMinutes(30 + new Random().nextInt(10)))) // 30-40分钟随机过期
.build();
}
}
五、异步与并发优化
1. @Async异步方法与线程池配置
- 将耗时且非结果依赖的操作(如发邮件、记录日志、调用外部系统)异步化,释放请求线程。
- 配置自定义线程池 (
ThreadPoolTaskExecutor
) ,避免使用默认的SimpleAsyncTaskExecutor
(为每个任务新建线程)。 - 设置合理的核心线程数、最大线程数、队列容量、拒绝策略。
java
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean(name = "taskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 核心线程数 = CPU核心数 + 1
int corePoolSize = Runtime.getRuntime().availableProcessors() + 1;
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(corePoolSize * 2); // 最大线程数
executor.setQueueCapacity(100); // 队列容量
executor.setThreadNamePrefix("Async-"); // 线程名前缀
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 拒绝策略:调用者执行
executor.setAwaitTerminationSeconds(60); // 等待终止时间
executor.setWaitForTasksToCompleteOnShutdown(true); // 关闭时等待任务完成
executor.initialize();
return executor;
}
}
@Service
public class NotificationService {
private final EmailClient emailClient;
// 构造函数注入...
@Async("taskExecutor") // 指定线程池
public CompletableFuture<Void> sendOrderConfirmationEmail(Order order) {
try {
// 模拟邮件发送耗时操作
Thread.sleep(1000);
emailClient.send(order.getCustomerEmail(), "Order Confirmation",
"Thank you for your order #" + order.getId());
return CompletableFuture.completedFuture(null);
} catch (Exception e) {
log.error("Failed to send email", e);
return CompletableFuture.failedFuture(e);
}
}
}
2. 响应式编程示例(WebFlux)
- 对于高并发、IO 密集型(如微服务间调用、数据库访问)应用,考虑使用 Spring WebFlux 和非阻塞式驱动(如 R2DBC, Reactive MongoDB)。
- 利用少量线程处理大量并发连接,提高资源利用率。
- 注意: 编程模型与 Servlet 不同,需要学习曲线。确保整个调用链都是非阻塞的。
java
@RestController
@RequestMapping("/api/orders")
public class OrderController {
private final OrderService orderService;
// 构造函数注入...
@GetMapping("/{id}")
public Mono<ResponseEntity<OrderDTO>> getOrder(@PathVariable Long id) {
return orderService.findById(id)
.map(order -> ResponseEntity.ok(convertToDTO(order)))
.defaultIfEmpty(ResponseEntity.notFound().build());
}
@GetMapping
public Flux<OrderSummaryDTO> getOrdersByCustomer(@RequestParam Long customerId) {
return orderService.findByCustomerId(customerId)
.map(this::convertToSummaryDTO)
.sort((o1, o2) -> o2.getCreateTime().compareTo(o1.getCreateTime()));
}
}
@Service
public class OrderService {
private final OrderRepository orderRepository;
// 构造函数注入...
public Mono<Order> findById(Long id) {
return Mono.fromCallable(() -> orderRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Order not found")));
}
public Flux<Order> findByCustomerId(Long customerId) {
return Flux.fromIterable(orderRepository.findByCustomerId(customerId));
}
}
六、完整的性能优化实践案例
案例1:数据库查询优化前后对比
优化前(问题代码):
java
// 问题:N+1查询和全表扫描
@GetMapping("/{orderId}")
public OrderDTO getOrder(@PathVariable Long orderId) {
Order order = orderRepository.findById(orderId).orElseThrow();
// 每次调用getItems()都会触发额外查询
List<OrderItem> items = order.getItems();
// ... 转换为DTO
}
优化后:
java
// 优化:使用JOIN FETCH + 投影DTO
@GetMapping("/{orderId}")
public OrderSummaryDTO getOrderSummary(@PathVariable Long orderId) {
return orderRepository.findOrderSummaryById(orderId)
.orElseThrow(() -> new ResourceNotFoundException("Order not found"));
}
// Repository方法
@Query("SELECT new com.example.dto.OrderSummaryDTO(o.id, o.orderNo, o.createTime, " +
"SUM(oi.quantity * oi.unitPrice) as totalAmount, " +
"COUNT(oi) as itemCount) " +
"FROM Order o JOIN o.items oi WHERE o.id = :id " +
"GROUP BY o.id, o.orderNo, o.createTime")
Optional<OrderSummaryDTO> findOrderSummaryById(@Param("id") Long id);
案例2:缓存策略实现
多级缓存实现(Caffeine本地缓存 + Redis分布式缓存):
java
@Configuration
@EnableCaching
public class MultiLevelCacheConfig {
// Caffeine本地缓存配置
@Bean
public Cache<String, Object> caffeineCache() {
return Caffeine.newBuilder()
.maximumSize(10_000) // 最大缓存项数
.expireAfterWrite(5, TimeUnit.MINUTES) // 写入后过期时间
.recordStats() // 开启统计
.build();
}
// Redis分布式缓存配置
@Bean
public RedisCacheManager redisCacheManager(RedisConnectionFactory connectionFactory) {
// 配置Redis缓存
// ... 省略RedisCacheManager配置,同上一节
}
// 自定义缓存管理器,结合本地缓存和Redis缓存
@Bean
public CacheManager cacheManager(RedisCacheManager redisCacheManager, Cache<String, Object> caffeineCache) {
return new MultiLevelCacheManager(redisCacheManager, caffeineCache);
}
}
// 自定义多级缓存管理器实现
public class MultiLevelCacheManager implements CacheManager {
private final RedisCacheManager redisCacheManager;
private final Cache<String, Object> caffeineCache;
// 构造函数注入...
@Override
public Cache getCache(String name) {
return new MultiLevelCache(
name,
caffeineCache,
redisCacheManager.getCache(name)
);
}
// 其他方法实现...
}
// 多级缓存实现类
class MultiLevelCache implements Cache {
private final String name;
private final Cache<String, Object> localCache;
private final Cache redisCache;
// 实现get、put、evict等方法,先查本地缓存,再查Redis缓存...
}
七、监控与诊断
- 启用 Actuator:
spring-boot-starter-actuator
提供丰富的生产就绪端点 (/actuator/metrics
,/actuator/health
,/actuator/prometheus
等)。 - 集成监控系统:
- Prometheus + Grafana: 强大的指标采集和可视化组合。使用
micrometer-registry-prometheus
。 - ELK Stack (Elasticsearch, Logstash, Kibana): 集中式日志管理和分析。
- 分布式追踪: Zipkin, Jaeger。集成 Spring Cloud Sleuth 来追踪请求在微服务间的流转。
- Prometheus + Grafana: 强大的指标采集和可视化组合。使用
- Profiler 工具: 使用 VisualVM , YourKit , JProfiler , Arthas 等进行 CPU 采样、内存分析、线程分析,定位热点方法和内存泄漏。
- APM (应用性能监控): New Relic, Dynatrace, AppDynamics, SkyWalking, Pinpoint 等提供端到端的应用性能洞察。
总结
Spring Boot性能优化是一个系统性工程,需要从多个维度综合考虑:
- 应用层:合理使用Spring特性、优化Bean管理、AOP和事务
- JVM层:选择合适的JVM参数和垃圾收集器,根据应用特性调整
- 数据库层:优化连接池、SQL查询、ORM配置
- 缓存策略:多级缓存、缓存穿透/击穿/雪崩防护
- 并发处理:异步方法、响应式编程、线程池优化
- 监控与持续优化:建立完善的监控体系,持续收集数据并优化
关键建议:
- 始终以数据为驱动进行优化决策
- 优先解决最大的性能瓶颈("低垂的果实")
- 避免过早优化和过度优化
- 建立性能测试和基准测试体系
- 持续监控生产环境性能指标
- 关注Spring官方博客和文档,了解最新性能改进