从零搭建10万级QPS优惠券系统:高并发架构实战指南

一、系统架构分层设计:解耦与扩展性

1.1 分层架构选型

采用经典的三层架构(接入层-服务层-数据层),结合业务特性增加优惠券模板服务用户领券服务核销服务三个独立微服务。接入层使用Nginx+Lua实现动态路由,服务层通过gRPC进行内部通信,数据层采用分库分表+缓存双写策略。

  1. # Nginx动态路由配置示例
  2. upstream coupon_services {
  3. server 10.0.0.1:8080 weight=5;
  4. server 10.0.0.2:8080 weight=3;
  5. server 10.0.0.3:8080 backup;
  6. }
  7. server {
  8. location /api/coupon {
  9. set $backend "";
  10. if ($arg_action = "claim") {
  11. set $backend coupon_claim;
  12. }
  13. proxy_pass http://$backend;
  14. }
  15. }

1.2 服务拆分原则

  • 模板服务:处理优惠券规则配置(满减、折扣、有效期等)
  • 领券服务:实现限流控制、库存扣减、用户黑名单校验
  • 核销服务:对接订单系统,验证优惠券有效性
  • 监控服务:实时采集QPS、错误率、延迟等指标

二、数据库优化:支撑百万级并发读写

2.1 分库分表策略

采用用户ID取模分库+优惠券ID范围分表的混合方案:

  1. -- 创建分库分表示例
  2. CREATE DATABASE coupon_db_0;
  3. CREATE DATABASE coupon_db_1;
  4. CREATE TABLE coupon_db_0.coupon_0 (
  5. id BIGINT PRIMARY KEY,
  6. user_id BIGINT,
  7. template_id BIGINT,
  8. status TINYINT,
  9. expire_time DATETIME
  10. ) PARTITION BY RANGE (id) (
  11. PARTITION p0 VALUES LESS THAN (1000000),
  12. PARTITION p1 VALUES LESS THAN (2000000)
  13. );

2.2 读写分离实现

通过MyCat中间件实现自动路由,写请求发往主库,读请求按权重分配至从库。配置中设置writeHostreadHost节点,并启用连接池复用。

2.3 事务处理方案

对库存扣减等关键操作采用分布式锁+本地事务组合:

  1. // Redis分布式锁实现
  2. public boolean tryAcquireLock(String key, long expire) {
  3. String result = redisTemplate.opsForValue().setIfAbsent(key, "1", expire, TimeUnit.SECONDS);
  4. return Boolean.TRUE.equals(result);
  5. }
  6. // 库存扣减事务示例
  7. @Transactional
  8. public boolean deductStock(Long couponId) {
  9. if (!tryAcquireLock("lock:" + couponId, 5)) {
  10. throw new RuntimeException("获取锁失败");
  11. }
  12. try {
  13. Coupon coupon = couponMapper.selectById(couponId);
  14. if (coupon.getStock() <= 0) {
  15. return false;
  16. }
  17. couponMapper.updateStock(couponId, coupon.getStock() - 1);
  18. return true;
  19. } finally {
  20. redisTemplate.delete("lock:" + couponId);
  21. }
  22. }

三、缓存体系构建:毫秒级响应

3.1 多级缓存架构

  • 本地缓存:Guava Cache存储热点数据(如用户已领优惠券列表)
  • 分布式缓存:Redis集群存储模板规则、全局库存
  • CDN缓存:静态资源(优惠券图片、规则说明)
  1. // Guava本地缓存配置
  2. LoadingCache<String, CouponTemplate> templateCache = CacheBuilder.newBuilder()
  3. .maximumSize(10000)
  4. .expireAfterWrite(10, TimeUnit.MINUTES)
  5. .build(new CacheLoader<String, CouponTemplate>() {
  6. @Override
  7. public CouponTemplate load(String templateId) {
  8. return redisTemplate.opsForValue().get("template:" + templateId);
  9. }
  10. });

3.2 缓存穿透解决方案

  • 空值缓存:对不存在的优惠券ID缓存NULL值
  • 布隆过滤器:预加载所有有效优惠券ID
  • 互斥锁:缓存未命中时只允许一个请求查询数据库

