性能优化指南
作者:唐亚峰 | battcn
字数统计:1.5k 字
学习目标
掌握 Wemirr Platform 性能优化的方法和技巧
性能优化原则
┌─────────────────────────────────────────────────────────┐
│ 性能优化原则 │
├─────────────────────────────────────────────────────────┤
│ 1. 先测量,再优化(不要盲目优化) │
│ 2. 优化瓶颈点(二八法则) │
│ 3. 空间换时间(缓存) │
│ 4. 异步化、并行化 │
│ 5. 减少网络往返 │
└─────────────────────────────────────────────────────────┘数据库优化
索引优化
sql
-- 1. 为常用查询条件添加索引
CREATE INDEX idx_tenant_status ON t_order(tenant_id, status);
CREATE INDEX idx_create_time ON t_order(create_time);
-- 2. 覆盖索引(避免回表)
CREATE INDEX idx_cover ON t_order(tenant_id, status, order_no, amount);
-- 3. 前缀索引(大字段)
CREATE INDEX idx_name ON t_customer(name(10));
-- 4. 避免索引失效的情况
-- ❌ 函数操作
SELECT * FROM t_order WHERE DATE(create_time) = '2024-01-01';
-- ✅ 范围查询
SELECT * FROM t_order WHERE create_time >= '2024-01-01' AND create_time < '2024-01-02';
-- ❌ 隐式类型转换
SELECT * FROM t_order WHERE order_no = 123; -- order_no 是 varchar
-- ✅ 类型匹配
SELECT * FROM t_order WHERE order_no = '123';
-- ❌ 前缀模糊查询
SELECT * FROM t_order WHERE order_no LIKE '%123';
-- ✅ 后缀模糊查询(可用索引)
SELECT * FROM t_order WHERE order_no LIKE '123%';慢查询优化
sql
-- 开启慢查询日志
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;
-- 分析执行计划
EXPLAIN SELECT * FROM t_order WHERE tenant_id = 1 AND status = 1;
-- 优化建议
-- 1. 避免 SELECT *
SELECT id, order_no, amount FROM t_order WHERE ...;
-- 2. 分页优化(深度分页)
-- ❌ 大偏移量
SELECT * FROM t_order LIMIT 100000, 10;
-- ✅ 游标分页
SELECT * FROM t_order WHERE id > 100000 ORDER BY id LIMIT 10;
-- 3. 批量操作
-- ❌ 循环插入
for (Order order : orders) {
orderMapper.insert(order);
}
-- ✅ 批量插入
orderMapper.insertBatch(orders);分库分表
当单表数据量超过 1000W 时,考虑分库分表:
java
// 使用 ShardingSphere
// 按租户分库
spring.shardingsphere.rules.sharding.tables.t_order.actual-data-nodes=ds_$->{0..2}.t_order
// 按时间分表
spring.shardingsphere.rules.sharding.tables.t_order.table-strategy.standard.sharding-column=create_time
spring.shardingsphere.rules.sharding.tables.t_order.table-strategy.standard.sharding-algorithm-name=t_order_inline缓存优化
多级缓存架构
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 本地缓存 │ ──▶ │ Redis 缓存 │ ──▶ │ 数据库 │
│ (Caffeine) │ │ │ │ (MySQL) │
└─────────────┘ └─────────────┘ └─────────────┘
10μs 1-5ms 10-100ms本地缓存
java
@Configuration
public class CacheConfig {
@Bean
public Cache<String, Object> localCache() {
return Caffeine.newBuilder()
.maximumSize(10000)
.expireAfterWrite(5, TimeUnit.MINUTES)
.recordStats()
.build();
}
}
// 使用
@Service
@RequiredArgsConstructor
public class DictService {
private final Cache<String, Object> localCache;
private final StringRedisTemplate redisTemplate;
public List<DictItem> getByCode(String code) {
String key = "dict:" + code;
// 1. 查本地缓存
Object cached = localCache.getIfPresent(key);
if (cached != null) {
return (List<DictItem>) cached;
}
// 2. 查 Redis 缓存
String json = redisTemplate.opsForValue().get(key);
if (StrUtil.isNotBlank(json)) {
List<DictItem> items = JsonUtil.toList(json, DictItem.class);
localCache.put(key, items);
return items;
}
// 3. 查数据库
List<DictItem> items = dictMapper.selectByCode(code);
redisTemplate.opsForValue().set(key, JsonUtil.toJson(items), 1, TimeUnit.HOURS);
localCache.put(key, items);
return items;
}
}缓存一致性
java
// 更新数据时删除缓存
@Transactional(rollbackFor = Exception.class)
@CacheEvict(value = "dict", key = "#dto.code")
public void update(DictDTO dto) {
dictMapper.updateById(dto);
// 通知其他节点清除本地缓存
redisTemplate.convertAndSend("cache:evict", "dict:" + dto.code);
}
// 监听缓存清除消息
@Component
@RequiredArgsConstructor
public class CacheEvictListener {
private final Cache<String, Object> localCache;
@PostConstruct
public void subscribe() {
redisTemplate.getConnectionFactory().getConnection()
.subscribe((message, pattern) -> {
String key = new String(message.getBody());
localCache.invalidate(key);
}, "cache:evict".getBytes());
}
}缓存穿透防护
java
// 1. 布隆过滤器
@Service
public class UserService {
private final BloomFilter<Long> userIdFilter;
public User getById(Long id) {
// 先检查布隆过滤器
if (!userIdFilter.mightContain(id)) {
return null;
}
// 继续查询
return userMapper.selectById(id);
}
}
// 2. 缓存空值
public User getById(Long id) {
String key = "user:" + id;
String cached = redisTemplate.opsForValue().get(key);
if ("NULL".equals(cached)) {
return null; // 缓存的空值
}
if (cached != null) {
return JsonUtil.toObject(cached, User.class);
}
User user = userMapper.selectById(id);
if (user == null) {
// 缓存空值,较短过期时间
redisTemplate.opsForValue().set(key, "NULL", 5, TimeUnit.MINUTES);
} else {
redisTemplate.opsForValue().set(key, JsonUtil.toJson(user), 1, TimeUnit.HOURS);
}
return user;
}接口优化
批量处理
java
// ❌ 循环调用
for (Long userId : userIds) {
User user = userService.getById(userId);
// ...
}
// ✅ 批量查询
List<User> users = userService.listByIds(userIds);
Map<Long, User> userMap = users.stream()
.collect(Collectors.toMap(User::getId, Function.identity()));并行处理
java
@Service
public class OrderDetailService {
public OrderDetailVO getOrderDetail(Long orderId) {
OrderDetailVO vo = new OrderDetailVO();
// 并行获取数据
CompletableFuture<Order> orderFuture = CompletableFuture.supplyAsync(
() -> orderMapper.selectById(orderId)
);
CompletableFuture<List<OrderItem>> itemsFuture = CompletableFuture.supplyAsync(
() -> orderItemMapper.selectByOrderId(orderId)
);
CompletableFuture<User> userFuture = orderFuture.thenCompose(
order -> CompletableFuture.supplyAsync(
() -> userMapper.selectById(order.getUserId())
)
);
// 等待所有完成
CompletableFuture.allOf(orderFuture, itemsFuture, userFuture).join();
// 组装结果
vo.setOrder(orderFuture.join());
vo.setItems(itemsFuture.join());
vo.setUser(userFuture.join());
return vo;
}
}异步处理
java
@Service
public class OrderService {
@Async
public void sendOrderNotification(Order order) {
// 发送邮件
emailService.send(order.getEmail(), "订单创建成功", "...");
// 发送短信
smsService.send(order.getMobile(), "...");
// 推送消息
pushService.push(order.getUserId(), "...");
}
@Transactional
public Order createOrder(OrderDTO dto) {
Order order = doCreateOrder(dto);
// 异步发送通知,不阻塞主流程
sendOrderNotification(order);
return order;
}
}接口响应压缩
yaml
# application.yml
server:
compression:
enabled: true
mime-types: application/json,application/xml,text/html,text/plain
min-response-size: 1024微服务配置优化
优雅停机与虚拟线程
yaml
server:
shutdown: graceful # 开启优雅停机
spring:
threads:
virtual:
enabled: true # 开启全局虚拟线程(JDK 21+)
reactor:
context-propagation: auto # 上下文自动传播
lifecycle:
timeout-per-shutdown-phase: 30s # 给足时间处理旧请求Feign 与负载均衡配置
yaml
spring:
cloud:
openfeign:
httpclient:
hc5:
enabled: true # 使用 Apache HttpClient 5
client:
config:
default: # 全局配置
connect-timeout: 1000 # 连接超时 1s(K8s 内部超 1s 通常 IP 已死)
read-timeout: 60000 # 读取超时 60s
loadbalancer:
nacos:
enabled: true # 使用 Nacos 负载均衡
retry:
enabled: true # 开启重试
max-retries-on-same-service-instance: 0 # 不在同一个坏 IP 上死磕
max-retries-on-next-service-instance: 2 # 切换到下一个实例重试 2 次
retryable-status-codes: 503, 500 # 针对哪些状态码重试监控与链路追踪
yaml
management:
tracing:
enabled: true
sampling:
probability: 1.0 # 采样率 100%(生产可调低)
propagation:
type: w3c, b3 # 支持 W3C 和 B3 格式
baggage:
remote-fields: x-request-id
correlation:
fields: x-request-id
endpoints:
web:
exposure:
include: "health,info,prometheus" # 只暴露必要端点JVM 优化
内存配置
bash
# 生产环境推荐配置
JAVA_OPTS="-Xms2g -Xmx2g \
-XX:+UseG1GC \
-XX:MaxGCPauseMillis=200 \
-XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=/app/logs/heapdump.hprof \
-Xlog:gc*:file=/app/logs/gc.log:time,uptime:filecount=5,filesize=10M"GC 调优
bash
# G1 收集器(推荐)
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:G1HeapRegionSize=16m
# 大堆内存场景
-XX:+UseZGC # JDK 17+监控与诊断
接口性能监控
java
@Aspect
@Component
@Slf4j
public class PerformanceAspect {
@Around("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
public Object around(ProceedingJoinPoint point) throws Throwable {
long start = System.currentTimeMillis();
String method = point.getSignature().toShortString();
try {
return point.proceed();
} finally {
long cost = System.currentTimeMillis() - start;
if (cost > 1000) {
log.warn("慢接口: {} 耗时: {}ms", method, cost);
} else {
log.debug("接口: {} 耗时: {}ms", method, cost);
}
}
}
}性能指标采集
yaml
# Prometheus 指标
management:
endpoints:
web:
exposure:
include: prometheus,health,info
metrics:
tags:
application: ${spring.application.name}性能检查清单
| 检查项 | 标准 |
|---|---|
| 接口响应时间 | P99 < 500ms |
| 数据库查询 | 单次 < 100ms |
| 缓存命中率 | > 90% |
| CPU 使用率 | < 70% |
| 内存使用率 | < 80% |
| GC 停顿时间 | < 200ms |
