如何设计高并发系统:从架构原理到实战落地

如何设计高并发系统:从架构原理到实战落地

高并发系统设计不是简单的"加机器",而是要在流量、数据、服务三个维度上做系统性优化。本文从真实场景出发,拆解高并发系统的设计原理与落地实践。

一、高并发系统的核心挑战

1.1 问题本质

当并发请求量超过系统处理能力时,会出现以下问题:

挑战类型 表现症状 根本原因 典型场景
流量洪峰 响应时间飙升、服务雪崩 请求量超过系统承载能力 电商大促、秒杀活动
热点数据 缓存击穿、数据库压力集中 少量Key承载大部分流量 热门商品、明星用户
资源竞争 死锁、连接池耗尽 共享资源访问冲突 库存扣减、订单创建
数据一致性 超卖、数据不一致 分布式环境下的并发写入 秒杀、抢购

1.2 性能指标参考

业务场景 QPS峰值 响应时间要求 可用性要求
普通电商 1,000-10,000 <500ms 99.9%
大促活动 100,000-1,000,000 <200ms 99.99%
秒杀系统 1,000,000+ <100ms 99.999%
实时推荐 10,000-50,000 <300ms 99.95%

1.3 设计目标

在保证可用性数据一致性的前提下,最大化系统吞吐量。

CAP权衡

  • CA(一致性 + 可用性):适合强一致场景,如金融交易
  • CP(一致性 + 分区容错):适合数据一致性要求高场景,如库存管理
  • AP(可用性 + 分区容错):适合高可用场景,如商品浏览

实战建议 :大多数互联网业务选择最终一致性(BASE理论),通过补偿机制保证数据最终一致。

二、流量治理:从源头控制请求

2.1 多层限流策略

经验要点:限流要分层,越靠近用户越好。

场景:电商大促,QPS峰值从平时的1000飙升至100万。

架构设计

复制代码
用户请求
  ↓
CDN限流(静态资源)
  ↓
API网关限流(用户级限流)
  ↓
服务限流(接口级限流)
  ↓
资源限流(数据库/缓存)

Java实现(令牌桶算法)

java 复制代码
@Component
public class RateLimiter {
    private final Map<String, GuavaRateLimiter> limiters = new ConcurrentHashMap<>();
    
    public boolean tryAcquire(String key, int permitsPerSecond) {
        GuavaRateLimiter limiter = limiters.computeIfAbsent(
            key, 
            k -> GuavaRateLimiter.create(permitsPerSecond)
        );
        return limiter.tryAcquire();
    }
}

// 使用示例
@GetMapping("/order/create")
public Result createOrder(@RequestParam Long userId) {
    // 用户级限流:每用户每秒最多5次请求
    if (!rateLimiter.tryAcquire("user:" + userId, 5)) {
        return Result.error("请求过于频繁");
    }
    // 业务逻辑...
}

关键点

  • 用户级限流:防止恶意刷单
  • 接口级限流:保护核心接口
  • 资源级限流:保护数据库等稀缺资源

2.2 服务降级与熔断

场景:支付服务依赖第三方接口,第三方响应超时导致订单服务阻塞。

设计模式:使用Hystrix或Resilience4j实现熔断降级。

Java实现(Resilience4j)

java 复制代码
@Configuration
public class ResilienceConfig {
    
    @Bean
    public CircuitBreaker paymentCircuitBreaker() {
        return CircuitBreaker.ofDefaults("paymentService");
    }
    
    @Bean
    public TimeLimiter paymentTimeLimiter() {
        return TimeLimiter.ofDefaults("paymentService");
    }
}

@Service
public class PaymentService {
    
    @CircuitBreaker(name = "paymentService", fallbackMethod = "fallback")
    @TimeLimiter(name = "paymentService")
    @Retry(name = "paymentService")
    public CompletableFuture<PaymentResult> pay(Order order) {
        // 调用第三方支付接口
        return CompletableFuture.supplyAsync(() -> 
            thirdPartyPaymentClient.pay(order)
        );
    }
    
    private CompletableFuture<PaymentResult> fallback(Order order, Exception e) {
        // 降级逻辑:记录订单,稍后重试
        return CompletableFuture.completedFuture(
            PaymentResult.pending(order.getId())
        );
    }
}

降级策略

  • 自动降级:超时、失败率达到阈值自动触发
  • 手动降级:大促期间主动降级非核心功能
  • 分级降级:核心功能保留,非核心功能降级

常见陷阱

  1. 限流阈值设置不当 - 过低影响正常业务,过高无法保护系统
  2. 降级逻辑未测试 - 真正降级时发现降级逻辑有bug
  3. 熔断恢复策略缺失 - 熔断后无法自动恢复,导致服务长期不可用
  4. 限流粒度太粗 - 全局限流导致正常用户受影响
  5. 降级未通知用户 - 用户不知道功能已降级,体验差

技术选型建议

场景 推荐方案 理由
单机限流 Guava RateLimiter 轻量级,无需外部依赖
分布式限流 Redis + Lua 支持集群,性能好
API网关限流 Sentinel / Hystrix 功能完善,易于集成
服务熔断 Resilience4j 轻量级,性能优异

三、缓存策略:多级缓存体系

3.1 缓存架构设计

经验要点:缓存要分层,每层解决不同问题。

架构设计

复制代码
浏览器缓存(静态资源)
  ↓
CDN缓存(边缘节点)
  ↓
应用本地缓存(Caffeine/Guava)
  ↓
分布式缓存(Redis Cluster)
  ↓
数据库(MySQL)

3.2 缓存穿透解决方案

场景:恶意请求不存在的商品ID,直接打到数据库。

方案一:布隆过滤器

java 复制代码
@Service
public class ProductService {
    
    @Autowired
    private BloomFilter<Long> productBloomFilter;
    
    @Autowired
    private RedisTemplate<String, Product> redisTemplate;
    
    @Autowired
    private ProductMapper productMapper;
    
