1. 优惠券秒杀的业务挑战与技术选型
去年双十一期间,我们电商平台的200元无门槛优惠券在3秒内被抢购一空,服务器却因为瞬时高并发险些崩溃。这种秒杀场景对系统架构提出了极高要求:既要保证超卖问题零容忍,又要应对突发流量冲击。Redis凭借其单线程原子性操作和超高吞吐量,成为解决这类问题的首选方案。
传统数据库在秒杀场景存在三大致命伤:首先,ACID事务在高并发下性能急剧下降;其次,行级锁在万人抢购时根本扛不住;最后,库存校验与订单创建难以保证原子性。而Redis的WATCH/MULTI/EXEC机制配合Lua脚本,能完美实现"库存扣减+订单创建"的原子操作。
2. Redis秒杀架构核心设计
2.1 库存预热与分段锁设计
我们采用预扣减策略,活动开始前将优惠券库存加载到Redis:
# 预热1000张优惠券 SET stock:coupon_1001 1000但直接使用DECR命令会导致热点Key问题。我们创新性地采用分段锁方案:
# 将库存拆分为10个段 for i in range(10): redis_client.set(f'stock:coupon_1001_{i}', 100)用户抢购时随机选择分段进行扣减,通过Nginx负载均衡将请求分散到不同Redis节点,实测QPS提升8倍。
2.2 分布式锁防重复提交
为防止用户重复提交,采用Redisson实现的分布式锁:
RLock lock = redissonClient.getLock("coupon:" + userId); try { if (lock.tryLock(0, 10, TimeUnit.SECONDS)) { // 执行业务逻辑 } } finally { lock.unlock(); }特别注意设置合理的锁超时时间,我们通过压测确定10秒是最优值,既不会因过短导致业务中断,也不会因过长阻塞其他请求。
3. 秒杀流程的原子性保障
3.1 Lua脚本实现复合操作
以下脚本保证"校验库存→扣减库存→生成订单"的原子性:
local stockKey = KEYS[1] local orderKey = KEYS[2] local userId = ARGV[1] local couponId = ARGV[2] if tonumber(redis.call('GET', stockKey)) <= 0 then return 0 end redis.call('DECR', stockKey) redis.call('HSET', orderKey, userId, couponId) return 13.2 库存回滚机制
当订单创建失败时,需要通过消息队列触发库存回滚:
// RabbitMQ消费者示例 @RabbitListener(queues = "stock.recover.queue") public void recoverStock(String message) { redisTemplate.opsForValue().increment("stock:coupon_1001"); log.info("库存回滚成功:{}", message); }4. 高并发优化实践
4.1 多级缓存策略
- 本地缓存:使用Caffeine缓存库存信息,有效期50ms
- Redis集群:采用三主三从架构,读写分离
- 库存缓存:通过
SETNX stock:coupon_1001:lock 1实现互斥更新
4.2 流量削峰方案
我们采用三级缓冲策略:
- 前端:随机延迟按钮点击(300-800ms)
- 网关:令牌桶限流(5000请求/秒)
- 服务层:RabbitMQ异步削峰
# 令牌桶算法实现 class TokenBucket: def __init__(self, capacity, fill_rate): self.capacity = float(capacity) self.tokens = float(capacity) self.fill_rate = float(fill_rate) self.last_time = time.time() def consume(self, tokens=1): now = time.time() self.tokens = min( self.capacity, self.tokens + (now - self.last_time) * self.fill_rate ) if self.tokens >= tokens: self.tokens -= tokens self.last_time = now return True return False5. 异常处理与监控
5.1 熔断降级策略
配置Hystrix熔断规则:
hystrix: command: default: execution: isolation: thread: timeoutInMilliseconds: 2000 circuitBreaker: requestVolumeThreshold: 20 sleepWindowInMilliseconds: 50005.2 监控指标埋点
关键监控指标包括:
- Redis命中率(要求>99.5%)
- 库存扣减耗时(P99<50ms)
- 订单创建成功率(>99.9%)
通过Grafana配置实时看板:
SELECT rate(redis_commands_total[1m]) as qps, redis_memory_used_bytes/1024/1024 as memory_used_mb FROM redis_exporter6. 实战中的经验教训
库存分段数不是越多越好:我们通过压测发现,当分段超过16个时,Redis集群内部通信开销反而会降低性能
Lua脚本长度限制:阿里云Redis限制脚本不能超过10万字符,我们曾因脚本过大导致执行失败
热点Key监控必不可少:使用
redis-cli --hotkeys定期扫描,发现有个别用户通过脚本抢购导致Key过热秒杀结果异步通知:采用WebSocket推送替代短信通知,将300ms的短信延迟降低到50ms以内
防刷策略组合拳:
- 设备指纹识别(前端埋点)
- 行为模式分析(鼠标移动轨迹)
- 区域访问限制(异常IP段拦截)
这套方案在618大促中经受住了实战检验:200万张优惠券在5秒内售罄,系统保持平稳运行,Redis集群CPU峰值仅65%。关键是要根据业务特点持续调优参数,没有放之四海而皆准的完美方案。