每天一道面试题之架构篇|高并发系统架构设计实战手册

30 阅读7分钟

面试官:"如果让你设计一个百万QPS的系统,你会从哪些方面考虑架构设计?请详细说明你的设计方案。"

高并发系统架构是互联网公司的核心能力,今天我们来深入解析如何设计一个能够应对千万级流量的高可用系统架构。

一、核心难点:高并发系统的挑战分析

1. 性能瓶颈问题

  • 单机性能极限:CPU、内存、I/O瓶颈
  • 网络带宽限制:南北流量与东西流量
  • 数据库连接数限制:连接池耗尽风险

2. 可用性保障挑战

  • 单点故障导致服务雪崩
  • 依赖服务不稳定引发连锁反应
  • 机房级别故障的容灾能力

3. 数据一致性难题

  • 缓存与数据库数据不一致
  • 分布式环境下的事务处理
  • 并发写操作的数据冲突

4. 系统扩展性限制

  • 垂直扩展的天花板效应
  • 水平扩展的数据迁移成本
  • 服务发现和负载均衡瓶颈

二、架构设计原则

2.1 分层架构设计
// 典型的高并发系统分层架构
public class HighConcurrencyArchitecture {
    /**
     * 接入层:负载均衡、流量分发
     * - Nginx/LVS:四层负载均衡
     * - API Gateway:七层路由、鉴权、限流
     */
    public class AccessLayer {
        // 负载均衡策略
        public void loadBalance(Request request) {
            // 轮询、加权、最少连接等算法
        }
        
        // 限流防护
        public boolean rateLimit(String apiKey) {
            // 令牌桶、漏桶算法实现
            return true;
        }
    }
    
    /**
     * 应用层:业务逻辑处理
     * - 微服务架构:业务拆分、独立部署
     * - 无状态设计:便于水平扩展
     */
    public class ApplicationLayer {
        // 业务服务拆分
        public void processBusiness() {
            UserService userService = new UserService();
            OrderService orderService = new OrderService();
            InventoryService inventoryService = new InventoryService();
        }
    }
    
    /**
     * 数据层:数据存储与缓存
     * - 读写分离:主从架构
     * - 分库分表:数据水平拆分
     * - 多级缓存:减少数据库压力
     */
    public class DataLayer {
        // 缓存策略
        public void cacheStrategy() {
            // 本地缓存 + 分布式缓存
        }
    }
}
2.2 高性能设计模式

缓存架构设计:

// 多级缓存实现
@Service
public class MultiLevelCacheService {
    
    @Autowired
    private RedisTemplate<StringObject> redisTemplate;
    
    // 本地缓存(Caffeine)
    private Cache<StringObject> localCache = Caffeine.newBuilder()
        .maximumSize(10000)
        .expireAfterWrite(5TimeUnit.MINUTES)
        .build();
    
    // 获取数据(多级缓存策略)
    public Object getData(String key) {
        // 1. 查询本地缓存
        Object value = localCache.getIfPresent(key);
        if (value != null) {
            return value;
        }
        
        // 2. 查询分布式缓存
        value = redisTemplate.opsForValue().get(key);
        if (value != null) {
            // 回填本地缓存
            localCache.put(key, value);
            return value;
        }
        
        // 3. 查询数据库(缓存穿透保护)
        synchronized (this) {
            // 双重检查锁定
            value = redisTemplate.opsForValue().get(key);
            if (value == null) {
                value = getFromDatabase(key);
                if (value != null) {
                    // 写入缓存
                    redisTemplate.opsForValue().set(key, value, 30TimeUnit.MINUTES);
                    localCache.put(key, value);
                } else {
                    // 缓存空值防止穿透
                    redisTemplate.opsForValue().set(key, ""1TimeUnit.MINUTES);
                }
            }
            return value;
        }
    }
    
    // 数据库查询
    private Object getFromDatabase(String key) {
        // 实际数据库查询逻辑
        return null;
    }
}

异步处理设计:

// 异步化处理框架
@Service
public class AsyncProcessor {
    
    @Autowired
    private ThreadPoolTaskExecutor taskExecutor;
    
    @Autowired
    private KafkaTemplate<StringString> kafkaTemplate;
    
    // 线程池异步处理
    public CompletableFuture<ResultasyncProcess(Request request) {
        return CompletableFuture.supplyAsync(() -> {
            try {
                return processBusiness(request);
            } catch (Exception e) {
                throw new RuntimeException("Process failed", e);
            }
        }, taskExecutor);
    }
    
