⚡ 性能优化实战:从JVM到数据库的全链路优化
"过早的优化是万恶之源,但适时的优化是成功之本。"
🎯 业务场景:电商大促性能危机
📊 项目背景
双11前夕,电商系统面临性能瓶颈,急需全链路优化:
性能指标 | 当前状态 | 目标状态 | 业务影响 |
---|---|---|---|
接口响应时间 | 3000ms | 200ms | 用户体验差,转化率低 |
系统吞吐量 | 1000 QPS | 10000 QPS | 无法支撑大促流量 |
数据库连接 | 频繁超时 | 稳定可用 | 订单创建失败 |
内存使用率 | 85% | 60% | 频繁GC,系统卡顿 |
🔧 JVM性能优化实战
💡 JVM调优核心原理
1. 内存结构优化
❌ 优化前:默认JVM配置
bash
# 默认启动参数 - 性能差
java -jar ecommerce-app.jar
# 问题分析
jstat -gc 12345 1s
# S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
# 512.0 512.0 0.0 48.0 4096.0 2048.0 10240.0 8192.0 21248.0 20480.0 2560.0 2304.0 156 0.780 12 2.100 2.880
这是 `jstat -gc` 命令的输出,显示了 Java 虚拟机(JVM)的垃圾回收(GC)相关的内存和性能统计信息。让我们逐列分析这些数据:
### 各列含义:
1. **S0C** (Survivor 0 Capacity): 512.0 KB - Survivor 区 0 的总容量
2. **S1C** (Survivor 1 Capacity): 512.0 KB - Survivor 区 1 的总容量
3. **S0U** (Survivor 0 Used): 0.0 KB - Survivor 区 0 已使用量
4. **S1U** (Survivor 1 Used): 48.0 KB - Survivor 区 1 已使用量
5. **EC** (Eden Capacity): 4096.0 KB (4MB) - Eden 区总容量
6. **EU** (Eden Used): 2048.0 KB (2MB) - Eden 区已使用量
7. **OC** (Old Capacity): 10240.0 KB (10MB) - 老年代总容量
8. **OU** (Old Used): 8192.0 KB (8MB) - 老年代已使用量
9. **MC** (Metaspace Capacity): 21248.0 KB (~20.75MB) - 元空间总容量
10. **MU** (Metaspace Used): 20480.0 KB (20MB) - 元空间已使用量
11. **CCSC** (Compressed Class Space Capacity): 2560.0 KB (2.5MB) - 压缩类空间总容量
12. **CCSU** (Compressed Class Space Used): 2304.0 KB (~2.25MB) - 压缩类空间已使用量
13. **YGC** (Young GC Count): 156 - 年轻代 GC 发生次数
14. **YGCT** (Young GC Time): 0.780 秒 - 年轻代 GC 总耗时
15. **FGC** (Full GC Count): 12 - Full GC 发生次数
16. **FGCT** (Full GC Time): 2.100 秒 - Full GC 总耗时
17. **GCT** (Total GC Time): 2.880 秒 - 所有 GC 总耗时(YGCT + FGCT)
### 关键观察:
1. **内存使用**:
- Eden 区使用率为 50% (2048/4096)
- 老年代使用率为 80% (8192/10240),较高
- 元空间使用率为 ~96% (20480/21248),接近上限
2. **GC 情况**:
- 年轻代 GC 平均耗时:0.780/156 ≈ 0.005 秒/次
- Full GC 平均耗时:2.100/12 ≈ 0.175 秒/次
- Full GC 频率较高(平均每 13 次年轻代 GC 就有 1 次 Full GC)
### 潜在问题:
1. **老年代使用率过高**(80%)可能导致频繁 Full GC
2. **元空间接近满载**(96%)可能引发 Full GC
3. **Full GC 频率较高**(12 次)且单次耗时长(平均 175ms)
✅ 优化后:精细化JVM配置
bash
# JVM优化参数
java -server \
-Xms8g -Xmx8g \ # 堆内存8G,避免动态扩容
-Xmn3g \ # 年轻代3G,减少Minor GC频率
-XX:MetaspaceSize=256m \ # 元空间初始大小
-XX:MaxMetaspaceSize=256m \ # 元空间最大大小
-XX:+UseG1GC \ # 使用G1垃圾收集器
-XX:MaxGCPauseMillis=200 \ # GC暂停时间目标200ms
-XX:+PrintGC \ # 打印GC日志
-XX:+PrintGCDetails \ # 详细GC信息
-XX:+PrintGCTimeStamps \ # GC时间戳
-Xloggc:/var/log/gc.log \ # GC日志文件
-XX:+HeapDumpOnOutOfMemoryError \ # OOM时堆转储
-XX:HeapDumpPath=/var/log/java_heapdump.hprof \ # OOM时堆转储路径
-jar ecommerce-app.jar
2. 垃圾收集器选择
java
// GC性能测试工具
@Component
public class GCPerformanceAnalyzer {
private final MeterRegistry meterRegistry;
public GCPerformanceAnalyzer(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
}
@EventListener
public void onGCEvent(GCEvent event) {
// 记录GC指标
Timer.Sample sample = Timer.start(meterRegistry);
sample.stop(Timer.builder("gc.duration")
.tag("collector", event.getCollectorName())
.tag("generation", event.getGeneration())
.register(meterRegistry));
// 记录GC前后内存使用
meterRegistry.gauge("memory.before.gc", event.getMemoryUsedBefore());
meterRegistry.gauge("memory.after.gc", event.getMemoryUsedAfter());
// 计算GC效率
double gcEfficiency = (double)(event.getMemoryUsedBefore() - event.getMemoryUsedAfter())
/ event.getMemoryUsedBefore() * 100;
meterRegistry.gauge("gc.efficiency", gcEfficiency);
}
}
3. 内存泄漏检测与修复
❌ 问题代码:内存泄漏
java
// 内存泄漏示例
@Service
public class OrderService {
// 问题1:静态集合持续增长
private static final Map<String, Order> ORDER_CACHE = new HashMap<>();
// 问题2:监听器未正确移除
private final List<OrderListener> listeners = new ArrayList<>();
// 问题3:线程池未正确关闭
private final ExecutorService executor = Executors.newFixedThreadPool(10);
public void processOrder(Order order) {
// 缓存订单但从不清理
ORDER_CACHE.put(order.getId(), order);
// 添加监听器但从不移除
listeners.add(new OrderListener() {
@Override
public void onOrderProcessed(Order order) {
// 处理逻辑
}
});
// 提交任务到线程池
executor.submit(() -> {
// 异步处理订单
handleOrderAsync(order);
});
}
}
✅ 修复后:内存安全
java
@Service
public class OrderService {
// 使用有界缓存,自动过期
private final Cache<String, Order> orderCache = Caffeine.newBuilder()
.maximumSize(10000)
.expireAfterWrite(Duration.ofMinutes(30))
.removalListener((key, value, cause) -> {
log.info("订单缓存移除: {}, 原因: {}", key, cause);
})
.build();
// 使用弱引用避免内存泄漏
private final Set<OrderListener> listeners = Collections.newSetFromMap(
new WeakHashMap<>());
// 使用Spring管理的线程池
@Autowired
@Qualifier("orderProcessingExecutor")
private TaskExecutor taskExecutor;
public void processOrder(Order order) {
// 安全的缓存操作
orderCache.put(order.getId(), order);
// 通知监听器
listeners.forEach(listener -> {
try {
listener.onOrderProcessed(order);
} catch (Exception e) {
log.warn("监听器处理失败", e);
}
});
// 使用托管线程池
taskExecutor.execute(() -> handleOrderAsync(order));
}
public void addListener(OrderListener listener) {
listeners.add(listener);
}
public void removeListener(OrderListener listener) {
listeners.remove(listener);
}
// 内存使用监控
@Scheduled(fixedRate = 60000)
public void monitorMemoryUsage() {
MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
long used = heapUsage.getUsed();
long max = heapUsage.getMax();
double usagePercent = (double) used / max * 100;
log.info("堆内存使用率: {:.2f}%, 缓存大小: {}",
usagePercent, orderCache.estimatedSize());
if (usagePercent > 80) {
log.warn("内存使用率过高,触发缓存清理");
orderCache.invalidateAll();
}
}
}
🗄️ 数据库性能优化实战
💡 SQL查询优化
1. 慢查询识别与优化
❌ 问题SQL:性能低下
sql
-- 慢查询示例:订单查询(执行时间:2.5s)
SELECT o.*, u.name as user_name, p.name as product_name
FROM orders o
LEFT JOIN users u ON o.user_id = u.id
LEFT JOIN order_items oi ON o.id = oi.order_id
LEFT JOIN products p ON oi.product_id = p.id
WHERE o.create_time >= '2023-01-01'
AND o.status = 'COMPLETED'
AND u.level = 'VIP'
ORDER BY o.create_time DESC
LIMIT 20;
-- 执行计划分析
--+-----------+-----+----------+------+-------------+-------+-------+----------------------------+----+--------+--------------------------------------------+
id|select_type|table|partitions|type |possible_keys|key |key_len|ref |rows|filtered|Extra |
--+-----------+-----+----------+------+-------------+-------+-------+----------------------------+----+--------+--------------------------------------------+
1|SIMPLE |o | |ALL | | | | | 4| 25.0|Using where; Using temporary; Using filesort|
1|SIMPLE |u | |eq_ref|PRIMARY |PRIMARY|4 |daily_discover.o.user_id | 1| 50.0|Using where |
1|SIMPLE |oi | |ALL | | | | | 6| 100.0|Using where; Using join buffer (hash join) |
1|SIMPLE |p | |eq_ref|PRIMARY |PRIMARY|4 |daily_discover.oi.product_id| 1| 100.0| |
--+-----------+-----+----------+------+-------------+-------+-------+----------------------------+----+--------+--------------------------------------------+
-- 读懂 `EXPLAIN` 执行计划:
### **关键列的含义**
1. **`id`**:
- 查询的标识符。相同的 `id` 表示同一层级的查询,不同的 `id` 表示子查询或派生表。
- 如果有子查询,`id` 会递增。
2. **`select_type`**:
- **`SIMPLE`**:简单查询,没有子查询或派生表。
- **`PRIMARY`**:最外层的查询。
- **`SUBQUERY`**:子查询。
- **`DERIVED`**:派生表(子查询的结果被当作一个表)。
- **`DEPENDENT SUBQUERY`**:依赖子查询,子查询的结果依赖于外层查询的结果。
3. **`table`**:
- 当前行正在访问的表名。
4. **`type`**:
- **`ALL`**:全表扫描,性能最差。
- **`index`**:全索引扫描,性能较好。
- **`range`**:索引范围扫描,性能较好。
- **`ref`**:使用索引的单值查找,性能较好。
- **`eq_ref`**:使用索引的唯一值查找,性能更好。
5. **`possible_keys`**:
- 可能使用的索引。
6. **`key`**:
- 实际使用的索引。如果为空,表示没有使用索引。
7. **`key_len`**:
- 使用的索引长度。越短越好。
8. **`ref`**:
- 索引的比较值。如果是 `const`,表示常量值;如果是表的字段,表示通过字段值进行比较。
9. **`rows`**:
- 需要扫描的行数。越少越好。
10. **`filtered`**:
- 过滤后的行数比例。越接近 100% 表示过滤效果越好。
11. **`Extra`**:
- **`Using where`**:需要额外的过滤操作。
- **`Using temporary`**:需要创建临时表,性能较差。
- **`Using filesort`**:需要额外的排序操作,性能较差。
- **`Using index`**:只使用索引,不访问表数据,性能较好。
- **`Using join buffer (hash join)`**:使用了哈希连接,性能较差。
-- 问题分析:
1. 全表扫描问题
- **orders表**:`type=ALL`,未使用任何索引(`key=NULL`),扫描全部4行(实际可能更多)
- **order_items表**:`type=ALL`,同样未使用索引,扫描6行
- **风险**:随着数据量增长,性能会急剧下降(500万行数据时扫描500万行)
2. 索引缺失
- `keys`列显示没有可用索引
- 特别是orders表的status和create_time条件没有索引支持
3. 过滤效率低
- `filtered=25.0`(orders表):表示只有25%的行满足条件
- `filtered=50.0`(users表):只有50%的行满足VIP条件
4. 临时表和文件排序
- `Extra: Using temporary; Using filesort`
- 表示MySQL需要创建临时表并对结果进行排序
- **性能影响**:消耗大量内存和CPU资源,数据量大时可能使用磁盘临时文件
5. 低效的连接方式
- `Using join buffer (hash join)`:表示无法使用索引连接,使用内存缓冲
- **问题**:当连接大表时,hash join会消耗大量内存
✅ 优化后:高性能查询
sql
-- 第一步:创建复合索引
CREATE INDEX idx_orders_status_time ON orders(status, create_time);
CREATE INDEX idx_users_level ON users(level);
CREATE INDEX idx_order_items_order_id ON order_items(order_id);
CREATE INDEX idx_products_id ON products(id);
-- 第二步:优化查询语句
SELECT
o.id,
o.order_number,
o.total_amount,
o.create_time,
u.name AS user_name,
(
SELECT GROUP_CONCAT(DISTINCT p.name SEPARATOR ', ')
FROM order_items oi
JOIN products p ON oi.product_id = p.id
WHERE oi.order_id = o.id
LIMIT 50
) AS product_names
FROM (
SELECT id, user_id, order_number, total_amount, create_time
FROM orders
WHERE status = 'COMPLETED'
AND create_time >= '2023-01-01'
ORDER BY create_time DESC
LIMIT 20
) o
JOIN users u ON o.user_id = u.id AND u.level = 'VIP';
-- 优化后执行计划
--+------------------+----------+----------+------+------------------------+------------------------+-------+----------------------------+----+--------+------------------------------------------+
id|select_type |table |partitions|type |possible_keys |key |key_len|ref |rows|filtered|Extra |
--+------------------+----------+----------+------+------------------------+------------------------+-------+----------------------------+----+--------+------------------------------------------+
1|PRIMARY |u | |ref |PRIMARY,idx_users_level |idx_users_level |1 |const | 2| 100.0| |
1|PRIMARY |<derived3>| |ref |<auto_key0> |<auto_key0> |4 |daily_discover.u.id | 2| 100.0| |
3|DERIVED |orders | |range |idx_orders_status_time |idx_orders_status_time |6 | | 2| 100.0|Using index condition; Backward index scan|
2|DEPENDENT SUBQUERY|oi | |ref |idx_order_items_order_id|idx_order_items_order_id|4 |o.id | 1| 100.0| |
2|DEPENDENT SUBQUERY|p | |eq_ref|PRIMARY,idx_products_id |PRIMARY |4 |daily_discover.oi.product_id| 1| 100.0| |
--+------------------+----------+----------+------+------------------------+------------------------+-------+----------------------------+----+--------+------------------------------------------+
-- 优化效果:
| 指标 | 优化前 | 优化后 | 提升幅度 |
|-------------|-------------------------------|--------------------------------|----------------|
| 扫描方式 | 全表扫描(ALL) | 索引扫描(range/ref) | 1000倍+ |
| 临时表/排序 | 需要临时表和文件排序 | 完全消除 | 100% |
| 扫描行数 | 预估12行(实际更大) | 最大6行 | 减少50%+ |
| 执行复杂度 | O(N)线性增长 | O(logN)对数增长 | 指数级提升 |
| 内存消耗 | 高(临时表+join buffer) | 低(仅索引访问) | 减少90%+ |
| 查询稳定性 | 随数据量增加急剧下降 | 大数据量下仍稳定 | 根本性改善 |
-- Explain优化总结:
|----------------------------------------------------------------|
1. **避免全表扫描(`ALL`)**。
2. **确保使用索引(`key`)且 (`key_len`)越短越好**。
2. **减少扫描行数(`rows`), (`filtered`)100最好**。
3. **避免临时表(`Using temporary`)和文件排序(`Using filesort`)**。
|----------------------------------------------------------------|
3. 批量操作优化
❌ 问题:逐条插入
java
// 性能差的批量插入
@Service
public class OrderService {
public void batchCreateOrders(List<Order> orders) {
for (Order order : orders) {
// 每次都是一个数据库往返
orderRepository.save(order);
}
// 1000条订单需要1000次数据库调用,耗时10s
}
}
✅ 优化:真正的批量操作
java
@Service
public class OrderService {
@Autowired
private JdbcTemplate jdbcTemplate;
public void batchCreateOrders(List<Order> orders) {
String sql = "INSERT INTO orders (order_number, user_id, total_amount, status, create_time) VALUES (?, ?, ?, ?, ?)";
List<Object[]> batchArgs = orders.stream()
.map(order -> new Object[]{
order.getOrderNumber(),
order.getUserId(),
order.getTotalAmount(),
order.getStatus().name(),
order.getCreateTime()
})
.collect(Collectors.toList());
// 批量插入,一次数据库调用
jdbcTemplate.batchUpdate(sql, batchArgs);
// 1000条订单只需要1次数据库调用,耗时200ms
}
// 分批处理大量数据
public void batchCreateOrdersInChunks(List<Order> orders) {
int batchSize = 1000;
for (int i = 0; i < orders.size(); i += batchSize) {
int end = Math.min(i + batchSize, orders.size());
List<Order> batch = orders.subList(i, end);
batchCreateOrders(batch);
// 避免长事务,分批提交
if (i % (batchSize * 10) == 0) {
log.info("已处理 {} 条订单", i);
}
}
}
}
🔧 数据库连接池优化
1. 连接池配置优化
❌ 问题配置:连接池设置不当
yaml
# 默认配置 - 性能差
spring:
datasource:
hikari:
maximum-pool-size: 10 # 连接池太小
minimum-idle: 5 # 最小空闲连接
connection-timeout: 30000 # 连接超时30s
idle-timeout: 600000 # 空闲超时10分钟
max-lifetime: 1800000 # 最大生命周期30分钟
# 问题:
# 1. 连接池大小不足,高并发时连接等待
# 2. 连接超时时间过长,影响用户体验
# 3. 连接生命周期设置不合理
✅ 优化配置:精细化调优
yaml
# 生产环境优化配置
spring:
datasource:
# 1. 基础连接信息
url: jdbc:mysql://your-mysql-host:3306/your_database?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
username: your_username
password: your_password
driver-class-name: com.mysql.cj.jdbc.Driver
# 2. HikariCP 连接池配置(专为MySQL优化)
hikari:
pool-name: MySQL-HikariCP # 连接池名称(监控用)
# 核心连接池大小(根据实际并发调整)
maximum-pool-size: 50 # 最大连接数(建议 = CPU核心数 * 2 + 磁盘数)
minimum-idle: 10 # 最小空闲连接(避免冷启动延迟)
# 超时控制(MySQL默认wait_timeout=28800秒)
connection-timeout: 3000 # 获取连接超时3秒(快速失败)
idle-timeout: 300000 # 空闲连接超时5分钟(< MySQL的wait_timeout)
max-lifetime: 1800000 # 连接最大存活30分钟(防止长时间占用)
# MySQL性能优化参数
data-source-properties:
cachePrepStmts: true # 启用预编译语句缓存
prepStmtCacheSize: 250 # 预编译缓存大小
prepStmtCacheSqlLimit: 2048 # 单条SQL缓存大小
useServerPrepStmts: true # 使用服务端预编译
useLocalSessionState: true # 本地会话状态管理
rewriteBatchedStatements: true # 批量操作优化(INSERT批量插入关键!)
cacheResultSetMetadata: true # 结果集元数据缓存
cacheServerConfiguration: true # 服务端配置缓存
maintainTimeStats: false # 关闭Hikari内部时间统计(提升性能)
# 监控和调试
leak-detection-threshold: 60000 # 连接泄漏检测(60秒)
register-mbeans: true # 启用JMX监控
2. 连接池监控
java
@Component
public class ConnectionPoolMonitor {
private final HikariDataSource dataSource;
private final MeterRegistry meterRegistry;
public ConnectionPoolMonitor(DataSource dataSource, MeterRegistry meterRegistry) {
this.dataSource = (HikariDataSource) dataSource;
this.meterRegistry = meterRegistry;
}
@Scheduled(fixedRate = 30000) // 每30秒监控一次
public void monitorConnectionPool() {
HikariPoolMXBean poolBean = dataSource.getHikariPoolMXBean();
// 记录连接池指标
meterRegistry.gauge("hikari.connections.active", poolBean.getActiveConnections());
meterRegistry.gauge("hikari.connections.idle", poolBean.getIdleConnections());
meterRegistry.gauge("hikari.connections.total", poolBean.getTotalConnections());
meterRegistry.gauge("hikari.connections.pending", poolBean.getThreadsAwaitingConnection());
// 连接池健康检查
if (poolBean.getActiveConnections() > poolBean.getMaximumPoolSize() * 0.8) {
log.warn("连接池使用率过高: {}/{}",
poolBean.getActiveConnections(), poolBean.getMaximumPoolSize());
}
if (poolBean.getThreadsAwaitingConnection() > 0) {
log.warn("有 {} 个线程在等待数据库连接", poolBean.getThreadsAwaitingConnection());
}
}
}
🚀 缓存策略优化
💡 多级缓存架构
1. 缓存层次设计
java
// 多级缓存架构
@Service
public class ProductService {
// L1缓存:本地缓存(Caffeine)
private final Cache<String, Product> localCache = Caffeine.newBuilder()
.maximumSize(1000)
.expireAfterWrite(Duration.ofMinutes(5))
.build();
// L2缓存:分布式缓存(Redis)
@Autowired
private RedisTemplate<String, Product> redisTemplate;
// L3缓存:数据库
@Autowired
private ProductRepository productRepository;
public Product getProduct(String productId) {
// L1缓存查询
Product product = localCache.getIfPresent(productId);
if (product != null) {
log.debug("L1缓存命中: {}", productId);
return product;
}
// L2缓存查询
String redisKey = "product:" + productId;
product = redisTemplate.opsForValue().get(redisKey);
if (product != null) {
log.debug("L2缓存命中: {}", productId);
// 回填L1缓存
localCache.put(productId, product);
return product;
}
// L3数据库查询
product = productRepository.findById(productId)
.orElseThrow(() -> new ProductNotFoundException("产品不存在: " + productId));
log.debug("数据库查询: {}", productId);
// 回填缓存
redisTemplate.opsForValue().set(redisKey, product, Duration.ofHours(1));
localCache.put(productId, product);
return product;
}
// 缓存更新策略
public void updateProduct(Product product) {
// 更新数据库
productRepository.save(product);
// 删除缓存(Cache-Aside模式)
String redisKey = "product:" + product.getId();
redisTemplate.delete(redisKey);
localCache.invalidate(product.getId());
log.info("产品更新并清除缓存: {}", product.getId());
}
}
2. 缓存预热策略
java
@Component
public class CacheWarmupService {
@Autowired
private ProductService productService;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
// 应用启动时预热缓存
@EventListener(ApplicationReadyEvent.class)
public void warmupCache() {
log.info("开始缓存预热...");
CompletableFuture.runAsync(() -> {
try {
warmupHotProducts();
warmupUserSessions();
warmupConfigData();
log.info("缓存预热完成");
} catch (Exception e) {
log.error("缓存预热失败", e);
}
});
}
private void warmupHotProducts() {
// 预热热门商品
List<String> hotProductIds = getHotProductIds();
hotProductIds.parallelStream().forEach(productId -> {
try {
productService.getProduct(productId);
Thread.sleep(10); // 避免数据库压力过大
} catch (Exception e) {
log.warn("预热商品失败: {}", productId, e);
}
});
log.info("热门商品预热完成,共 {} 个", hotProductIds.size());
}
private void warmupUserSessions() {
// 预热活跃用户会话
Set<String> activeUserIds = getActiveUserIds();
activeUserIds.forEach(userId -> {
String sessionKey = "user:session:" + userId;
// 预加载用户会话数据
UserSession session = buildUserSession(userId);
redisTemplate.opsForValue().set(sessionKey, session, Duration.ofHours(2));
});
log.info("用户会话预热完成,共 {} 个", activeUserIds.size());
}
private void warmupConfigData() {
// 预热配置数据
Map<String, Object> configs = loadSystemConfigs();
configs.forEach((key, value) -> {
redisTemplate.opsForValue().set("config:" + key, value, Duration.ofDays(1));
});
log.info("配置数据预热完成,共 {} 项", configs.size());
}
}
3. 缓存穿透防护
java
@Service
public class ProductService {
// 布隆过滤器防止缓存穿透
private final BloomFilter<String> productBloomFilter = BloomFilter.create(
Funnels.stringFunnel(Charset.defaultCharset()),
1000000, // 预期元素数量
0.01 // 误判率1%
);
@PostConstruct
public void initBloomFilter() {
// 初始化布隆过滤器
List<String> allProductIds = productRepository.findAllProductIds();
allProductIds.forEach(productBloomFilter::put);
log.info("布隆过滤器初始化完成,包含 {} 个商品ID", allProductIds.size());
}
public Product getProduct(String productId) {
// 布隆过滤器快速判断
if (!productBloomFilter.mightContain(productId)) {
log.debug("布隆过滤器判断商品不存在: {}", productId);
throw new ProductNotFoundException("商品不存在: " + productId);
}
// 查询缓存
String cacheKey = "product:" + productId;
Product product = (Product) redisTemplate.opsForValue().get(cacheKey);
if (product != null) {
return product;
}
// 防止缓存击穿的分布式锁
String lockKey = "lock:product:" + productId;
Boolean lockAcquired = redisTemplate.opsForValue()
.setIfAbsent(lockKey, "1", Duration.ofSeconds(10));
if (Boolean.TRUE.equals(lockAcquired)) {
try {
// 双重检查
product = (Product) redisTemplate.opsForValue().get(cacheKey);
if (product == null) {
// 查询数据库
product = productRepository.findById(productId).orElse(null);
if (product != null) {
// 缓存数据
redisTemplate.opsForValue().set(cacheKey, product, Duration.ofHours(1));
} else {
// 缓存空值防止缓存穿透
redisTemplate.opsForValue().set(cacheKey, "NULL", Duration.ofMinutes(5));
throw new ProductNotFoundException("商品不存在: " + productId);
}
}
} finally {
redisTemplate.delete(lockKey);
}
} else {
// 等待其他线程查询完成
Thread.sleep(50);
return getProduct(productId);
}
return product;
}
}
🌐 网络与I/O优化
💡 HTTP连接优化
1. 连接池配置
java
// HTTP客户端连接池优化
@Configuration
public class HttpClientConfig {
@Bean
public RestTemplate restTemplate() {
// 创建连接池管理器
PoolingHttpClientConnectionManager connectionManager =
new PoolingHttpClientConnectionManager();
// 设置连接池参数
connectionManager.setMaxTotal(200); // 最大连接数
connectionManager.setDefaultMaxPerRoute(50); // 每个路由最大连接数
connectionManager.setValidateAfterInactivity(2000); // 连接空闲2s后验证
// 创建HTTP客户端
CloseableHttpClient httpClient = HttpClients.custom()
.setConnectionManager(connectionManager)
.setConnectionTimeToLive(30, TimeUnit.SECONDS) // 连接生存时间
.setDefaultRequestConfig(RequestConfig.custom()
.setConnectTimeout(3000) // 连接超时3s
.setSocketTimeout(5000) // 读取超时5s
.setConnectionRequestTimeout(1000) // 从连接池获取连接超时1s
.build())
.build();
// 配置RestTemplate
HttpComponentsClientHttpRequestFactory factory =
new HttpComponentsClientHttpRequestFactory(httpClient);
RestTemplate restTemplate = new RestTemplate(factory);
// 添加拦截器
restTemplate.getInterceptors().add(new LoggingInterceptor());
restTemplate.getInterceptors().add(new RetryInterceptor());
return restTemplate;
}
// 连接池监控
@Bean
public HttpConnectionPoolMonitor connectionPoolMonitor(
PoolingHttpClientConnectionManager connectionManager) {
return new HttpConnectionPoolMonitor(connectionManager);
}
}
@Component
public class HttpConnectionPoolMonitor {
private final PoolingHttpClientConnectionManager connectionManager;
public HttpConnectionPoolMonitor(PoolingHttpClientConnectionManager connectionManager) {
this.connectionManager = connectionManager;
}
@Scheduled(fixedRate = 30000)
public void monitorConnectionPool() {
PoolStats totalStats = connectionManager.getTotalStats();
log.info("HTTP连接池状态 - 总连接数: {}, 可用: {}, 租借: {}, 等待: {}",
totalStats.getMax(),
totalStats.getAvailable(),
totalStats.getLeased(),
totalStats.getPending());
// 清理过期连接
connectionManager.closeExpiredConnections();
connectionManager.closeIdleConnections(30, TimeUnit.SECONDS);
}
}
2. 异步处理优化
java
// 异步处理提升并发能力
@Service
public class OrderService {
@Autowired
@Qualifier("orderProcessingExecutor")
private TaskExecutor taskExecutor;
@Autowired
private CompletableFutureService completableFutureService;
// 异步订单处理
@Async("orderProcessingExecutor")
public CompletableFuture<OrderResult> processOrderAsync(CreateOrderRequest request) {
try {
// 并行执行多个任务
CompletableFuture<User> userFuture = CompletableFuture
.supplyAsync(() -> userService.getUser(request.getUserId()), taskExecutor);
CompletableFuture<Product> productFuture = CompletableFuture
.supplyAsync(() -> productService.getProduct(request.getProductId()), taskExecutor);
CompletableFuture<Boolean> inventoryFuture = CompletableFuture
.supplyAsync(() -> inventoryService.checkStock(request.getProductId(), request.getQuantity()), taskExecutor);
// 等待所有任务完成
CompletableFuture<Void> allTasks = CompletableFuture.allOf(
userFuture, productFuture, inventoryFuture);
return allTasks.thenApply(v -> {
User user = userFuture.join();
Product product = productFuture.join();
Boolean stockAvailable = inventoryFuture.join();
if (!stockAvailable) {
throw new InsufficientStockException("库存不足");
}
// 创建订单
Order order = createOrder(request, user, product);
return OrderResult.success(order);
});
} catch (Exception e) {
return CompletableFuture.completedFuture(OrderResult.failure(e.getMessage()));
}
}
// 批量异步处理
public List<CompletableFuture<OrderResult>> batchProcessOrders(List<CreateOrderRequest> requests) {
return requests.stream()
.map(this::processOrderAsync)
.collect(Collectors.toList());
}
}
// 线程池配置
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean("orderProcessingExecutor")
public TaskExecutor orderProcessingExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 核心线程数 = CPU核心数
executor.setCorePoolSize(Runtime.getRuntime().availableProcessors());
// 最大线程数 = CPU核心数 * 2
executor.setMaxPoolSize(Runtime.getRuntime().availableProcessors() * 2);
// 队列容量
executor.setQueueCapacity(1000);
// 线程名前缀
executor.setThreadNamePrefix("OrderProcessing-");
// 拒绝策略:调用者运行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 等待任务完成后关闭
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setAwaitTerminationSeconds(60);
executor.initialize();
return executor;
}
}
🎉 总结:性能优化的系统方法论
🚀 优化路径
第一阶段:基础优化(立竿见影)
- ✅ JVM参数调优
- ✅ 数据库索引优化
- ✅ 连接池配置
- ✅ 缓存策略
第二阶段:架构优化(中长期)
- ✅ 读写分离
- ✅ 分库分表
- ✅ 微服务拆分
- ✅ 异步处理
第三阶段:深度优化(持续改进)
- ✅ 算法优化
- ✅ 数据结构优化
- ✅ 业务逻辑优化
- ✅ 硬件升级
📈 优化维度总结
- 系统吞吐量
- 平均响应时间
- 内存使用率
- GC暂停时间
- 数据库查询
- 缓存命中率
"性能优化是一个持续的过程,需要在业务发展中不断调整和改进。最好的优化不是一次性的大改,而是持续的小步改进。"
记住,性能优化的目标不是追求极致的性能,而是在成本、复杂度和性能之间找到最佳平衡点。在实际工作中,要根据业务需求和资源约束,制定合理的优化策略。