B.10.01.3-性能优化实战:从JVM到数据库的全链路优化
⚡ 性能优化实战:从JVM到数据库的全链路优化
“过早的优化是万恶之源,但适时的优化是成功之本。”
🎯 业务场景:电商大促性能危机
📊 项目背景
双11前夕,电商系统面临性能瓶颈,急需全链路优化:
性能指标 | 当前状态 | 目标状态 | 业务影响 |
---|---|---|---|
接口响应时间 | 3000ms | 200ms | 用户体验差,转化率低 |
系统吞吐量 | 1000 QPS | 10000 QPS | 无法支撑大促流量 |
数据库连接 | 频繁超时 | 稳定可用 | 订单创建失败 |
内存使用率 | 85% | 60% | 频繁GC,系统卡顿 |
🔧 JVM性能优化实战
💡 JVM调优核心原理
1. 内存结构优化
❌ 优化前:默认JVM配置
# 默认启动参数 - 性能差
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配置
# 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. 垃圾收集器选择
// GC性能测试工具
@Component
public class GCPerformanceAnalyzer {private final MeterRegistry meterRegistry;public GCPerformanceAnalyzer(MeterRegistry meterRegistry) {this.meterRegistry = meterRegistry;}@EventListenerpublic 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. 内存泄漏检测与修复
❌ 问题代码:内存泄漏
// 内存泄漏示例
@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() {@Overridepublic void onOrderProcessed(Order order) {// 处理逻辑}});// 提交任务到线程池executor.submit(() -> {// 异步处理订单handleOrderAsync(order);});}
}
✅ 修复后:内存安全
@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:性能低下
-- 慢查询示例:订单查询(执行时间: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会消耗大量内存
✅ 优化后:高性能查询
-- 第一步:创建复合索引
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 oiJOIN products p ON oi.product_id = p.idWHERE oi.order_id = o.idLIMIT 50) AS product_names
FROM (SELECT id, user_id, order_number, total_amount, create_timeFROM ordersWHERE status = 'COMPLETED'AND create_time >= '2023-01-01'ORDER BY create_time DESCLIMIT 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. 批量操作优化
❌ 问题:逐条插入
// 性能差的批量插入
@Service
public class OrderService {public void batchCreateOrders(List<Order> orders) {for (Order order : orders) {// 每次都是一个数据库往返orderRepository.save(order);}// 1000条订单需要1000次数据库调用,耗时10s}
}
✅ 优化:真正的批量操作
@Service
public class OrderService {@Autowiredprivate 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. 连接池配置优化
❌ 问题配置:连接池设置不当
# 默认配置 - 性能差
spring:datasource:hikari:maximum-pool-size: 10 # 连接池太小minimum-idle: 5 # 最小空闲连接connection-timeout: 30000 # 连接超时30sidle-timeout: 600000 # 空闲超时10分钟max-lifetime: 1800000 # 最大生命周期30分钟# 问题:
# 1. 连接池大小不足,高并发时连接等待
# 2. 连接超时时间过长,影响用户体验
# 3. 连接生命周期设置不合理
✅ 优化配置:精细化调优
# 生产环境优化配置
spring:datasource:# 1. 基础连接信息url: jdbc:mysql://your-mysql-host:3306/your_database?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=trueusername: your_usernamepassword: your_passworddriver-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. 连接池监控
@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. 缓存层次设计
// 多级缓存架构
@Service
public class ProductService {// L1缓存:本地缓存(Caffeine)private final Cache<String, Product> localCache = Caffeine.newBuilder().maximumSize(1000).expireAfterWrite(Duration.ofMinutes(5)).build();// L2缓存:分布式缓存(Redis)@Autowiredprivate RedisTemplate<String, Product> redisTemplate;// L3缓存:数据库@Autowiredprivate 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. 缓存预热策略
@Component
public class CacheWarmupService {@Autowiredprivate ProductService productService;@Autowiredprivate 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. 缓存穿透防护
@Service
public class ProductService {// 布隆过滤器防止缓存穿透private final BloomFilter<String> productBloomFilter = BloomFilter.create(Funnels.stringFunnel(Charset.defaultCharset()),1000000, // 预期元素数量0.01 // 误判率1%);@PostConstructpublic 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. 连接池配置
// HTTP客户端连接池优化
@Configuration
public class HttpClientConfig {@Beanpublic 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();// 配置RestTemplateHttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);RestTemplate restTemplate = new RestTemplate(factory);// 添加拦截器restTemplate.getInterceptors().add(new LoggingInterceptor());restTemplate.getInterceptors().add(new RetryInterceptor());return restTemplate;}// 连接池监控@Beanpublic 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. 异步处理优化
// 异步处理提升并发能力
@Service
public class OrderService {@Autowired@Qualifier("orderProcessingExecutor")private TaskExecutor taskExecutor;@Autowiredprivate 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核心数 * 2executor.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暂停时间
- 数据库查询
- 缓存命中率
“性能优化是一个持续的过程,需要在业务发展中不断调整和改进。最好的优化不是一次性的大改,而是持续的小步改进。”
记住,性能优化的目标不是追求极致的性能,而是在成本、复杂度和性能之间找到最佳平衡点。在实际工作中,要根据业务需求和资源约束,制定合理的优化策略。