电商下单接口从520ms优化到185ms,如何做到?

业务场景

电商平台核心下单接口,当前响应时间P99达到520ms,影响用户体验。

目标:优化到200ms以内。


性能优化核心思路

┌─────────────────────────────────────────────────────────┐
│                   性能优化三板斧                          │
├─────────────────────────────────────────────────────────┤
│                                                         │
│   1. 异步化:非核心逻辑异步执行                           │
│                                                         │
│   2. 并行化:无依赖的调用并行执行                         │
│                                                         │
│   3. 缓存:减少I/O,用空间换时间                         │
│                                                         │
└─────────────────────────────────────────────────────────┘

优化前:串行执行

public OrderResult createOrder(OrderRequest request) {
    // 1. 校验用户 - 50ms
    User user = userService.getUser(request.getUserId());
    validateUser(user);
    
    // 2. 校验库存 - 80ms
    Stock stock = stockService.checkStock(request.getProductId());
    validateStock(stock);
    
    // 3. 计算价格 - 60ms
    Price price = priceService.calculatePrice(request);
    
    // 4. 创建订单 - 100ms
    Order order = orderService.createOrder(request, price);
    
    // 5. 扣减库存 - 80ms
    stockService.deductStock(request.getProductId(), request.getQuantity());
    
    // 6. 扣减优惠券 - 50ms
    couponService.useCoupon(request.getCouponId());
    
    // 7. 发送通知 - 100ms
    notificationService.sendOrderNotification(order);
    
    return new OrderResult(order);
}

总耗时:50+80+60+100+80+50+100 = 520ms


优化一:异步化非核心逻辑

分析核心与非核心

步骤 是否核心 原因
校验用户/库存 ✅ 核心 下单前必须校验
计算价格 ✅ 核心 价格必须准确
创建订单 ✅ 核心 核心业务
扣减库存 ✅ 核心 必须同步扣减
扣减优惠券 ⚠️ 准核心 可短暂延迟
发送通知 ❌ 非核心 完全可异步

优化代码

public OrderResult createOrder(OrderRequest request) {
    // 1-5: 核心逻辑(同步)
    User user = userService.getUser(request.getUserId());
    Stock stock = stockService.checkStock(request.getProductId());
    Price price = priceService.calculatePrice(request);
    Order order = orderService.createOrder(request, price);
    stockService.deductStock(request.getProductId(), request.getQuantity());
    
    // 6. 扣减优惠券(可以放入事务消息)
    couponService.useCoupon(request.getCouponId());
    
    // 7. 发送通知(异步)
    asyncExecutor.execute(() -> {
        notificationService.sendOrderNotification(order);
    });
    
    return new OrderResult(order);
}

优化效果:520 - 100 = 420ms


优化二:并行化无依赖调用

分析依赖关系

校验用户 ─────┐
              ├──→ 创建订单 ──→ 扣减库存
校验库存 ─────┤
              │
计算价格 ─────┘

用户校验、库存校验、价格计算互不依赖,可以并行!

使用CompletableFuture并行化

public OrderResult createOrder(OrderRequest request) {
    // 1,2,3: 并行执行
    CompletableFuture<User> userFuture = CompletableFuture.supplyAsync(
        () -> userService.getUser(request.getUserId()), executor);
    
    CompletableFuture<Stock> stockFuture = CompletableFuture.supplyAsync(
        () -> stockService.checkStock(request.getProductId()), executor);
    
    CompletableFuture<Price> priceFuture = CompletableFuture.supplyAsync(
        () -> priceService.calculatePrice(request), executor);
    
    // 等待所有并行任务完成
    CompletableFuture.allOf(userFuture, stockFuture, priceFuture).join();
    
    User user = userFuture.join();
    Stock stock = stockFuture.join();
    Price price = priceFuture.join();
    
    // 校验
    validateUser(user);
    validateStock(stock);
    
    // 4. 创建订单
    Order order = orderService.createOrder(request, price);
    
    // 5. 扣减库存
    stockService.deductStock(request.getProductId(), request.getQuantity());
    
    // 6. 扣减优惠券
    couponService.useCoupon(request.getCouponId());
    
    // 7. 发送通知(异步)
    asyncExecutor.execute(() -> {
        notificationService.sendOrderNotification(order);
    });
    
    return new OrderResult(order);
}

