秒杀活动Java代码实例分析
秒杀活动Java代码实战:像抢演唱会门票一样写代码
上个月老张在公司楼下抽烟时,突然接到老板电话:"咱们下个月周年庆要做个秒杀活动,你负责的后端系统可别给我掉链子!"他掐灭烟头的手微微发抖——这场景,每个Java工程师都懂。
一、秒杀系统的核心难点
想象下春运抢票现场,我们的代码就是那个维持秩序的保安。关键要解决三个问题:
- 库存超卖:100件商品卖出去101件
- 系统雪崩:流量洪峰冲垮服务器
- 黄牛脚本:机器人比人手速快100倍
解决方案 | 响应时间 | 实现复杂度 | 适用场景 |
---|---|---|---|
Redis原子操作 | 5ms | ★★☆ | 中小型活动 |
数据库行级锁 | 50ms | ★★★ | 传统系统改造 |
队列削峰 | 100ms | ★☆☆ | 百万级并发 |
1.1 库存操作的原子性
记得去年双十一某电商的"-1元购"事故吗?这就是典型的非原子操作导致的。我们来看段改良版的代码:
public boolean deductStock(Long itemId) {
String key = "stock:" + itemId;
// Lua脚本保证原子性
String script = "if redis.call('exists',KEYS) == 1 then\
+
local stock = tonumber(redis.call('get', KEYS))\
+
if stock > 0 then\
+
redis.call('decr', KEYS)\
+
return 1\
+
end\
+
end\
+
return 0";
Object result = redisTemplate.execute(
new DefaultRedisScript<>(script, Long.class),
Collections.singletonList(key));
return (Long)result == 1;
}
二、流量控制的三重门
就像地铁早高峰的限流措施,我们需要:
- 令牌桶算法控制请求速率
- Nginx层做请求过滤
- 验证码动态加载机制
2.1 分布式限流实现
使用Guava的RateLimiter只能单机限流,分布式环境下得这样玩:
@RestController
public class SeckillController {
// 每秒钟允许100个请求
private RateLimiter rateLimiter = RateLimiter.create(100);
@PostMapping("/seckill")
public Response seckill(@RequestBody Request request) {
if (!rateLimiter.tryAcquire) {
return Response.error("活动太火爆,请稍后再试");
// 业务逻辑处理
}
三、性能优化对比实验
优化措施 | QPS提升 | 资源消耗 |
---|---|---|
本地缓存+Redis | 300% | 内存增加200MB |
数据库连接池优化 | 150% | CPU上升5% |
静态资源CDN | 200% | 带宽成本增加 |
上周帮朋友公司做压力测试时发现,预热Redis集群能让首屏加载时间从800ms降到200ms。具体做法是在活动开始前5分钟,把库存数据从MySQL同步到Redis,并设置合理的过期时间。
3.1 缓存击穿防御
当缓存失效瞬间遇到大并发查询,就像突然打开的水闸。用红锁(RedLock)实现分布式锁:
public Object getData(String key) {
Object value = redis.get(key);
if (value == null) {
RLock lock = redissonClient.getLock(key);
if (lock.tryLock) {
try {
// 查数据库并回填缓存
} finally {
lock.unlock;
} else {
// 等待或重试
return value;
}
窗外飘来咖啡的香气,运维组的同事正在部署灰度环境。秒杀系统就像精心设计的交响乐,每个乐器都要在正确的时间奏响。下次聊聊我们在数据库分库分表时踩过的那些坑吧。
评论
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。
网友留言(0)