    public Product getProduct(Long id) {
        // 1. 布隆过滤器快速判断
        if (!productBloomFilter.mightContain(id)) {
            throw new ProductNotFoundException(id);
        }
        
        // 2. 查询Redis缓存
        String cacheKey = "product:" + id;
        Product product = redisTemplate.opsForValue().get(cacheKey);
        if (product != null) {
            return product;
        }
        
        // 3. 查询数据库
        product = productMapper.selectById(id);
        if (product != null) {
            // 4. 写入缓存,设置过期时间
            redisTemplate.opsForValue().set(cacheKey, product, 30, TimeUnit.MINUTES);
            return product;
        }
        
        // 5. 缓存空值,防止缓存穿透
        redisTemplate.opsForValue().set(cacheKey, NULL_PRODUCT, 5, TimeUnit.MINUTES);
        throw new ProductNotFoundException(id);
    }
}

方案二:缓存空值

java 复制代码
private static final Product NULL_PRODUCT = new Product();

public Product getProduct(Long id) {
    String cacheKey = "product:" + id;
    Product product = redisTemplate.opsForValue().get(cacheKey);
    
    if (product == NULL_PRODUCT) {
        throw new ProductNotFoundException(id);
    }
    
    if (product != null) {
        return product;
    }
    
    product = productMapper.selectById(id);
    if (product == null) {
        redisTemplate.opsForValue().set(cacheKey, NULL_PRODUCT, 5, TimeUnit.MINUTES);
        throw new ProductNotFoundException(id);
    }
    
    redisTemplate.opsForValue().set(cacheKey, product, 30, TimeUnit.MINUTES);
    return product;
}

3.3 缓存击穿解决方案

场景:热点商品缓存过期瞬间,大量请求同时打到数据库。

方案一:互斥锁

java 复制代码
public Product getProductWithLock(Long id) {
    String cacheKey = "product:" + id;
    String lockKey = "lock:product:" + id;
    
    // 1. 查询缓存
    Product product = redisTemplate.opsForValue().get(cacheKey);
    if (product != null) {
        return product;
    }
    
    // 2. 获取分布式锁
    try {
        Boolean locked = redisTemplate.opsForValue()
            .setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS);
        
        if (Boolean.TRUE.equals(locked)) {
            // 3. 双重检查
            product = redisTemplate.opsForValue().get(cacheKey);
            if (product != null) {
                return product;
            }
            
            // 4. 查询数据库
            product = productMapper.selectById(id);
            redisTemplate.opsForValue().set(cacheKey, product, 30, TimeUnit.MINUTES);
            return product;
        } else {
            // 5. 未获取锁,短暂等待后重试
            Thread.sleep(100);
            return getProductWithLock(id);
        }
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        throw new RuntimeException(e);
    } finally {
        redisTemplate.delete(lockKey);
    }
}

方案二:逻辑过期

java 复制代码
@Data
public class CacheData<T> {
    private T data;
    private long expireTime; // 逻辑过期时间
}

public Product getProductWithLogicalExpire(Long id) {
    String cacheKey = "product:" + id;
    
    // 1. 查询缓存
    CacheData<Product> cacheData = redisTemplate.opsForValue().get(cacheKey);
    
    if (cacheData == null) {
        // 2. 缓存未命中,返回空商品
        return null;
    }
    
    // 3. 检查逻辑过期时间
    if (cacheData.getExpireTime() > System.currentTimeMillis()) {
        // 4. 未过期,直接返回
        return cacheData.getData();
    }
    
    // 5. 已过期,异步刷新缓存
    CompletableFuture.runAsync(() -> {
        refreshCache(id);
    });
    
    // 6. 返回旧数据
    return cacheData.getData();
}

private void refreshCache(Long id) {
    Product product = productMapper.selectById(id);
    CacheData<Product> cacheData = new CacheData<>();
    cacheData.setData(product);
    cacheData.setExpireTime(System.currentTimeMillis() + 30 * 60 * 1000);
    
    String cacheKey = "product:" + id;
    redisTemplate.opsForValue().set(cacheKey, cacheData, 1, TimeUnit.HOURS);
}

3.4 缓存雪崩解决方案

场景:大量缓存同时过期,数据库压力激增。

方案一:随机过期时间

java 复制代码
public void setProductToCache(Long id, Product product) {
    String cacheKey = "product:" + id;
    // 基础过期时间30分钟 + 随机0-5分钟
    long expireTime = 30 + ThreadLocalRandom.current().nextInt(5);
    redisTemplate.opsForValue().set(cacheKey, product, expireTime, TimeUnit.MINUTES);
}

方案二:缓存预热

java 复制代码
@Component
public class CacheWarmup {
    
    @Autowired
    private ProductService productService;
    
    @Autowired
    private RedisTemplate<String, Product> redisTemplate;
    
    @Scheduled(cron = "0 0 2 * * ?") // 每天凌晨2点执行
    public void warmupCache() {
        log.info("开始缓存预热...");
        
        // 预热热门商品
        List<Long> hotProductIds = getHotProductIds();
        hotProductIds.parallelStream().forEach(id -> {
            Product product = productService.getProductFromDB(id);
            String cacheKey = "product:" + id;
            redisTemplate.opsForValue().set(cacheKey, product, 30, TimeUnit.MINUTES);
        });
        
        log.info("缓存预热完成,共预热{}个商品", hotProductIds.size());
    }
}

常见陷阱

  1. 缓存过期时间设置不当 - 过短导致频繁穿透,过长导致数据不一致
  2. 缓存更新策略错误 - 先删缓存再更新数据库,导致并发读取到旧数据
  3. 大Key问题 - 缓存单个对象过大,导致网络传输慢
  4. 缓存热点问题 - 某些Key访问过于频繁,导致单机压力过大
  5. 缓存依赖未考虑 - 缓存A依赖缓存B,B失效导致A也失效

性能对比

