美文网首页
解读秒杀业务:Redis如何助力高并发秒杀系统以及完美解决超卖问

解读秒杀业务:Redis如何助力高并发秒杀系统以及完美解决超卖问

作者: Java柚子 | 来源:发表于2020-10-22 16:15 被阅读0次

    秒杀业务

    在电商领域,存在着典型的秒杀业务场景,那何谓秒杀场景呢。简单的来说就是一件商品的购买人数远远大于这件商品的库存,而且这件商品在很短的时间内就会被抢购一空。比如每年的618、双11大促,小米新品促销等业务场景,就是典型的秒杀业务场景。

    秒杀业务最大的特点就是瞬时并发流量高,在电商系统中,库存数量往往会远远小于并发流量,比如:天猫的秒杀活动,可能库存只有几百、几千件,而瞬间涌入的抢购并发流量可能会达到几十到几百万。

    所以,我们可以将秒杀系统的业务特点总结如下。

    需要更多Java知识点和面试题的朋友可以加q群:1103806531  备注:简书   免费领取~

    (1)限时、限量、限价

    在规定的时间内进行;秒杀活动中商品的数量有限;商品的价格会远远低于原来的价格,也就是说,在秒杀活动中,商品会以远远低于原来的价格出售。

    例如,秒杀活动的时间仅限于某天上午10点到10点半,商品数量只有10万件,售完为止,而且商品的价格非常低,例如:1元购等业务场景。

    限时、限量和限价可以单独存在,也可以组合存在。

    (2)活动预热

    需要提前配置活动;活动还未开始时,用户可以查看活动的相关信息;秒杀活动开始前,对活动进行大力宣传。

    (3)持续时间短

    购买的人数数量庞大;商品会迅速售完。

    在系统流量呈现上,就会出现一个突刺现象,此时的并发访问量是非常高的,大部分秒杀场景下,商品会在极短的时间内售完。

    秒杀三阶段

    通常,从秒杀开始到结束,往往会经历三个阶段:

    准备阶段:这个阶段也叫作系统预热阶段,此时会提前预热秒杀系统的业务数据,往往这个时候,用户会不断刷新秒杀页面,来查看秒杀活动是否已经开始。在一定程度上,通过用户不断刷新页面的操作,可以将一些数据存储到Redis中进行预热。

    秒杀阶段:这个阶段主要是秒杀活动的过程,会产生瞬时的高并发流量,对系统资源会造成巨大的冲击,所以,在秒杀阶段一定要做好系统防护。

    结算阶段: 完成秒杀后的数据处理工作,比如数据的一致性问题处理,异常情况处理,商品的回仓处理等。

    Redis助力秒杀系统

    我们可以在Redis中设计一个Hash数据结构,来支持商品库存的扣减操作,如下所示。

    seckill:goodsStock:${goodsId}{

        totalCount:200,

        initStatus:0,

        seckillCount:0

    }

    在我们设计的Hash数据结构中,有三个非常主要的属性。

    totalCount:表示参与秒杀的商品的总数量,在秒杀活动开始前,我们就需要提前将此值加载到Redis缓存中。

    initStatus:我们把这个值设计成一个布尔值。秒杀开始前,这个值为0,表示秒杀未开始。可以通过定时任务或者后台操作,将此值修改为1,则表示秒杀开始。

    seckillCount:表示秒杀的商品数量,在秒杀过程中,此值的上限为totalCount,当此值达到totalCount时,表示商品已经秒杀完毕。

    我们可以通过下面的代码片段在秒杀预热阶段,将要参与秒杀的商品数据加载的缓存。

    /**

    * @author binghe

    * @description 秒杀前构建商品缓存代码示例

    */

    public class SeckillCacheBuilder{

        private static final String GOODS_CACHE = "seckill:goodsStock:";

        private String getCacheKey(String id) {

            return  GOODS_CACHE.concat(id);

        }

        public void prepare(String id, int totalCount) {

            String key = getCacheKey(id);

            Map<String, Integer> goods = new HashMap<>();

            goods.put("totalCount", totalCount);

            goods.put("initStatus", 0);

            goods.put("seckillCount", 0);

            redisTemplate.opsForHash().putAll(key, goods);

        }

    }

    秒杀开始的时候,我们需要在代码中首先判断缓存中的seckillCount值是否小于totalCount值,如果seckillCount值确实小于totalCount值,我们才能够对库存进行锁定。在我们的程序中,这两步其实并不是原子性的。如果在分布式环境中,我们通过多台机器同时操作Redis缓存,就会发生同步问题,进而引起“超卖”的严重后果。

    在电商领域,有一个专业名词叫作“超卖”。顾名思义:“超卖”就是说卖出的商品数量比商品的库存数量多,这在电商领域是一个非常严重的问题。那么,我们如何解决“超卖”问题呢?

    Lua脚本完美解决超卖问题

    我们如何解决多台机器同时操作Redis出现的同步问题呢?一个比较好的方案就是使用Lua脚本。我们可以使用Lua脚本将Redis中扣减库存的操作封装成一个原子操作,这样就能够保证操作的原子性,从而解决高并发环境下的同步问题。

    例如,我们可以编写如下的Lua脚本代码,来执行Redis中的库存扣减操作。

    local resultFlag = "0"

    local n = tonumber(ARGV[1])

    local key = KEYS[1]

    local goodsInfo = redis.call("HMGET",key,"totalCount","seckillCount")

    local total = tonumber(goodsInfo[1])

    local alloc = tonumber(goodsInfo[2])

    if not total then

        return resultFlag

    end

    if total >= alloc + n  then

        local ret = redis.call("HINCRBY",key,"seckillCount",n)

        return tostring(ret)

    end

    return resultFlag

    我们可以使用如下的Java代码来调用上述Lua脚本。

    public int secKill(String id, int number) {

        String key = getCacheKey(id);

        Object seckillCount =  redisTemplate.execute(script, Arrays.asList(key), String.valueOf(number));

        return Integer.valueOf(seckillCount.toString());

    }

    这样,我们在执行秒杀活动时,就能够保证操作的原子性,从而有效的避免数据的同步问题,进而有效的解决了“超卖”问题。

    总结

    为大家整理了各个知识点模块整理文档和更多大厂面试真题,有需要的朋友可以加q群:1103806531  备注:简书   免费领取~

    相关文章

      网友评论

          本文标题:解读秒杀业务:Redis如何助力高并发秒杀系统以及完美解决超卖问

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