优化效果

  • 步骤1,2,3并行:max(50, 80, 60) = 80ms
  • 步骤4,5,6串行:100+80+50 = 230ms
  • 总计:80+230 = 310ms

优化三:缓存热点数据

用户信息缓存

@Cacheable(value = "user", key = "#userId", unless = "#result == null")
public User getUser(Long userId) {
    return userMapper.findById(userId);
}

商品库存缓存

public Stock checkStock(Long productId) {
    String key = "stock:" + productId;
    
    // 先查Redis
    String stockJson = redisTemplate.opsForValue().get(key);
    if (stockJson != null) {
        return JSON.parseObject(stockJson, Stock.class);
    }
    
    // 回源查库
    Stock stock = stockMapper.findByProductId(productId);
    redisTemplate.opsForValue().set(key, JSON.toJSONString(stock), 5, TimeUnit.SECONDS);
    return stock;
}

优化效果

  • 用户查询:50ms → 5ms
  • 库存查询:80ms → 5ms

步骤1,2,3并行:max(5, 5, 60) = 60ms


优化四:数据库优化

批量写入

// 优化前:多次单条插入
for (OrderItem item : items) {
    orderItemMapper.insert(item);
}

// 优化后:批量插入
orderItemMapper.batchInsert(items);

索引优化

-- 确保扣减库存走索引
ALTER TABLE stock ADD INDEX idx_product_id (product_id);

-- 减少查询字段
SELECT stock_count FROM stock WHERE product_id = ?;  -- 覆盖索引

最终代码

@Service
public class OrderService {
    
    @Autowired
    private ThreadPoolExecutor parallelExecutor;
    
    @Autowired  
    private ThreadPoolExecutor asyncExecutor;
    
    public OrderResult createOrder(OrderRequest request) {
        long start = System.currentTimeMillis();
        
        // 1. 并行获取用户、库存、价格(60ms)
        CompletableFuture<User> userFuture = CompletableFuture.supplyAsync(
            () -> userService.getCachedUser(request.getUserId()), parallelExecutor);
        CompletableFuture<Stock> stockFuture = CompletableFuture.supplyAsync(
            () -> stockService.getCachedStock(request.getProductId()), parallelExecutor);
        CompletableFuture<Price> priceFuture = CompletableFuture.supplyAsync(
            () -> priceService.calculatePrice(request), parallelExecutor);
        
        CompletableFuture.allOf(userFuture, stockFuture, priceFuture).join();
        
        validateUser(userFuture.join());
        validateStock(stockFuture.join());
        Price price = priceFuture.join();
        
        // 2. 创建订单 + 扣减库存(事务内,100ms)
        Order order = doCreateOrder(request, price);
        
        // 3. 异步处理非核心逻辑
        asyncExecutor.execute(() -> {
            couponService.useCoupon(request.getCouponId());
            notificationService.sendOrderNotification(order);
        });
        
        long cost = System.currentTimeMillis() - start;
        log.info("下单耗时: {}ms", cost);  // ~185ms
        
        return new OrderResult(order);
    }
}

优化效果汇总

优化手段 优化前 优化后 提升
异步化通知 520ms 420ms 100ms
并行化查询 420ms 310ms 110ms
缓存热点数据 310ms 240ms 70ms
数据库优化 240ms 185ms 55ms
总计 520ms 185ms 64%↓

面试答题框架

原始问题:下单接口P99=520ms

优化思路:
1. 异步化:发送通知异步处理 → 节省100ms
2. 并行化:用户/库存/价格并行查询 → 节省110ms
3. 缓存:热点数据Redis缓存 → 节省70ms
4. DB优化:批量插入、索引优化 → 节省55ms

工具选择:
- CompletableFuture实现并行化
- @Cacheable + Redis实现缓存
- 自定义线程池控制并发

最终效果:520ms → 185ms,提升64%

总结

优化手段 适用场景 注意事项
异步化 非核心逻辑 异常处理、监控
并行化 无依赖的I/O操作 线程池配置
缓存 热点数据 一致性、失效策略
DB优化 写操作多 批量、索引