方案 QPS P99延迟 命中率
无缓存 1,000 800ms -
单级缓存 10,000 150ms 80%
多级缓存 50,000 50ms 95%
提升倍数 50x 16x -

技术选型建议

场景 推荐方案 理由
本地缓存 Caffeine 性能最优,支持自动过期
分布式缓存 Redis Cluster 高可用,支持数据持久化
缓存穿透防护 布隆过滤器 内存占用小,查询快
缓存击穿防护 互斥锁 简单有效,易于实现

四、异步处理:解耦与削峰

4.1 消息队列架构

经验要点:异步处理要考虑消息可靠性、顺序性和幂等性。

架构设计

复制代码
生产者
  ↓
消息队列(Kafka/RocketMQ)
  ↓
消费者集群
  ↓
下游服务

4.2 秒杀系统异步处理

场景:秒杀活动,10万商品在1秒内被抢光。

设计思路:前端限流 + 预扣库存 + 异步下单。

Java实现

java 复制代码
@Service
public class SeckillService {
    
    @Autowired
    private RedisTemplate<String, Long> redisTemplate;
    
    @Autowired
    private RocketMQTemplate rocketMQTemplate;
    
    /**
     * 秒杀下单
     */
    @Transactional
    public SeckillResult seckill(Long userId, Long activityId, Long skuId) {
        // 1. 检查用户是否已购买
        String userKey = "seckill:user:" + activityId + ":" + userId;
        Boolean hasBought = redisTemplate.opsForValue().setIfAbsent(userKey, skuId, 24, TimeUnit.HOURS);
        if (Boolean.FALSE.equals(hasBought)) {
            return SeckillResult.fail("您已购买过该商品");
        }
        
        // 2. 预扣库存(Redis原子操作)
        String stockKey = "seckill:stock:" + activityId + ":" + skuId;
        Long stock = redisTemplate.opsForValue().decrement(stockKey);
        
        if (stock == null || stock < 0) {
            // 库存不足,回滚
            redisTemplate.opsForValue().increment(stockKey);
            redisTemplate.delete(userKey);
            return SeckillResult.fail("库存不足");
        }
        
        // 3. 发送消息到MQ,异步创建订单
        SeckillMessage message = new SeckillMessage(userId, activityId, skuId);
        rocketMQTemplate.asyncSend("seckill-order-topic", message, new SendCallback() {
            @Override
            public void onSuccess(SendResult sendResult) {
                log.info("秒杀消息发送成功: {}", message);
            }
            
            @Override
            public void onException(Throwable e) {
                log.error("秒杀消息发送失败: {}", message, e);
                // 发送失败,回滚库存
                redisTemplate.opsForValue().increment(stockKey);
                redisTemplate.delete(userKey);
            }
        });
        
        return SeckillResult.success("抢购成功,请等待订单创建");
    }
}

/**
 * 秒杀消息消费者
 */
@Service
@RocketMQMessageListener(
    topic = "seckill-order-topic",
    consumerGroup = "seckill-order-group"
)
public class SeckillOrderConsumer implements RocketMQListener<SeckillMessage> {
    
    @Autowired
    private OrderService orderService;
    
    @Autowired
    private RedisTemplate<String, Long> redisTemplate;
    
    @Override
    public void onMessage(SeckillMessage message) {
        try {
            // 1. 幂等性检查
            String idempotentKey = "seckill:idempotent:" + message.getUserId() + ":" + message.getSkuId();
            Boolean isNew = redisTemplate.opsForValue().setIfAbsent(idempotentKey, "1", 7, TimeUnit.DAYS);
            if (Boolean.FALSE.equals(isNew)) {
                log.info("重复消息,跳过处理: {}", message);
                return;
            }
            
            // 2. 创建订单
            Order order = orderService.createSeckillOrder(message);
            
            log.info("秒杀订单创建成功: {}", order);
        } catch (Exception e) {
            log.error("处理秒杀消息失败: {}", message, e);
            throw e; // 重试
        }
    }
}

4.3 延迟队列应用

场景:订单超时自动取消。

Java实现(Redis + ZSet)

java 复制代码
@Service
public class OrderDelayQueue {
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    private static final String DELAY_QUEUE_KEY = "order:delay:queue";
    private static final String ORDER_PREFIX = "order:";
    
    /**
     * 添加订单到延迟队列
     */
    public void addOrderToDelayQueue(Long orderId, long delayMinutes) {
        long executeTime = System.currentTimeMillis() + delayMinutes * 60 * 1000;
        redisTemplate.opsForZSet().add(DELAY_QUEUE_KEY, ORDER_PREFIX + orderId, executeTime);
    }
    
    /**
     * 扫描延迟队列,处理超时订单
     */
    @Scheduled(fixedDelay = 1000) // 每秒执行一次
    public void processExpiredOrders() {
        long currentTime = System.currentTimeMillis();
        
        // 查询所有到期的订单
        Set<String> expiredOrders = redisTemplate.opsForZSet()
            .rangeByScore(DELAY_QUEUE_KEY, 0, currentTime);
        
        if (expiredOrders == null || expiredOrders.isEmpty()) {
            return;
        }
        
        // 处理到期订单
        expiredOrders.forEach(orderKey -> {
            try {
                Long orderId = Long.parseLong(orderKey.substring(ORDER_PREFIX.length()));
                
                // 检查订单状态
                Order order = orderService.getOrder(orderId);
                if (order != null && order.getStatus() == OrderStatus.PENDING) {
                    // 取消订单
                    orderService.cancelOrder(orderId, "超时未支付");
                    log.info("订单超时取消: {}", orderId);
                }
                
                // 从延迟队列中移除
                redisTemplate.opsForZSet().remove(DELAY_QUEUE_KEY, orderKey);
            } catch (Exception e) {
                log.error("处理超时订单失败: {}", orderKey, e);
            }
        });
    }
}