    // 消息队列异步解耦
    public void asyncByMessage(Request request) {
        // 转换业务消息
        String message = convertToMessage(request);
        
        // 发送到消息队列
        kafkaTemplate.send("async-process-topic", message)
            .addCallback(
                result -> log.info("Message sent successfully"),
                ex -> log.error("Failed to send message", ex)
            );
    }
    
    // 批量处理优化
    public void batchProcess(List<Request> requests) {
        // 批量操作减少I/O次数
        if (requests.size() > BATCH_THRESHOLD) {
            processInBatches(requests);
        } else {
            processOneByOne(requests);
        }
    }
}

三、关键技术方案

3.1 负载均衡策略

四层负载均衡(LVS):

  • 基于IP+端口的转发
  • 高性能,支持DR/NAT/TUNNEL模式
  • 适用于TCP/UDP协议

七层负载均衡(Nginx):

  • 基于HTTP协议的内容转发
  • 支持URL路由、重写、限流
  • 更灵活的业务感知能力
// 负载均衡算法实现
public class LoadBalanceAlgorithm {
    
    // 轮询算法
    public String roundRobin(List<String> servers, AtomicInteger counter) {
        int index = counter.getAndIncrement() % servers.size();
        return servers.get(index);
    }
    
    // 加权轮询
    public String weightedRoundRobin(Map<String, Integer> serverWeights) {
        // 根据权重选择服务器
        return selectByWeight(serverWeights);
    }
    
    // 最少连接数
    public String leastConnection(Map<String, Integer> connectionCounts) {
        return connectionCounts.entrySet().stream()
            .min(Map.Entry.comparingByValue())
            .map(Map.Entry::getKey)
            .orElse(null);
    }
}
3.2 数据库扩展方案

读写分离架构:

// 读写分离数据源配置
@Configuration
public class ReadWriteDataSourceConfig {
    
    @Bean
    @Primary
    public DataSource dataSource() {
        ReadWriteRoutingDataSource routingDataSource = new ReadWriteRoutingDataSource();
        
        // 写数据源(主库)
        DataSource writeDataSource = createDataSource("master_url");
        
        // 读数据源(从库)
        DataSource readDataSource = createDataSource("slave_url");
        
        Map<ObjectObjecttargetDataSources = new HashMap<>();
        targetDataSources.put("write", writeDataSource);
        targetDataSources.put("read", readDataSource);
        
        routingDataSource.setTargetDataSources(targetDataSources);
        routingDataSource.setDefaultTargetDataSource(writeDataSource);
        
        return routingDataSource;
    }
}

// 读写分离注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ReadOnly {
    // 标记读操作使用从库
}

// AOP切面处理
@Aspect
@Component
public class ReadWriteAspect {
    
    @Before("@annotation(readOnly)")
    public void setReadDataSource(ReadOnly readOnly) {
        DataSourceContext.setRead();
    }
    
    @Before("execution(* *..*Service.*(..)) && !@annotation(ReadOnly)")
    public void setWriteDataSource() {
        DataSourceContext.setWrite();
    }
}

分库分表方案:

// 分库分表路由策略
public class ShardingStrategy {
    
    // 基于用户ID的分片策略
    public String routeByUserId(Long userId) {
        // 分库:userId % 数据库数量
        int dbIndex = userId % DB_COUNT;
        
        // 分表:userId % 每库表数量
        int tableIndex = userId % TABLE_PER_DB;
        
        return String.format("db_%d.user_%d", dbIndex, tableIndex);
    }
    
    // 一致性哈希分片
    public String consistentHash(String key) {
        // 一致性哈希算法实现
        return findNode(key);
    }
}
3.3 限流降级策略

限流算法实现:

// 令牌桶限流
@Service
public class RateLimiter {
    
    private final AtomicLong tokens;
    private final long capacity;
    private final long refillRate; // 令牌/秒
    
    public RateLimiter(long capacity, long refillRate) {
        this.capacity = capacity;
        this.refillRate = refillRate;
        this.tokens = new AtomicLong(capacity);
        startRefillTask();
    }
    
    public boolean tryAcquire() {
        while (true) {
            long current = tokens.get();
            if (current <= 0) {
                return false;
            }
            if (tokens.compareAndSet(current, current - 1)) {
                return true;
            }
        }
    }
    
    private void startRefillTask() {
        ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
        scheduler.scheduleAtFixedRate(() -> {
            tokens.updateAndGet(current -> Math.min(capacity, current + refillRate));
        }, 11, TimeUnit.SECONDS);
    }
}

// 滑动窗口限流
public class SlidingWindowRateLimiter {
    private final ConcurrentNavigableMap<Long, Integer> events = new ConcurrentSkipListMap<>();
    private final long windowSizeMs;
    private final int maxRequests;
    
