关于电商库存的读取

在这里插入图片描述

一. 问题背景

背景起因在单例模式之懒加载模式。对懒加载不熟悉的伙伴推荐先阅读单例模式之懒加载模式。此篇只对电商库存做分析。

二. 前言

电商的库存是用redis做缓存来解决的,估计是应对秒杀活动时页面流量很高,频繁读取数据库效果不佳,也会出现超卖现象。

三. 创建订单

获取库存的缘于创建订单,如下是创建订单涉及到的过程:

创建订单
添加客户信息
加载临时订单
加载sku信息并锁库存
计算营销活动
创建订单

四. 加载sku信息并锁库存

取不到
取到
不能
加载sku信息并锁库存
获取多个sku信息
获取sku库存
获取sku价格
通过shopId和productId获取商品库存信息
从缓存中取
从数据库取
根据判断结果设置是否可销售
获取构建商品库存的锁,
调用构建商品库存的方法,
如果50ms没有获取到redis锁,
则直接返回null
是否获取到锁
二次检查:从缓存获取sku库存
如果获取redis锁超时,则抛异常
是否能获取到
返回商品库存数组出去
去数据库查,
并设置到缓存中,
即使是null也设

五. 获取库存的代码

先简单浏览下面2个代码块,无需纠结某处。看的过程中,思考是否与单例模式懒加载的双重检查锁定的写法有相同

/**
     * 通过productId获取商品库存信息
     * @param productId 商品ID
     * @param shopId 店铺ID
     * @return
     */
    @Override
    public List<ProductVariantInventoryDTO> getProductInventoryByProductId(Long shopId, Long productId){
        //从缓存中取
        List<ProductVariantInventoryDTO> productVariantInventoryDTOS = productInventoryCache.getProductVariantInventoryDTOFromCache(shopId ,productId);
        // 第一次判空
        if(productVariantInventoryDTOS == null){ 
            productVariantInventoryDTOS = getInventoryByVariantIdFromDB(shopId, productId);
        }
       // 此处省略判断销售状态的代码
        return productVariantInventoryDTOS;
    }
public List<ProductVariantInventoryDTO> getInventoryByVariantIdFromDB(Long shopId, Long productId){
        String createProductInventoryKey = CacheKeyGenerateUtil.generateCreateProductInventoryKey(shopId, productId);
        //获取构建商品库存的锁,调用构建商品库存的方法,如果50ms没有获取到redis锁,则直接返回null
        boolean success = distributedLock.lock(createProductInventoryKey, 50, 5, 10);
        if(success){
            try{
                //二次检查
                List<ProductVariantInventoryDTO> productVariantInventoryDTOS = productInventoryCache.getProductVariantInventoryDTOFromCache(shopId ,productId);
                // 第二次判空
                if(productVariantInventoryDTOS != null){
                    return productVariantInventoryDTOS;
                }
                ProductInventoryQuery query = new ProductInventoryQuery();
                query.setShopId(shopId);
                query.setProductId(productId);
                List<ProductVariantInventoryDTO> productInventories = inventoryMapper.getInventoryInfo(query);
                //如果查出来是空的也要缓存起来,防止缓存穿透,如果对商品库存有编辑的话,需要在编辑接口中删除并重构缓存
                productInventoryCache.setProductVariantInventoryDTO(shopId, productId, productInventories);
                return productInventories;
            }finally{
                distributedLock.releaseLock(createProductInventoryKey);
            }
        }else{
            //如果获取redis锁超时,则直接返回null
            throw ShoplusProductException.GET_PRODUCT_INVENTORY_LOCK_FAIL.format(shopId, productId);
        }
    }

六. 为什么获取库存的时候要用分布式锁?

答:因为从缓存获取库存信息失败,就要去数据库查,查出来肯定要写回缓存。那么100个线程过来查数据库,就要100个线程查完后写入缓存?不需要如此。我们只需要让1个线程从数据库查好了就写回缓存里面。所以需要加锁,至于为什么现在就用用分布式锁,其实就是为了以后分布式架构准备的(现在是单节点,当做分布式时,我们就无需再更改用分布式锁了)。

七. 为什么要做两个判null?

第一次判null是在第一次从缓存获取库存后做的,第二次判null是获得redis分布式锁之后做的。

两次判null的原因类似于单例模式懒加载的双重检查锁定机制。详情可以看单例模式之懒加载模式

八. Redis分布式锁

还没了解,后期研究完再补充

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值