常见陷阱

  1. 消息丢失未处理 - 发送失败后没有重试或补偿机制
  2. 幂等性设计缺失 - 重复消费导致数据重复
  3. 顺序性未保证 - 需要顺序处理的消息被并发消费
  4. 死信队列未处理 - 消息消费失败后堆积在死信队列
  5. 消息积压未监控 - 消费速度跟不上生产速度,导致积压

性能对比

方案 QPS P99延迟 系统复杂度
同步处理 5,000 800ms
异步处理 50,000 150ms
异步+批量 100,000 100ms
提升倍数 20x 8x -

技术选型建议

场景 推荐方案 理由
高吞吐场景 Kafka 吞吐量高,支持分区
可靠性要求高 RocketMQ 支持事务消息,可靠性高
延迟敏感 Redis Stream 延迟低,简单易用
轻量级场景 RabbitMQ 功能完善,易于集成

五、数据库优化:读写分离与分库分表

5.1 读写分离架构

场景:电商系统,读多写少,查询压力远大于写入压力。

架构设计

复制代码
应用层
  ↓
读写分离中间件(ShardingSphere/MyCat)
  ↓
主库(写操作)
  ↓
从库集群(读操作)

Java实现(ShardingSphere)

yaml 复制代码
# application.yml
spring:
  shardingsphere:
    datasource:
      names: master,slave1,slave2
      master:
        type: com.zaxxer.hikari.HikariDataSource
        jdbc-url: jdbc:mysql://master:3306/db
      slave1:
        type: com.zaxxer.hikari.HikariDataSource
        jdbc-url: jdbc:mysql://slave1:3306/db
      slave2:
        type: com.zaxxer.hikari.HikariDataSource
        jdbc-url: jdbc:mysql://slave2:3306/db
    rules:
      readwrite-splitting:
        data-sources:
          readwrite_ds:
            static-strategy:
              write-data-source-name: master
              read-data-source-names: slave1,slave2
            load-balancers:
              round-robin:
                type: ROUND_ROBIN

代码使用

java 复制代码
@Service
public class OrderService {
    
    @Autowired
    private OrderMapper orderMapper;
    
    /**
     * 写操作 - 自动路由到主库
     */
    @Transactional
    public void createOrder(Order order) {
        orderMapper.insert(order);
    }
    
    /**
     * 读操作 - 自动路由到从库
     */
    public Order getOrder(Long orderId) {
        return orderMapper.selectById(orderId);
    }
    
    /**
     * 强制读主库(保证数据一致性)
     */
    @Hint("master")
    public Order getOrderFromMaster(Long orderId) {
        return orderMapper.selectById(orderId);
    }
}

5.2 分库分表策略

场景:订单表数据量超过1亿,查询性能下降。

分片策略

yaml 复制代码
spring:
  shardingsphere:
    rules:
      sharding:
        tables:
          t_order:
            actual-data-nodes: db$->{0..1}.t_order_$->{0..3}
            database-strategy:
              standard:
                sharding-column: user_id
                sharding-algorithm-name: db_mod
            table-strategy:
              standard:
                sharding-column: user_id
                sharding-algorithm-name: table_mod
        sharding-algorithms:
          db_mod:
            type: MOD
            props:
              sharding-count: 2
          table_mod:
            type: MOD
            props:
              sharding-count: 4

分片算法

java 复制代码
/**
 * 用户ID分片算法
 */
public class UserIdShardingAlgorithm implements PreciseShardingAlgorithm<Long> {
    
    @Override
    public String doSharding(Collection<String> availableTargetNames, 
                             PreciseShardingValue<Long> shardingValue) {
        Long userId = shardingValue.getValue();
        Long dbIndex = userId % 2; // 分2个库
        Long tableIndex = userId % 4; // 每个库分4张表
        
        return "db" + dbIndex + ".t_order_" + tableIndex;
    }
}

5.3 分页查询优化

场景:订单列表分页查询,越往后越慢。

优化方案一:游标分页

java 复制代码
@Data
public class PageResult<T> {
    private List<T> records;
    private Long nextCursor; // 下一页游标
    private Boolean hasNext;
}

@Service
public class OrderQueryService {
    
    /**
     * 游标分页查询
     */
    public PageResult<Order> queryByCursor(Long userId, Long cursor, int pageSize) {
        // 1. 构建查询条件
        LambdaQueryWrapper<Order> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Order::getUserId, userId);
        
        if (cursor != null) {
            wrapper.lt(Order::getId, cursor);
        }
        
        wrapper.orderByDesc(Order::getId);
        wrapper.last("LIMIT " + (pageSize + 1)); // 多查一条判断是否有下一页
        
        // 2. 执行查询
        List<Order> orders = orderMapper.selectList(wrapper);
        
        // 3. 处理结果
        List<Order> result = orders.stream()
            .limit(pageSize)
            .collect(Collectors.toList());
        
        Long nextCursor = orders.size() > pageSize ? 
            orders.get(pageSize - 1).getId() : null;
        
        return new PageResult<>(result, nextCursor, nextCursor != null);
    }
}

优化方案二:延迟关联

java 复制代码
/**
 * 延迟关联分页查询
 */
public PageResult<OrderVO> queryWithDelayAssociation(Long userId, int pageNum, int pageSize) {
    // 1. 先查询ID(只查主键,速度快)
    Page<Long> idPage = new Page<>(pageNum, pageSize);
    IPage<Long> ids = orderMapper.selectIdsByUserId(userId, idPage);
    
    if (ids.getRecords().isEmpty()) {
        return new PageResult<>(Collections.emptyList(), false);
    }
    
    // 2. 根据ID批量查询完整数据
    List<Order> orders = orderMapper.selectBatchIds(ids.getRecords());
    
    // 3. 转换为VO
    List<OrderVO> orderVOs = orders.stream()
        .map(this::convertToVO)
        .collect(Collectors.toList());
    
    return new PageResult<>(orderVOs, ids.hasNext());
}