3.3 缓存雪崩预防

  • 随机过期时间:在基础过期时间上增加±5分钟随机值
  • 多级缓存:本地缓存与Redis缓存设置不同过期时间
  • 限流降级:缓存失效时触发流量控制

四、流量控制与降级策略

4.1 动态限流算法

采用令牌桶算法实现平滑限流:

  1. // Guava RateLimiter实现
  2. private final RateLimiter rateLimiter = RateLimiter.create(1000.0); // 每秒1000个请求
  3. public boolean tryAcquire() {
  4. return rateLimiter.tryAcquire();
  5. }
  6. // 分布式限流(Redis+Lua实现)
  7. String luaScript =
  8. "local key = KEYS[1]\n" +
  9. "local limit = tonumber(ARGV[1])\n" +
  10. "local current = tonumber(redis.call('GET', key) or '0')\n" +
  11. "if current + 1 > limit then\n" +
  12. " return 0\n" +
  13. "else\n" +
  14. " redis.call('INCRBY', key, '1')\n" +
  15. " redis.call('EXPIRE', key, '1')\n" +
  16. " return 1\n" +
  17. "end";

4.2 服务降级方案

  • 熔断机制:Hystrix实现服务调用超时自动降级
  • 静态页面:系统过载时返回预生成的HTML
  • 队列削峰:将领券请求写入Kafka,异步处理

五、压测与优化实战

5.1 全链路压测方案

使用JMeter模拟10万QPS请求:

  1. <!-- JMeter线程组配置 -->
  2. <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup">
  3. <stringProp name="ThreadGroup.num_threads">1000</stringProp>
  4. <stringProp name="ThreadGroup.ramp_time">60</stringProp>
  5. <stringProp name="ThreadGroup.duration">300</stringProp>
  6. </ThreadGroup>
  7. <!-- HTTP请求采样器 -->
  8. <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy">
  9. <elementProp name="HTTPsampler.Arguments" elementType="Arguments">
  10. <collectionProp name="Arguments.arguments">
  11. <elementProp name="couponId" elementType="HTTPArgument">
  12. <stringProp name="Argument.value">${__Random(1,1000000)}</stringProp>
  13. </elementProp>
  14. </collectionProp>
  15. </elementProp>
  16. </HTTPSamplerProxy>

5.2 性能瓶颈定位

  • 慢查询分析:通过pt-query-digest定位耗时SQL
  • 链路追踪:SkyWalking实现全链路调用跟踪
  • 火焰图分析:使用async-profiler定位CPU热点

5.3 优化效果验证

经过三轮优化后关键指标对比:
| 优化项 | 优化前QPS | 优化后QPS | 延迟降低 |
|————————|—————-|—————-|—————|
| 数据库分库 | 3,200 | 8,500 | 62% |
| Redis集群 | 5,800 | 12,000 | 51% |
| 连接池复用 | 7,200 | 15,000 | 52% |
| 静态化改造 | 9,500 | 22,000 | 57% |

六、运维监控体系

6.1 监控指标设计

  • 业务指标:领券成功率、核销率、优惠金额
  • 系统指标:QPS、响应时间、错误率
  • 基础设施:CPU使用率、内存占用、网络IO

6.2 告警策略配置

  • 阈值告警:QPS持续5分钟超过90%峰值时触发
  • 趋势告警:响应时间10分钟内上升30%时告警
  • 关联告警:数据库连接数超限+慢查询数激增

6.3 自动化运维

  • 弹性伸缩:根据QPS自动调整容器副本数
  • 金丝雀发布:新版本先接收1%流量验证
  • 混沌工程:定期模拟网络分区、服务宕机等故障

七、实战经验总结

  1. 渐进式优化:先解决数据库瓶颈,再优化缓存,最后处理网络层
  2. 全链路压测:必须模拟真实用户行为,包括参数分布、请求间隔
  3. 容灾设计:关键服务需具备跨机房部署能力
  4. 数据一致性:最终一致性方案需设计补偿机制
  5. 成本平衡:在QPS、延迟、硬件成本间找到最佳平衡点

通过上述方案,某电商平台的优惠券系统成功支撑了双11期间12.7万QPS的峰值流量,领券接口平均响应时间控制在48ms以内,系统可用性达到99.99%。实际开发中需根据业务特性调整技术选型,建议先实现基础功能,再逐步优化高并发场景。