    public boolean allowRequest() {
        long now = System.currentTimeMillis();
        long windowStart = now - windowSizeMs;
        
        // 清理过期事件
        events.headMap(windowStart).clear();
        
        // 统计窗口内事件数
        int count = events.values().stream().mapToInt(Integer::intValue).sum();
        if (count < maxRequests) {
            events.put(now, events.getOrDefault(now, 0) + 1);
            return true;
        }
        return false;
    }
}

四、实战架构案例

4.1 电商秒杀系统架构
用户请求 → CDN加速 → 负载均衡层 → 限流防护层
    ↓
API网关 → 业务校验 → 缓存预减库存 → 消息队列异步下单
    ↓
订单服务 → 数据库分库分表 → 最终一致性处理

秒杀核心代码:

// 秒杀服务实现
@Service
public class SeckillService {
    
    @Autowired
    private RedisTemplate<StringObject> redisTemplate;
    
    @Autowired
    private KafkaTemplate<StringString> kafkaTemplate;
    
    // 秒杀入口
    public SeckillResponse seckill(SeckillRequest request) {
        // 1. 风险控制:用户频率限制
        if (!rateLimit(request.getUserId())) {
            return SeckillResponse.fail("请求过于频繁");
        }
        
        // 2. 库存预检:Redis原子操作
        Long stock = redisTemplate.opsForValue().decrement("seckill:stock:" + request.getItemId());
        if (stock == null || stock < 0) {
            // 库存不足,恢复计数
            redisTemplate.opsForValue().increment("seckill:stock:" + request.getItemId());
            return SeckillResponse.fail("库存不足");
        }
        
        // 3. 防重检查:用户购买记录
        if (redisTemplate.opsForSet().isMember("seckill:users:" + request.getItemId(), request.getUserId())) {
            return SeckillResponse.fail("已经购买过");
        }
        
        // 4. 发送消息异步处理
        String message = buildSeckillMessage(request);
        kafkaTemplate.send("seckill-orders", message);
        
        return SeckillResponse.success("排队中");
    }
}
4.2 实时排行榜架构
// 实时排行榜服务
@Service
public class RankingService {
    
    @Autowired
    private RedisTemplate<StringObject> redisTemplate;
    
    // 更新分数
    public void updateScore(String userId, double score) {
        // 使用Redis SortedSet
        redisTemplate.opsForZSet().add("user:ranking", userId, score);
    }
    
    // 获取排行榜
    public List<RankingItemgetTopN(int n) {
        Set<ZSetOperations.TypedTuple<Object>> result = 
            redisTemplate.opsForZSet().reverseRangeWithScores("user:ranking"0, n - 1);
        
        return result.stream()
            .map(tuple -> new RankingItem(
                (String) tuple.getValue(), 
                tuple.getScore()))
            .collect(Collectors.toList());
    }
    
    // 分段查询优化大数据集
    public List<RankingItemgetRange(long start, long end) {
        Set<ZSetOperations.TypedTuple<Object>> result = 
            redisTemplate.opsForZSet().reverseRangeWithScores("user:ranking", start, end);
        
        return convertToRankingItems(result);
    }
}

五、性能优化总结

架构优化矩阵:

优化维度技术方案适用场景效果评估
接入层CDN加速、负载均衡静态资源、流量分发减少带宽成本,提升访问速度
应用层微服务、异步化业务复杂、耗时操作提升吞吐量,降低响应时间
数据层缓存、读写分离读多写少、数据热点降低数据库压力,提升QPS
容灾限流降级、多活流量峰值、机房故障保障系统可用性,避免雪崩

六、面试建议

回答框架:

  1. 先分析场景:明确业务特点(读多写少?强一致性?)
  2. 分层阐述:从接入层→应用层→数据层逐层设计
  3. 技术选型:根据场景选择合适的技术方案
  4. 数据估算:进行简单的容量规划(QPS、数据量等)
  5. 容灾设计:考虑故障场景下的应对方案

加分回答点:

  • 提到具体的技术指标(如Redis单机10万QPS)
  • 讨论缓存穿透、雪崩、击穿解决方案
  • 考虑数据一致性保障方案
  • 提及监控和告警体系建设
  • 讨论成本与性能的平衡

常见问题准备:

  1. Redis和MySQL如何保证数据一致性?
  2. 如何设计一个不会超卖的秒杀系统?
  3. 微服务架构下如何监控系统性能?
  4. 如何设计系统的弹性扩缩容机制?
  5. 数据库分库分表的具体实施方案?

本文由微信公众号"程序员小胖"整理发布,转载请注明出处。

明日面试题预告:分布式微服务架构