常见陷阱

  1. 读写分离延迟问题 - 主从复制延迟导致读取到旧数据
  2. 分片键选择不当 - 导致数据倾斜,某些分片压力过大
  3. 跨分片查询 - 性能极差,应避免或使用聚合服务
  4. 分片后事务问题 - 跨分片事务难以保证
  5. 分片扩展困难 - 分片策略设计不当,后续扩展困难

性能对比

方案 QPS P99延迟 存储容量
单库单表 1,000 800ms 1000万
读写分离 5,000 300ms 1000万
分库分表 20,000 150ms 10亿
提升倍数 20x 5.3x 1000x

技术选型建议

场景 推荐方案 理由
读写分离 ShardingSphere 功能完善,易于集成
分库分表 MyCat / ShardingSphere 支持多种分片策略
分布式事务 Seata 支持AT、TCC、SAGA模式
数据同步 Canal 基于binlog,实时性好

六、服务治理:微服务架构

6.1 服务拆分原则

经验要点:按业务领域拆分,避免按技术层次拆分。

拆分维度

拆分维度 示例 优点 缺点
业务领域 订单服务、商品服务、用户服务 职责清晰、独立演进 拆分粒度难把握
数据归属 按数据表归属拆分 数据一致性易保证 可能出现跨服务查询
读写分离 读服务、写服务 性能优化明确 增加系统复杂度

6.2 服务调用优化

场景:订单详情页需要调用多个服务获取数据。

优化方案一:并行调用

java 复制代码
@Service
public class OrderDetailService {
    
    @Autowired
    private OrderService orderService;
    
    @Autowired
    private UserService userService;
    
    @Autowired
    private ProductService productService;
    
    @Autowired
    private LogisticsService logisticsService;
    
    /**
     * 并行调用多个服务
     */
    public OrderDetailVO getOrderDetail(Long orderId) {
        // 1. 查询订单
        Order order = orderService.getOrder(orderId);
        
        // 2. 并行查询关联数据
        CompletableFuture<User> userFuture = CompletableFuture.supplyAsync(
            () -> userService.getUser(order.getUserId()),
            executor
        );
        
        CompletableFuture<Product> productFuture = CompletableFuture.supplyAsync(
            () -> productService.getProduct(order.getSkuId()),
            executor
        );
        
        CompletableFuture<Logistics> logisticsFuture = CompletableFuture.supplyAsync(
            () -> logisticsService.getLogistics(orderId),
            executor
        );
        
        // 3. 等待所有结果
        CompletableFuture.allOf(userFuture, productFuture, logisticsFuture).join();
        
        // 4. 组装结果
        OrderDetailVO vo = new OrderDetailVO();
        vo.setOrder(order);
        vo.setUser(userFuture.get());
        vo.setProduct(productFuture.get());
        vo.setLogistics(logisticsFuture.get());
        
        return vo;
    }
}

优化方案二:数据聚合服务

java 复制代码
/**
 * 订单聚合服务
 */
@Service
public class OrderAggregateService {
    
    @Autowired
    private OrderRepository orderRepository;
    
    @Autowired
    private UserRepository userRepository;
    
    @Autowired
    private ProductRepository productRepository;
    
    /**
     * 聚合查询订单详情
     */
    public OrderDetailVO getAggregateOrderDetail(Long orderId) {
        // 1. 查询订单
        Order order = orderRepository.findById(orderId);
        
        // 2. 批量查询关联数据
        List<Long> userIds = Collections.singletonList(order.getUserId());
        List<Long> skuIds = Collections.singletonList(order.getSkuId());
        
        Map<Long, User> userMap = userRepository.findByIds(userIds);
        Map<Long, Product> productMap = productRepository.findByIds(skuIds);
        
        // 3. 组装结果
        OrderDetailVO vo = new OrderDetailVO();
        vo.setOrder(order);
        vo.setUser(userMap.get(order.getUserId()));
        vo.setProduct(productMap.get(order.getSkuId()));
        
        return vo;
    }
}

6.3 服务网格架构

场景:微服务数量超过100个,服务间调用复杂。

架构设计

复制代码
应用服务
  ↓
Sidecar代理(Envoy)
  ↓
服务网格(Istio)
  ↓
其他服务

核心功能

  • 流量管理:灰度发布、蓝绿部署
  • 安全:mTLS认证、授权
  • 可观测性:链路追踪、指标监控

常见陷阱

  1. 服务拆分过细 - 增加运维成本和调用延迟
  2. 服务间循环依赖 - 导致服务启动失败或死锁
  3. 服务版本管理混乱 - 多版本共存导致兼容性问题
  4. 服务监控缺失 - 无法及时发现服务异常
  5. 配置管理不当 - 配置变更导致服务不可用

性能对比

方案 平均响应时间 系统复杂度 运维成本
单体应用 200ms
微服务 300ms
微服务+服务网格 350ms 很高 很高
提升倍数 1.75x - -

技术选型建议

场景 推荐方案 理由
服务注册发现 Nacos / Consul 功能完善,易于集成
服务配置 Nacos / Apollo 支持动态配置
服务网关 Spring Cloud Gateway 功能丰富,易于扩展
服务网格 Istio 功能强大,适合大规模

七、实战场景:秒杀系统设计

7.1 整体架构

复制代码
用户
  ↓
CDN(静态资源)
  ↓
Nginx(限流)
  ↓
API网关(用户限流)
  ↓
秒杀服务
  ↓
Redis(库存预扣)
  ↓
MQ(异步下单)
  ↓
订单服务
  ↓
数据库

7.2 核心代码实现

java 复制代码
/**
 * 秒杀服务
 */
@Service
public class SeckillService {
    
    @Autowired
    private RedisTemplate<String, Long> redisTemplate;
    
    @Autowired
    private RocketMQTemplate rocketMQTemplate;
    
    @Autowired
    private ActivityService activityService;
    
