大环境影响,目前各地消费券发放火热,促消费、扩内需,已成2025主旋律,那么,如何保证消费券有序发放呢,在系统设计和实操中,会遇到哪些问题?应该注意什么?
思考
我们可能会遇到的问题?
- 超卖问题
- 高并发问题
- 安全性问题
- 数据库性能瓶颈问题
如何解决
- 我们先来看传统意义的库存扣减实现,先查询库存,如果还有库存,那么则库存-1,没有库存,则提示下单失败,很简单。
- 但会存在一个问题,并发情况下,如果 A 和 B 都同时查到库存 = 1,都去进行了扣减,那么最后就会产生超卖的情况。
- 那我们将该查询和扣减操作加上锁,就解决了二者的问题,如果是多实例,就升级为分布式锁。
- 那么,第一个方案就出来了,效率如何,我们来看一下。
测试场景
基于优惠券秒杀场景,初始化优惠券至 Redis 缓存中,设置库存为 500 张。
@GetMapping("/init/{couponId}")
public String init(@PathVariable String couponId) {
RedisUtil.set(couponId, 500);
return RedisUtil.get(couponId);
}
这里,我们通过创建测试接口,使用压测工具 Jmeter,设置线程组如下:
线程数:10
Ramp-Up 时间:1秒
循环次数:100
模拟1秒 10 个并发,1000 个请求,那么抢到优惠券的概率为 50%。
秒杀方案一
分布式锁。
- 由于现阶段基本都是分布式架构了(单机也适用),之前封装好的分布式锁工具,直接拿来用:点我。
@GetMapping("/seckill/{couponId}")
public String seckill(@PathVariable String couponId) {
return RedissonUtil.lock(couponId, () -> {
int couponNum = Integer.parseInt(RedisUtil.get(couponId));
log.info("优惠券库存:{}", couponNum);
Assert.isTrue(couponNum > 0, "优惠券抢完了...");
// 库存 - 1
RedisUtil.set(couponId, couponNum - 1);
return