中间件 | Redis - [秒杀]

§1 秒杀常见问题

超卖
因并发导致且没有加锁导致的
通过 WATCH 解决

超时
redis 连接数不够,导致大并发下大量请求不能获取连接
通过连接池解决

遗留
乐观锁互相影响导致大量失败导致
乐观锁改悲观锁,使用 lua 实现

§2 实现

@Service
public class SeckillDemo {
    private static String SEC_KILL_STOCK_KEY = "SK:STOCK:";
    private static String SEC_KILL_USERS_KEY = "SK:USERS:";

    @Resource
    private RedisTemplate<String,Object> redisTemplate;

    public int seckill(String prodId,String userId){
        String stockKey = SEC_KILL_STOCK_KEY + prodId;
        String usersKey = SEC_KILL_USERS_KEY + prodId;

        //可以增加判断秒杀是否开始的操作,可以用 ttl或时间戳比较进行

        redisTemplate.watch(Arrays.asList(stockKey,usersKey));
        try{
            int count = Integer.parseInt((String) redisTemplate.opsForValue().get(stockKey));
            if(count < 1)
                return -1;

            if(Boolean.TRUE.equals(redisTemplate.opsForSet().isMember(usersKey,userId)))
                return -2;

            redisTemplate.multi();
            redisTemplate.opsForValue().decrement(stockKey);
            redisTemplate.opsForSet().add(usersKey,userId);
            redisTemplate.exec();
        }finally {
            redisTemplate.unwatch();
        }
        return 1;
    }
}

§3 另一种思路

  • 假设有 10000 库存
  • 开设 n 个奖池(候选人池,set),每个奖池都增设一个启用时间,可选的设置最大候选人数
    因为是秒杀,所以每级奖池开启时间相互延时几秒,比如3秒
  • 开设 n+1个中奖池,除了总中奖池,其他与奖池对应,增加中奖池结束标记(bitmap)
  • 设置一开始是第一级奖池,0;前 n-1 个奖池均分秒杀成功集合,或制定更丰富的策略
  • 最后一级奖池作为补漏,后面说
  • 用户开始秒杀
    • 一大波用户的请求进入服务
    • 根据时间,用户进入当前奖池,时间超过、最大候选人数超过时快速返回秒杀失败
    • 其他用户可以 O(1) 的返回自己所在的奖池号
    • 时间超过、最大候选人数超过时,加分布式锁
      • 加锁成功的线程负责替换奖池、触发抽奖 spop,抽出的用户进入对应的中奖池
      • 失败的线程秒杀失败或者10ms后进入下一级奖池
    • 用户的秒杀动作如上面流程,循环至最后一级奖池
  • 用户客户端收到返回后
    • 直接失败的显示失败
    • 否则显示转圈
    • 转圈时周期性访问对应级别的中奖池 ismember
      • 直到中奖池结束标记,池里没有自己,显示失败
      • 否则自己中奖了,客户端给服务端发送确认 ack ,并显示自己秒杀成功
        ack使自己从中奖池进入总中奖池
      • ack 反馈失败的用户自动进入最后一级奖池

理论上好像可以同时解决超卖超时和遗留问题,推测的优缺点

  • 时间可控

  • 阶段性秒杀成功比例可控

  • 玩法非常容易扩展,看产品脑洞

  • 但是相比 lua、原始秒杀,齁重

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值