    /**
     * 秒杀入口
     */
    @RateLimiter(value = 100, timeout = 1) // 接口限流100QPS
    public SeckillResult seckill(Long userId, Long activityId, Long skuId) {
        // 1. 活动校验
        Activity activity = activityService.getActivity(activityId);
        if (activity == null || !activity.isActive()) {
            return SeckillResult.fail("活动不存在或已结束");
        }
        
        // 2. 用户限流
        String userLimitKey = "seckill:limit:" + activityId + ":" + userId;
        Long count = redisTemplate.opsForValue().increment(userLimitKey);
        if (count != null && count == 1) {
            redisTemplate.expire(userLimitKey, 1, TimeUnit.SECONDS);
        }
        if (count != null && count > 1) {
            return SeckillResult.fail("请求过于频繁");
        }
        
        // 3. 预扣库存
        String stockKey = "seckill:stock:" + activityId + ":" + skuId;
        Long stock = redisTemplate.opsForValue().decrement(stockKey);
        
        if (stock == null || stock < 0) {
            // 库存不足
            redisTemplate.opsForValue().increment(stockKey);
            return SeckillResult.fail("库存不足");
        }
        
        // 4. 发送消息到MQ
        SeckillMessage message = new SeckillMessage(userId, activityId, skuId);
        rocketMQTemplate.syncSend("seckill-order-topic", message);
        
        return SeckillResult.success("抢购成功");
    }
}

/**
 * 秒杀消息消费者
 */
@Service
@RocketMQMessageListener(
    topic = "seckill-order-topic",
    consumerGroup = "seckill-order-group",
    consumeMode = ConsumeMode.ORDERLY // 顺序消费
)
public class SeckillOrderConsumer implements RocketMQListener<SeckillMessage> {
    
    @Autowired
    private OrderService orderService;
    
    @Autowired
    private InventoryService inventoryService;
    
    @Override
    public void onMessage(SeckillMessage message) {
        try {
            // 1. 幂等性检查
            if (orderService.exists(message.getUserId(), message.getSkuId())) {
                log.info("订单已存在,跳过: {}", message);
                return;
            }
            
            // 2. 创建订单
            Order order = orderService.createOrder(message);
            
            // 3. 扣减真实库存
            inventoryService.deductInventory(message.getSkuId(), 1);
            
            log.info("秒杀订单创建成功: {}", order);
        } catch (Exception e) {
            log.error("处理秒杀消息失败: {}", message, e);
            // 重试
            throw e;
        }
    }
}

7.3 性能优化总结

优化点 优化手段 效果
前端优化 静态资源CDN、页面静态化 减少服务器压力
流量控制 多层限流、排队系统 保护系统稳定性
库存管理 Redis预扣、异步确认 提升吞吐量
订单创建 消息队列、异步处理 解耦削峰
数据一致性 幂等性设计、补偿机制 保证数据准确

7.5 常见设计模式

模式一:生产者-消费者模式

场景:订单创建后需要发送通知、积分、物流等多个下游服务。

Java实现

java 复制代码
/**
 * 订单事件发布器
 */
@Service
public class OrderEventPublisher {
    
    @Autowired
    private ApplicationEventPublisher eventPublisher;
    
    /**
     * 发布订单创建事件
     */
    public void publishOrderCreated(Order order) {
        OrderCreatedEvent event = new OrderCreatedEvent(order);
        eventPublisher.publishEvent(event);
    }
}

/**
 * 通知服务监听器
 */
@Component
public class NotificationListener {
    
    @EventListener
    @Async
    public void handleOrderCreated(OrderCreatedEvent event) {
        // 发送通知
        notificationService.send(event.getOrder());
    }
}

/**
 * 积分服务监听器
 */
@Component
public class PointsListener {
    
    @EventListener
    @Async
    public void handleOrderCreated(OrderCreatedEvent event) {
        // 增加积分
        pointsService.add(event.getOrder());
    }
}

模式二:CQRS(命令查询职责分离)

场景:订单查询和写入的优化需求不同。

架构设计

复制代码
命令端(写)
  ↓
订单服务
  ↓
事件总线
  ↓
查询端(读)
  ↓
订单查询服务
  ↓
读模型(ES/Redis)

Java实现

java 复制代码
/**
 * 命令端:创建订单
 */
@Service
public class OrderCommandService {
    
    @Autowired
    private OrderRepository orderRepository;
    
    @Autowired
    private ApplicationEventPublisher eventPublisher;
    
    /**
     * 创建订单命令
     */
    @Transactional
    public Order createOrder(CreateOrderCommand command) {
        // 1. 创建订单
        Order order = new Order(command);
        orderRepository.save(order);
        
        // 2. 发布事件
        OrderCreatedEvent event = new OrderCreatedEvent(order);
        eventPublisher.publishEvent(event);
        
        return order;
    }
}

/**
 * 查询端:查询订单
 */
@Service
public class OrderQueryService {
    
    @Autowired
    private OrderReadRepository readRepository;
    
    /**
     * 查询订单
     */
    public OrderVO getOrder(Long orderId) {
        return readRepository.findById(orderId);
    }
    
    /**
     * 查询订单列表
     */
    public Page<OrderVO> queryOrders(OrderQuery query) {
        return readRepository.query(query);
    }
}

/**
 * 事件处理器:更新读模型
 */
@Component
public class OrderReadModelUpdater {
    
    @Autowired
    private OrderReadRepository readRepository;
    
    @EventListener
    public void handleOrderCreated(OrderCreatedEvent event) {
        // 更新读模型
        OrderVO vo = convertToVO(event.getOrder());
        readRepository.save(vo);
    }
}

模式三:Saga模式

场景:跨服务的长事务,如订单+库存+支付+物流。

Java实现

java 复制代码
/**
 * 订单Saga编排器
 */
@Service
public class OrderSagaOrchestrator {
    
    @Autowired
    private InventoryService inventoryService;
    
    @Autowired
    private PaymentService paymentService;
    
    @Autowired
    private LogisticsService logisticsService;
    
