Spring Boot 3.x 基于 Spring Framework 6 和 Jakarta EE,带来了显著的性能提升。本文深入探讨如何最大化应用性能。
java -jar app.jar \ -XX:+UseG1GC \ -XX:MaxGCPauseMillis=200 \ -XX:G1HeapRegionSize=16m
适用场景:大堆内存(4GB+),需要低延迟
java -jar app.jar \ -XX:+UseZGC \ -XX:+ZGenerational
适用场景:超低延迟要求(<10ms GC 暂停)
# 初始堆和最大堆设为相同值 java -Xms4g -Xmx4g -jar app.jar # 元空间 java -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m
# 限制直接内存,防止 OOM java -XX:MaxDirectMemorySize=1g
# 分层编译(默认) java -XX:+TieredCompilation # 提前优化 java -XX:CompileThreshold=10000 # 使用 C2 编译器 java -XX:+UseCompressedOops
spring: # 关闭开发时功能 devtools: restart: enabled: false # 生产环境配置 jpa: show-sql: false open-in-view: false # Jackson 优化 jackson: default-property-inclusion: non_null serialization: write-dates-as-timestamps: false # 日志配置 logging: level: root: INFO org.springframework: WARN pattern: console: "%d{yyyy-MM-dd HH:mm:ss} - %msg%n"
server: tomcat: threads: max: 200 min-spare: 10 max-connections: 10000 accept-count: 100
@Configuration @EnableAsync public class AsyncConfig { @Bean(name = "taskExecutor") public Executor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(10); executor.setMaxPoolSize(50); executor.setQueueCapacity(100); executor.setThreadNamePrefix("async-"); executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); executor.initialize(); return executor; } }
spring: datasource: hikari: maximum-pool-size: 20 minimum-idle: 5 idle-timeout: 30000 max-lifetime: 1200000 connection-timeout: 30000 pool-name: "HikariPool"
@Bean public DataSource dataSource(DataSourceProperties properties) { HikariDataSource dataSource = properties .initializeDataSourceBuilder() .type(HikariDataSource.class) .build(); // 启用 JMX 监控 dataSource.setMetricRegistry(metricRegistry); return dataSource; }
-- 创建复合索引 CREATE INDEX idx_user_email_status ON users(email, status); -- 覆盖索引 CREATE INDEX idx_order_user_status ON orders(user_id, status, created_at);
// ❌ 不好的做法 List<User> users = user.findAll(); for (User user : users) { List<Order> orders = order.findByUserId(user.getId()); } // ✅ 好的做法(JOIN FETCH) @Query("SELECT u FROM User u JOIN FETCH u.orders WHERE u.id = :id") User findByIdWithOrders(@Param("id") Long id); // ✅ 或使用 EntityGraph @EntityGraph(attributePaths = {"orders"}) User findById(Long id);
spring: jpa: properties: hibernate: jdbc: batch_size: 50 order_inserts: true order_updates: true
@Transactional public void batchInsert(List<User> users) { for (int i = 0; i < users.size(); i++) { entityManager.persist(users.get(i)); // 定期 flush 和 clear if (i % 50 == 0) { entityManager.flush(); entityManager.clear(); } } }
spring: jpa: properties: hibernate: cache: use_second_level_cache: true region: factory_class: jcache
@Cacheable @CacheRegion("users") @Entity public class User { // ... }
@Configuration @EnableCaching public class CacheConfig { @Bean public RedisCacheManager cacheManager(RedisConnectionFactory factory) { RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() .entryTtl(Duration.ofMinutes(10)) .disableCachingNullValues() .serializeValuesWith( RedisSerializationContext.SerializationPair.fromSerializer( new GenericJackson2JsonRedisSerializer() ) ); return RedisCacheManager.builder(factory) .cacheDefaults(config) .build(); } }
@Service public class UserService { @Cacheable(value = "users", key = "#id") public User findById(Long id) { return userRepository.findById(id).orElse(null); } @CacheEvict(value = "users", key = "#user.id") public User update(User user) { return userRepository.save(user); } @CacheEvict(value = "users", allEntries = true) public void clearCache() { // 清空所有缓存 } }
@Bean public Cache<String, Object> localCache() { return Caffeine.newBuilder() .maximumSize(10000) .expireAfterWrite(Duration.ofMinutes(5)) .recordStats() .build(); }
@Service public class EmailService { @Async("taskExecutor") public void sendEmail(String to, String subject, String body) { // 异步发送邮件 mailSender.send(to, subject, body); } }
@RestController public class ReactiveUserController { private final UserRepository userRepository; @GetMapping("/users/{id}") public Mono<User> getUser(@PathVariable Long id) { return userRepository.findById(id); } @GetMapping("/users") public Flux<User> getAllUsers() { return userRepository.findAll(); } }
server: compression: enabled: true mime-types: text/html,text/xml,text/plain,text/css,application/javascript,application/json
@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/static/**") .addResourceLocations("classpath:/static/") .setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS)); } }
management: endpoints: web: exposure: include: health,info,metrics,prometheus metrics: export: prometheus: enabled: true
@Component public class MetricsConfig { private final MeterRegistry meterRegistry; public void recordApiCall(String endpoint, long duration) { Timer.builder("api.call.duration") .tag("endpoint", endpoint) .register(meterRegistry) .record(duration, TimeUnit.MILLISECONDS); } }
<dependency> <groupId>io.micrometer</groupId> <artifactId>micrometer-tracing-bridge-brave</artifactId> </dependency> <dependency> <groupId>io.zipkin.reporter2</groupId> <artifactId>zipkin-reporter-brave</artifactId> </dependency>
management: tracing: sampling: probability: 1.0 zipkin: tracing: endpoint: http://zipkin:9411/api/v2/spans
// 性能测试脚本 val httpProtocol = http.baseUrl("http://localhost:8080") val scn = scenario("User Scenario") .exec(http("get_users") .get("/api/users") .check(status.is(200))) setUp( scn.inject( rampUsersPerSec(10) to 100 during (60 seconds) ) ).protocols(httpProtocol)
记住:过早优化是万恶之源,先测量再优化!