public class TokenService {
private final RedisTemplate<String, Integer> redisTemplate;
// 生成秒杀令牌(每个用户限领1个)
public boolean generateToken(Long userId, Long goodsId) {
String key = "seckill:token:" + userId + ":" + goodsId;
return redisTemplate.opsForValue().setIfAbsent(key, 1, 60, TimeUnit.SECONDS);
}
// 校验令牌并删除(防止重复使用)
public boolean validateToken(Long userId, Long goodsId) {
String key = "seckill:token:" + userId + ":" + goodsId;
return redisTemplate.delete(key);
}
}
3. 异步队列削峰(Kafka 实现)
typescript复制代码
public class SeckillProducer {
private final KafkaTemplate<String, SeckillRequest> kafkaTemplate;
// 发送秒杀请求到队列(削峰填谷)
public void sendSeckillRequest(SeckillRequest request) {
kafkaTemplate.send("seckill_topic", request.getGoodsId().toString(), request);
}
}
@Service
public class SeckillConsumer {
private final StockPreheatService stockService;
private final OrderService orderService;
@KafkaListener(topics = "seckill_topic", groupId = "seckill_group")
public void processSeckillRequest(SeckillRequest request) {
// 1. 库存扣减
if (stockService.deductStock(request.getGoodsId())) {
// 2. 生成订单(数据库事务)
createOrder(request);
}
}
private void createOrder(SeckillRequest request) {
OrderEntity order = new OrderEntity();
order.setGoodsId(request.getGoodsId());
order.setUserId(request.getUserId());
order.setCreateTime(LocalDateTime.now());
orderService.save(order);
}
}
四、数据库层防超卖设计
1. 库存扣减的三级校验
Redis 预扣:通过 Lua 脚本保证原子性扣减(内存级校验)
数据库行锁:扣减时使用SELECT ... FOR UPDATE锁定库存记录
版本号校验:通过UPDATE goods SET stock = stock - 1 WHERE id=? AND version=?防止 ABA 问题
2. 数据库表结构优化
sql复制代码
CREATE TABLE seckill_goods (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
goods_id BIGINT NOT NULL COMMENT '商品ID',
stock INT NOT NULL COMMENT '库存',
version INT DEFAULT 0 COMMENT '乐观锁版本号',
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- 扣减库存SQL(带版本号校验)
UPDATE seckill_goods
SET stock = stock - 1, version = version + 1
WHERE goods_id = ? AND stock > 0 AND version = ?