    /**
     * 执行订单Saga
     */
    @Saga
    public void executeOrderSaga(Order order) {
        try {
            // 1. 扣减库存
            inventoryService.deduct(order.getSkuId(), order.getQuantity());
            
            // 2. 创建支付
            Payment payment = paymentService.create(order);
            
            // 3. 创建物流
            Logistics logistics = logisticsService.create(order, payment);
            
            // 4. 更新订单状态
            order.setStatus(OrderStatus.COMPLETED);
            orderRepository.save(order);
            
        } catch (Exception e) {
            // 补偿操作
            compensate(order, e);
            throw e;
        }
    }
    
    /**
     * 补偿操作
     */
    private void compensate(Order order, Exception e) {
        try {
            // 回滚库存
            inventoryService.rollback(order.getSkuId(), order.getQuantity());
            
            // 取消支付
            paymentService.cancel(order.getPaymentId());
            
            // 取消物流
            logisticsService.cancel(order.getLogisticsId());
            
            // 更新订单状态
            order.setStatus(OrderStatus.FAILED);
            orderRepository.save(order);
            
        } catch (Exception ex) {
            log.error("补偿操作失败: {}", order, ex);
            // 记录失败,人工介入
        }
    }
}

7.4 压测方案与容量规划

场景:秒杀系统上线前,需要验证系统承载能力。

压测工具选择

工具 优点 缺点 适用场景
JMeter 功能强大,图形界面 资源占用高 功能测试
Gatling 性能好,脚本简洁 学习曲线陡 性能测试
Locust 分布式压测,Python编写 需要编程能力 大规模压测
wrk/ab 轻量级,命令行 功能简单 快速验证

压测流程

java 复制代码
/**
 * 压测脚本示例(JMeter + Java)
 */
public class SeckillLoadTest {
    
    private static final String BASE_URL = "http://seckill-service";
    private static final int THREAD_COUNT = 100;
    private static final int LOOP_COUNT = 100;
    
    @Test
    public void testSeckillPerformance() {
        // 1. 准备测试数据
        List<Long> userIds = prepareTestUsers(THREAD_COUNT * LOOP_COUNT);
        Long activityId = 1L;
        Long skuId = 1001L;
        
        // 2. 创建线程池
        ExecutorService executor = Executors.newFixedThreadPool(THREAD_COUNT);
        CountDownLatch latch = new CountDownLatch(THREAD_COUNT * LOOP_COUNT);
        
        // 3. 记录开始时间
        long startTime = System.currentTimeMillis();
        
        // 4. 并发执行
        for (int i = 0; i < THREAD_COUNT; i++) {
            final int threadIndex = i;
            executor.submit(() -> {
                for (int j = 0; j < LOOP_COUNT; j++) {
                    try {
                        Long userId = userIds.get(threadIndex * LOOP_COUNT + j);
                        SeckillResult result = seckill(userId, activityId, skuId);
                        // 记录结果
                        recordResult(result);
                    } catch (Exception e) {
                        recordError(e);
                    } finally {
                        latch.countDown();
                    }
                }
            });
        }
        
        // 5. 等待所有请求完成
        latch.await();
        
        // 6. 记录结束时间
        long endTime = System.currentTimeMillis();
        
        // 7. 输出压测报告
        printReport(startTime, endTime);
    }
    
    private SeckillResult seckill(Long userId, Long activityId, Long skuId) {
        // 模拟HTTP请求
        RestTemplate restTemplate = new RestTemplate();
        String url = BASE_URL + "/seckill?userId=" + userId + 
                     "&activityId=" + activityId + "&skuId=" + skuId;
        return restTemplate.getForObject(url, SeckillResult.class);
    }
}

容量规划方法

java 复制代码
/**
 * 容量规划工具
 */
@Component
public class CapacityPlanner {
    
    /**
     * 计算所需实例数
     * 
     * @param expectedQPS 预期QPS
     * @param singleInstanceQPS 单实例QPS
     * @param redundancyRatio 冗余比例(建议1.5-2.0)
     * @return 所需实例数
     */
    public int calculateRequiredInstances(int expectedQPS, int singleInstanceQPS, double redundancyRatio) {
        double required = (double) expectedQPS / singleInstanceQPS * redundancyRatio;
        return (int) Math.ceil(required);
    }
    
    /**
     * 计算数据库连接池大小
     * 
     * @param expectedQPS 预期QPS
     * @param avgQueryTime 平均查询时间(毫秒)
     * @param maxConnections 单数据库最大连接数
     * @return 连接池大小
     */
    public int calculatePoolSize(int expectedQPS, int avgQueryTime, int maxConnections) {
        double poolSize = (double) expectedQPS * avgQueryTime / 1000;
        return (int) Math.min(Math.ceil(poolSize), maxConnections);
    }
    
    /**
     * 计算Redis内存需求
     * 
     * @param keyCount Key数量
     * @param avgValueSize 平均Value大小(字节)
     * @param redundancyRatio 冗余比例(建议1.2)
     * @return 所需内存(GB)
     */
    public double calculateRedisMemory(int keyCount, int avgValueSize, double redundancyRatio) {
        double totalBytes = keyCount * avgValueSize * redundancyRatio;
        return totalBytes / (1024 * 1024 * 1024);
    }
}

压测报告模板

markdown 复制代码
# 秒杀系统压测报告

## 测试环境
- 测试时间:2024-01-15 14:00-16:00
- 测试工具:JMeter 5.5
- 并发线程:100
- 循环次数:100
- 总请求数:10,000

## 测试结果

| 指标 | 目标值 | 实际值 | 是否达标 |
|------|-------|-------|---------|
| QPS | 10,000 | 12,500 | ✅ |
| P99延迟 | 200ms | 150ms | ✅ |
| P95延迟 | 100ms | 80ms | ✅ |
| 平均延迟 | 50ms | 40ms | ✅ |
| 错误率 | 0.1% | 0.05% | ✅ |

