性能优化实战
性能优化方法论
优化原则
80/20 法则:
- 80% 的性能问题来自 20% 的代码
- 优先优化热点代码
- 避免过早优化
数据驱动:
- 基于监控数据定位瓶颈
- 避免凭感觉优化
- 持续监控验证效果
循序渐进:
- 从易到难
- 从局部到整体
- 持续迭代改进
优化流程
1. 建立性能基线
↓
2. 性能监控分析
↓
3. 定位性能瓶颈
↓
4. 制定优化方案
↓
5. 实施优化
↓
6. 验证优化效果
↓
7. 持续监控
JVM 性能优化
1. 内存优化
堆内存配置:
# 生产环境配置
-Xms4g -Xmx4g # 堆内存大小
-Xmn2g # 新生代大小
-XX:MetaspaceSize=256m # 元空间初始大小
-XX:MaxMetaspaceSize=512m # 元空间最大大小
垃圾回收器选择:
# G1 GC(推荐)
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:G1HeapRegionSize=16m
# ZGC(低延迟)
-XX:+UseZGC
-XX:ZCollectionInterval=5
内存优化建议:
- 堆内存设置为物理内存的 50-70%
- 新生代占堆内存的 1/3
- 避免频繁 Full GC
2. GC 优化
GC 日志分析:
# 开启 GC 日志
-Xlog:gc*:file=gc.log:time,uptime,level,tags
# 分析工具
gceviz -f gc.log
GC 调优参数:
# G1 GC 调优
-XX:InitiatingHeapOccupancyPercent=45 # 触发并发标记的堆占用率
-XX:ConcGCThreads=4 # 并发 GC 线程数
-XX:ParallelGCThreads=8 # 并行 GC 线程数
-XX:MaxGCPauseMillis=200 # 最大 GC 停顿时间
GC 优化建议:
- 控制 GC 停顿时间 < 200ms
- Full GC 频率 < 1 次/小时
- Young GC 频率 < 10 次/分钟
3. 线程优化
线程池配置:
@Bean
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 核心线程数 = CPU 核心数 * 2
executor.setCorePoolSize(Runtime.getRuntime().availableProcessors() * 2);
// 最大线程数 = CPU 核心数 * 4
executor.setMaxPoolSize(Runtime.getRuntime().availableProcessors() * 4);
// 队列容量
executor.setQueueCapacity(200);
// 线程空闲时间
executor.setKeepAliveSeconds(60);
// 线程名前缀
executor.setThreadNamePrefix("async-");
// 拒绝策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
return executor;
}
Tomcat 线程池:
server:
tomcat:
threads:
max: 200 # 最大工作线程数
min-spare: 10 # 最小空闲线程数
max-connections: 8192 # 最大连接数
accept-count: 100 # 等待队列长度
数据库优化
1. SQL 优化
索引优化:
-- 创建复合索引
CREATE INDEX idx_user_status_time ON orders (user_id, status, create_time);
-- 覆盖索引
SELECT user_id, status FROM orders WHERE user_id = 1;
-- 避免全表扫描
EXPLAIN SELECT * FROM orders WHERE user_id = 1;
SQL 优化建议:
- 避免 SELECT *
- 避免在 WHERE 子句中使用函数
- 避免 LIKE ‘%xxx%’
- 使用 EXISTS 代替 IN
- 使用 UNION ALL 代替 UNION
分页优化:
-- 不推荐(大数据量时慢)
SELECT * FROM orders ORDER BY create_time DESC LIMIT 100000, 10;
-- 推荐(使用覆盖索引)
SELECT * FROM orders o
INNER JOIN (
SELECT id FROM orders ORDER BY create_time DESC LIMIT 100000, 10
) tmp ON o.id = tmp.id;
2. 连接池优化
HikariCP 配置:
spring:
datasource:
hikari:
# 最小空闲连接数
minimum-idle: 10
# 最大连接数
maximum-pool-size: 50
# 空闲连接超时(毫秒)
idle-timeout: 30000
# 连接超时(毫秒)
connection-timeout: 30000
# 连接最大生命周期(毫秒)
max-lifetime: 1800000
# 连接测试查询
connection-test-query: SELECT 1
连接池优化建议:
- 最大连接数 = ((核心数 * 2) + 有效磁盘数)
- 空闲连接数 = 核心数
- 连接超时 = 30 秒
- 开启连接测试
3. 缓存优化
Redis 缓存:
@Configuration
public class RedisConfig {
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(10)) // 默认 TTL 10 分钟
.serializeKeysWith(RedisSerializationContext.SerializationPair
.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair
.fromSerializer(new GenericJackson2JsonRedisSerializer()));
return RedisCacheManager.builder(factory)
.cacheDefaults(config)
.withCacheConfiguration("user",
config.entryTtl(Duration.ofMinutes(30))) // 用户缓存 30 分钟
.withCacheConfiguration("order",
config.entryTtl(Duration.ofMinutes(60))) // 订单缓存 60 分钟
.build();
}
}
多级缓存:
@Component
public class MultiLevelCache {
@Autowired
private CacheManager localCache; // Caffeine
@Autowired
private CacheManager redisCache; // Redis
public <T> T get(String key, Class<T> type) {
// 1. 本地缓存
T value = localCache.getCache("local").get(key, type);
if (value != null) {
return value;
}
// 2. Redis 缓存
value = redisCache.getCache("redis").get(key, type);
if (value != null) {
// 回写本地缓存
localCache.getCache("local").put(key, value);
return value;
}
return null;
}
}
网络优化
1. HTTP 优化
连接复用:
feign:
httpclient:
enabled: true
max-connections: 200 # 最大连接数
max-connections-per-route: 50 # 每路由最大连接数
time-to-live: 60000 # 连接存活时间(毫秒)
压缩配置:
feign:
compression:
request:
enabled: true
mime-types: text/xml,application/xml,application/json
min-request-size: 2048
response:
enabled: true
2. Nginx 优化
负载均衡:
upstream backend {
least_conn; # 最少连接数
server backend1:8080 weight=5;
server backend2:8080 weight=3;
server backend3:8080 backup;
keepalive 32; # 长连接数
}
server {
listen 80;
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
# 超时配置
proxy_connect_timeout 10s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# 缓冲配置
proxy_buffer_size 4k;
proxy_buffers 4 32k;
proxy_busy_buffers_size 64k;
}
}
3. TCP 优化
Linux 内核参数:
# /etc/sysctl.conf
# TCP 连接复用
net.ipv4.tcp_tw_reuse = 1
# 端口范围
net.ipv4.ip_local_port_range = 1024 65535
# 连接队列
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
# 缓冲区
net.core.rmem_default = 262144
net.core.rmem_max = 67108864
net.core.wmem_default = 262144
net.core.wmem_max = 67108864
# 应用配置
sysctl -p
代码优化
1. 集合优化
选择合适集合:
// 频繁查询,不修改 → ArrayList
List<String> list = new ArrayList<>();
// 频繁插入删除 → LinkedList
List<String> list = new LinkedList<>();
// 频繁查询 → HashMap
Map<String, String> map = new HashMap<>();
// 有序 → TreeMap
Map<String, String> map = new TreeMap<>();
// 线程安全 → ConcurrentHashMap
Map<String, String> map = new ConcurrentHashMap<>();
集合初始化:
// 指定初始容量,避免扩容
List<String> list = new ArrayList<>(100);
Map<String, String> map = new HashMap<>(64);
// 使用 Collections 工具类
List<String> immutableList = Collections.unmodifiableList(list);
2. 流处理优化
并行流:
// 数据量大时使用并行流
List<String> result = list.parallelStream()
.filter(s -> s.length() > 5)
.map(String::toUpperCase)
.collect(Collectors.toList());
// 注意线程安全
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
list.parallelStream().forEach(s -> map.put(s, s.length()));
流式处理:
// 避免中间操作产生新集合
list.stream()
.filter(s -> s != null)
.map(String::trim)
.filter(s -> !s.isEmpty())
.collect(Collectors.toList());
3. 异步优化
CompletableFuture:
// 串行执行(慢)
User user = userService.getUser(id);
Order order = orderService.getOrder(id);
Product product = productService.getProduct(id);
// 并行执行(快)
CompletableFuture<User> userFuture = CompletableFuture.supplyAsync(() ->
userService.getUser(id));
CompletableFuture<Order> orderFuture = CompletableFuture.supplyAsync(() ->
orderService.getOrder(id));
CompletableFuture<Product> productFuture = CompletableFuture.supplyAsync(() ->
productService.getProduct(id));
CompletableFuture.allOf(userFuture, orderFuture, productFuture).join();
User user = userFuture.get();
Order order = orderFuture.get();
Product product = productFuture.get();
架构优化
1. 读写分离
主从配置:
spring:
datasource:
dynamic:
primary: master
datasource:
master:
url: jdbc:mysql://master:3306/db
username: root
password: root
slave1:
url: jdbc:mysql://slave1:3306/db
username: root
password: root
slave2:
url: jdbc:mysql://slave2:3306/db
username: root
password: root
读写分离注解:
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ReadOnly {
}
@Aspect
@Component
public class ReadOnlyAspect {
@Around("@annotation(readOnly)")
public Object around(ProceedingJoinPoint pjp, ReadOnly readOnly) throws Throwable {
DynamicDataSource.setSlave();
try {
return pjp.proceed();
} finally {
DynamicDataSource.setMaster();
}
}
}
@Service
public class UserService {
@ReadOnly
public User getUser(Long id) {
return userRepository.findById(id);
}
}
2. 分库分表
分片策略:
@Configuration
public class ShardingConfig {
@Bean
public DataSource dataSource() {
ShardingSphereDataSource dataSource = new ShardingSphereDataSource(
createDataSourceMap(),
Collections.singletonList(shardingRule),
new Properties()
);
return dataSource;
}
private ShardingRuleConfiguration shardingRule() {
ShardingRuleConfiguration shardingRule = new ShardingRuleConfiguration();
// 分表策略
shardingRule.getTables().add(orderTableRule());
// 分库策略
shardingRule.getDatabaseShardingRule().setDatabaseShardingColumn("user_id");
shardingRule.getDatabaseShardingRule().setShardingAlgorithmName("database_inline");
return shardingRule;
}
private TableRuleConfiguration orderTableRule() {
TableRuleConfiguration tableRule = new TableRuleConfiguration("orders");
tableRule.setActualDataNodes("ds${0..1}.orders_${0..3}");
tableRule.setTableShardingColumn("order_id");
tableRule.setTableShardingAlgorithmName("table_inline");
return tableRule;
}
}
3. 消息队列削峰
异步处理:
// 同步处理(慢)
@PostMapping("/orders")
public Result<Order> createOrder(@RequestBody CreateOrderRequest request) {
// 1. 创建订单
Order order = orderService.create(request);
// 2. 发送短信
smsService.send(order);
// 3. 发送邮件
emailService.send(order);
// 4. 更新统计
analyticsService.update(order);
return Result.success(order);
}
// 异步处理(快)
@PostMapping("/orders")
public Result<Order> createOrder(@RequestBody CreateOrderRequest request) {
// 1. 创建订单
Order order = orderService.create(request);
// 2. 发送消息
kafkaTemplate.send("order-created", order);
return Result.success(order);
}
// 消息消费者
@KafkaListener(topics = "order-created")
public void consume(Order order) {
// 异步处理
smsService.send(order);
emailService.send(order);
analyticsService.update(order);
}
监控与调优
1. 性能监控
APM 工具:
# SkyWalking Agent
-javaagent:/opt/skywalking-agent/skywalking-agent.jar
-Dskywalking.agent.service_name=user-service
-Dskywalking.collector.backend_service=oap:11800
关键指标:
- QPS(每秒请求数)
- RT(响应时间)
- 错误率
- 系统负载
- JVM 指标
2. 性能测试
压测工具:
# JMeter
jmeter -n -t test.jmx -l result.jtl
# wrk
wrk -t12 -c400 -d30s http://localhost:8080/api/users
# ab
ab -n 10000 -c 100 http://localhost:8080/api/users
压测指标:
- 吞吐量(TPS/QPS)
- 响应时间(P50/P90/P95/P99)
- 错误率
- 资源使用率
3. 性能分析
JProfiler:
- CPU 分析
- 内存分析
- SQL 分析
- 线程分析
Arthas:
# 查看方法执行时间
trace com.example.UserService getUser
# 查看方法调用参数
watch com.example.UserService getUser '{params, returnObj}'
# 查看 JVM 信息
dashboard
# 查看线程
thread
# 查看堆内存
heap
最佳实践
1. 性能基线
建立基线:
- 定期性能测试
- 记录关键指标
- 对比历史数据
性能预算:
- P95 响应时间 < 500ms
- 错误率 < 0.1%
- CPU 使用率 < 70%
- 内存使用率 < 80%
2. 持续优化
优化循环:
监控 → 分析 → 优化 → 验证 → 监控
优化优先级:
- 数据库优化(影响最大)
- 缓存优化(效果明显)
- 代码优化(成本最低)
- 架构优化(长期收益)
3. 性能文化
性能意识:
- 代码 Review 关注性能
- 性能测试纳入 CI/CD
- 性能指标纳入考核
知识沉淀:
- 性能优化案例库
- 性能最佳实践文档
- 定期技术分享
总结
微服务性能优化是系统性工程,需要从 JVM、数据库、缓存、网络、代码、架构等多个维度进行优化。
建立完善的性能监控体系,基于数据驱动优化,持续迭代改进。
培养性能文化,将性能意识融入开发流程,建立性能最佳实践。