java如何保证抢购
使用分布式锁
Redis的SETNX命令可以实现分布式锁,通过设置一个唯一的key来确保同一时间只有一个请求能获取锁。设置锁时可以添加过期时间,避免死锁。
public boolean tryLock(String lockKey, String requestId, int expireTime) {
return redisTemplate.opsForValue().setIfAbsent(lockKey, requestId, expireTime, TimeUnit.SECONDS);
}
public boolean unlock(String lockKey, String requestId) {
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
return redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class),
Collections.singletonList(lockKey), requestId) == 1;
}
库存预减与异步处理
在Redis中预减库存,避免直接操作数据库。使用Lua脚本保证操作的原子性,库存不足时直接返回失败。
String script = "if tonumber(redis.call('get', KEYS[1])) > 0 then " +
"redis.call('decr', KEYS[1]); " +
"return 1; " +
"else " +
"return 0; " +
"end";
Long result = redisTemplate.execute(new DefaultRedisScript<>(script, Long.class),
Collections.singletonList("stock:" + productId));
if (result == 0) {
throw new RuntimeException("库存不足");
}
消息队列削峰
将抢购请求放入消息队列(如RabbitMQ或Kafka),后台消费者按顺序处理订单。这种方式能有效缓解高并发压力。
@RabbitListener(queues = "seckill.queue")
public void processSeckillOrder(SeckillMessage message) {
// 处理订单逻辑
}
限流措施
使用Guava的RateLimiter或Redis实现限流,控制单位时间内的请求量。例如每秒只允许1000个请求通过。
RateLimiter rateLimiter = RateLimiter.create(1000);
if (!rateLimiter.tryAcquire()) {
throw new RuntimeException("系统繁忙,请稍后再试");
}
数据库乐观锁
在更新库存时使用版本号或时间戳实现乐观锁,防止超卖。

UPDATE product
SET stock = stock - 1, version = version + 1
WHERE id = ? AND version = ? AND stock > 0
缓存预热与多层校验
活动开始前将库存加载到Redis,并在下单时进行多层校验:缓存库存检查→分布式锁→数据库最终校验。