## 瓶颈分析
1. 数据库连接池:达到上限,需要扩容
2. Redis内存:使用率80%,建议增加内存
3. 网络带宽:使用率60%,暂不扩容

## 优化建议
1. 增加数据库从库,提升读能力
2. 优化Redis缓存策略,减少内存占用
3. 增加应用实例,提升并发能力

## 容量规划
- 预期峰值QPS:100,000
- 单实例QPS:1,000
- 所需实例数:100(冗余系数1.5)
- 当前实例数:20
- 需要扩容:80个实例

八、监控与运维

8.1 全链路追踪

Java实现(SkyWalking)

java 复制代码
@RestController
public class OrderController {
    
    @Autowired
    private OrderService orderService;
    
    @GetMapping("/order/{id}")
    @NewSpan("getOrder") // 自定义Span
    public Order getOrder(@PathVariable Long id) {
        return orderService.getOrder(id);
    }
}

@Service
public class OrderService {
    
    @Autowired
    private OrderMapper orderMapper;
    
    @NewSpan("queryOrderFromDB")
    public Order getOrder(Long id) {
        return orderMapper.selectById(id);
    }
}

8.2 指标监控

Java实现(Micrometer)

java 复制代码
@Service
public class OrderMetrics {
    
    private final Counter orderCreatedCounter;
    private final Timer orderCreationTimer;
    private final Gauge pendingOrderGauge;
    
    public OrderMetrics(MeterRegistry registry) {
        this.orderCreatedCounter = Counter.builder("order.created")
            .description("订单创建总数")
            .register(registry);
        
        this.orderCreationTimer = Timer.builder("order.creation.time")
            .description("订单创建耗时")
            .register(registry);
        
        this.pendingOrderGauge = Gauge.builder("order.pending.count", 
            this, OrderMetrics::getPendingOrderCount)
            .description("待处理订单数")
            .register(registry);
    }
    
    public void recordOrderCreated() {
        orderCreatedCounter.increment();
    }
    
    public Timer.Sample startOrderCreation() {
        return Timer.start();
    }
    
    public void stopOrderCreation(Timer.Sample sample) {
        sample.stop(orderCreationTimer);
    }
    
    private double getPendingOrderCount() {
        return orderService.getPendingOrderCount();
    }
}

8.3 告警规则

Prometheus告警规则

yaml 复制代码
groups:
  - name: order_alerts
    interval: 30s
    rules:
      # 订单创建失败率过高
      - alert: HighOrderFailureRate
        expr: |
          rate(order_created_failed_total[5m]) / 
          rate(order_created_total[5m]) > 0.1
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "订单创建失败率过高"
          description: "订单创建失败率超过10%"
      
      # 订单处理延迟过高
      - alert: HighOrderProcessingLatency
        expr: |
          histogram_quantile(0.99, 
            rate(order_creation_time_seconds_bucket[5m])) > 2
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "订单处理延迟过高"
          description: "P99订单处理延迟超过2秒"
      
      # 待处理订单积压
      - alert: PendingOrderBacklog
        expr: order_pending_count > 10000
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "待处理订单积压"
          description: "待处理订单数超过10000"

九、总结与行动建议

核心设计原则

  1. 分层限流:从CDN到数据库,层层设防
  2. 多级缓存:本地缓存 + 分布式缓存 + 数据库
  3. 异步解耦:消息队列处理非实时业务
  4. 读写分离:读多写少场景下的性能优化
  5. 服务治理:熔断降级保证系统稳定性

实战检查清单

在做高并发系统设计时,建议回答以下问题:

  • 流量峰值是多少?是否有突发流量?
  • 哪些是核心接口?需要重点保护?
  • 缓存策略是什么?如何解决缓存穿透/击穿/雪崩?
  • 是否需要异步处理?消息队列如何保证可靠性?
  • 数据库如何优化?是否需要读写分离或分库分表?
  • 如何保证数据一致性?是否有补偿机制?
  • 监控指标有哪些?告警规则是否完善?

行动建议

从你负责的系统中选一个高并发场景,按照本文的方法论进行优化:

  1. 分析现状:梳理流量、数据、服务三个维度
  2. 设计方案:制定限流、缓存、异步等优化策略
  3. 逐步实施:先优化核心路径,再完善周边系统
  4. 监控验证:通过指标验证优化效果

相关推荐
牛奶15 小时前
《前端架构设计》:除了写代码,我们还得管点啥
前端·架构·设计
苏渡苇17 小时前
Java + Redis + MySQL:工业时序数据缓存与持久化实战(适配高频采集场景)
java·spring boot·redis·后端·spring·缓存·架构
麦聪聊数据17 小时前
如何用 B/S 架构解决混合云环境下的数据库连接碎片化难题?
运维·数据库·sql·安全·架构
2的n次方_17 小时前
CANN HCOMM 底层架构深度解析:异构集群通信域管理、硬件链路使能与算力重叠优化机制
架构
技术传感器17 小时前
大模型从0到精通:对齐之心 —— 人类如何教会AI“好“与“坏“ | RLHF深度解析
人工智能·深度学习·神经网络·架构
小北的AI科技分享19 小时前
万亿参数时代:大语言模型的技术架构与演进趋势
架构·模型·推理
一条咸鱼_SaltyFish21 小时前
从零构建个人AI Agent:Node.js + LangChain + 上下文压缩全流程
网络·人工智能·架构·langchain·node.js·个人开发·ai编程
码云数智-园园21 小时前
解决 IntelliJ IDEA 运行 Spring Boot 测试时“命令行过长”错误
架构
AC赳赳老秦1 天前
虚拟化技术演进:DeepSeek适配轻量级虚拟机,实现AI工作负载高效管理
人工智能·python·架构·数据挖掘·自动化·数据库架构·deepseek
Francek Chen1 天前
【大数据存储与管理】分布式文件系统HDFS:01 分布式文件系统
大数据·hadoop·分布式·hdfs·架构