TAOCARTS 知识

反向海淘系统高并发秒杀实战:Redis+Lua分布式锁防超卖-阿里云开发者社区

2026-06-26 系统功能介绍

一、秒杀场景的技术挑战

跨境电商独立站在大促期间(黑五、双十一)经常推出限时秒杀活动。Taocarts的1688代购秒杀场景中,同一商品可能被数千用户同时抢购,库存扣减面临瞬时高并发冲击。

三大核心挑战:

瞬时高并发:秒杀开始瞬间,QPS可达日常的50倍以上

超卖风险:库存扣减不是原子操作时,可能出现超卖

响应延迟:数据库行锁导致接口响应时间从毫秒级退化到秒级

二、Redis+Lua原子扣减方案

传统数据库行锁扣减库存的方式在高并发下性能极差。我们采用Redis Lua脚本实现原子库存扣减,避免竞态条件。

Lua脚本:

lua

-- stock_deduct.lua

-- KEYS[1]: 库存Key

-- ARGV[1]: 扣减数量

-- ARGV[2]: 超时时间(秒)

local stock_key = KEYS[1]

local deduct_qty = tonumber(ARGV[1])

local timeout = tonumber(ARGV[2])

-- 获取当前库存

local current = tonumber(redis.call('get', stock_key) or 0)

-- 库存不足返回0

if current < deduct_qty then

return 0

end

-- 原子扣减

local new_stock = redis.call('decrby', stock_key, deduct_qty)

if new_stock < 0 then

-- 极端情况:扣减后为负,回滚

redis.call('incrby', stock_key, deduct_qty)

return 0

end

-- 设置过期时间(防止库存Key永久占用内存)

redis.call('expire', stock_key, timeout)

return new_stock

Java调用实现:

java

@Service

public class StockService {

@Autowired

private StringRedisTemplate redisTemplate;

private static final String STOCK_PREFIX = "taocarts:stock:";

private static final DefaultRedisScript DEDUCT_SCRIPT;

static {

DEDUCT_SCRIPT = new DefaultRedisScript<>();

DEDUCT_SCRIPT.setScriptSource(

new ResourceScriptSource(new ClassPathResource("lua/stock_deduct.lua"))

);

DEDUCT_SCRIPT.setResultType(Long.class);

}

public boolean deductStock(Long productId, Integer quantity) {

String stockKey = STOCK_PREFIX + productId;

Long result = redisTemplate.execute(

DEDUCT_SCRIPT,

Collections.singletonList(stockKey),

String.valueOf(quantity),

String.valueOf(3600) // 1小时过期

);

return result != null && result >= 0;

}

}

三、分布式锁防止重复下单

同一用户可能因网络延迟多次点击下单按钮。我们需要防止同一用户对同一商品重复下单。

java

@RestController

@RequestMapping("/api/seckill")

public class SeckillController {

@Autowired

private StringRedisTemplate redisTemplate;

@Autowired

private StockService stockService;

@Autowired

private OrderService orderService;

@PostMapping("/place")

public Result placeOrder(@RequestBody SeckillRequest request) {

Long userId = request.getUserId();

Long productId = request.getProductId();

// 1. 分布式锁:防止同一用户重复下单

String lockKey = "taocarts:seckill:lock:" + userId + ":" + productId;

Boolean locked = redisTemplate.opsForValue()

.setIfAbsent(lockKey, "1", Duration.ofSeconds(3));

if (!locked) {

return Result.fail("请勿重复提交");

}

try {

// 2. 原子扣减库存

boolean success = stockService.deductStock(productId, 1);

if (!success) {

return Result.fail("库存不足");

}

// 3. 创建订单(异步)

orderService.createSeckillOrder(userId, productId);

return Result.success("下单成功");

} finally {

redisTemplate.delete(lockKey);

}

}

}

四、消息队列削峰填谷

秒杀订单创建后,后续的采购、仓储、物流操作通过消息队列异步处理,避免阻塞主线程。

java

@Component

public class SeckillOrderConsumer {

@Autowired

private PurchaseService purchaseService;

@RabbitListener(queues = "seckill.order.queue")

public void handleSeckillOrder(SeckillOrderMessage msg) {

// 执行采购、仓储、物流等耗时操作

purchaseService.purchaseFrom1688(msg.getOrderId());

}

}

五、效果评估

这套秒杀方案在生产环境中经历了黑五大促的考验:

QPS承载:从500提升至8000,提升16倍

超卖率:从2.3%降至0%

平均响应时间:从1200ms降至80ms

Redis+Lua原子扣减方案已在Taocarts跨境电商独立站系统的大促场景中稳定运行,确保了大促期间零超卖事故