美文网首页
redis秒杀

redis秒杀

作者: ChandlerQian | 来源:发表于2020-09-27 13:37 被阅读0次

    一个秒杀系统是非常复杂的,一般来说,秒杀可以分为一下三个阶段:

    1.准备阶段,会提前载入一些必需的数据到缓存中,并提前预热业务数据,用户会不断刷新页面,来查看秒杀是否开始;
    2.抢购阶段,就是我们通常说的秒杀,会产生瞬时的高并发流量,对资源进行集中操作;
    3.结束清算,主要完成数据的一致性,处理一些异常情况和回仓操作。

    首先设计一个 Hash 数据结构,来支持库存的扣减。

    seckill:goods:${goodsId}{ 
        total: 100, 
        start: 0, 
        alloc:0 
    }
    

    在这个 Hash 数据结构中,有以下三个重要部分:

    total 是一个静态值,表示要秒杀商品的数量,在秒杀开始前,会将这个数值载入到缓存中。

    start 是一个布尔值。秒杀开始前的值为 0;通过后台或者定时,将这个值改为 1,则表示秒杀开始。

    此时,alloc 将会记录已经被秒杀的商品数量,直到它的值达到 total 的上限。

    static final String goodsId = "seckill:goods:%s"; 
     
    String getKey(String id) { 
        return String.format(goodsId, id); 
    } 
    public void prepare(String id, int total) { 
        String key = getKey(id); 
        Map<String, Integer> goods = new HashMap<>(); 
        goods.put("total", total); 
        goods.put("start", 0); 
        goods.put("alloc", 0); 
        redisTemplate.opsForHash().putAll(key, goods); 
     }
    

    秒杀的时候,首先需要判断库存,才能够对库存进行锁定。这两步动作并不是原子的,在分布式环境下,多台机器同时对 Redis 进行操作,就会发生同步问题。

    为了解决同步问题,一种方式就是使用 Lua 脚本,把这些操作封装起来,这样就能保证原子性;另外一种方式就是使用分布式锁,在这里就暂时不介绍了。

    下面是一个调试好的 Lua 脚本,可以看到一些关键的比较动作,和 HINCRBY 命令,能够成为一个原子操作。

    local falseRet = "0" 
    local n = tonumber(ARGV[1]) 
    local key = KEYS[1] 
    local goodsInfo = redis.call("HMGET",key,"total","alloc") 
    local total = tonumber(goodsInfo[1]) 
    local alloc = tonumber(goodsInfo[2]) 
    if not total then 
        return falseRet 
    end 
    if total >= alloc + n  then 
        local ret = redis.call("HINCRBY",key,"alloc",n) 
        return tostring(ret) 
    end 
    return falseRet
    

    对应的秒杀代码如下,由于我们使用的是 String 的序列化方式,所以会把库存的扣减数量先转化为字符串,然后再调用 Lua 脚本。

    public int secKill(String id, int number) { 
        String key = getKey(id); 
        Object alloc =  redisTemplate.execute(script, Arrays.asList(key), String.valueOf(number)); 
        return Integer.valueOf(alloc.toString()); 
    }
    

    执行仓库里的 testSeckill 方法。启动 1000 个线程对 100 个资源进行模拟秒杀,可以看到生成了 100 条记录,同时其他的线程返回的是 0,表示没有秒杀到。

    相关文章

      网友评论

          本文标题:redis秒杀

          本文链接:https://www.haomeiwen.com/subject/ignzyktx.html