B.10.01.3-性能优化实战:从JVM到数据库的全链路优化

⚡ 性能优化实战:从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暂停时间
  • 数据库查询
  • 缓存命中率

"性能优化是一个持续的过程,需要在业务发展中不断调整和改进。最好的优化不是一次性的大改,而是持续的小步改进。"

记住,性能优化的目标不是追求极致的性能,而是在成本、复杂度和性能之间找到最佳平衡点。在实际工作中,要根据业务需求和资源约束,制定合理的优化策略。

相关推荐
cccc来财1 小时前
Golang的本地缓存freecache
java·开发语言·jvm
ALLSectorSorft2 小时前
定制客车系统票务管理系统功能设计
linux·服务器·前端·数据库·apache
用户84913717547162 小时前
JDK 17 实战系列(第5期):开发工具与API增强详解
java·jvm·架构
傻啦嘿哟3 小时前
Django模型开发全解析:字段、元数据与继承的实战指南
数据库·sqlite
lifallen3 小时前
Kafka ISR机制和Raft区别:副本数优化的秘密
java·大数据·数据库·分布式·算法·kafka·apache
加油吧zkf4 小时前
MySQL索引优化全攻略:提升查询性能30%
数据库·mysql
嫩萝卜头儿4 小时前
AWT 事件监听器深入浅出:Action/Mouse/Key/Window 全解析与实战
java·开发语言·性能优化
FogLetter4 小时前
解放主线程!Web Worker 让你的前端应用飞起来
前端·性能优化
程序员JerrySUN4 小时前
四级页表通俗讲解与实践(以 64 位 ARM Cortex-A 为例)
java·arm开发·数据库·redis·嵌入式硬件·缓存