Redis分布式锁的实战解析

这篇文章是对公司仓储项目中箱号或SN采集校验时并发分布式锁的解析

建立锁key

// 建立锁的名字:根据本次需要进行采集的出库单和物料类型创建。这个锁的粒度在这个项目中可以分为三级:
// 一级锁单据,二级锁物料编码,三级锁对应SN。粒度选择为物料编码的原因是:锁单据会造成等待,耗时较长,锁SN会导致同一物料的不同SN同时进行校验并通过,造成锁失效。相比之下选择物料编码是最好的。(lua脚本另说)
String cacheQuantityKey = "outbound:pick:" + dto.getOutboundId() + ":" + dto.getMisMatCode();
        String lockKey = "lock:outbound:pick:" + dto.getOutboundId() + ":" + dto.getMisMatCode();
        try {
            //分布式锁
            // 向redis设置锁,返回结果如果为真,代表设置成功,redis中没有这种物料在进行校验,取反后不会抛出异常。
            if (!redisUtils.getValueOperations().setIfAbsent(lockKey, userEntity.getUserId().toString(), 120L, TimeUnit.SECONDS)) {
                Assert.is(true, "单据正在检验中,请稍后再试!");
            }
			//查询出需要进行采集的SN并校验 -- 开始
            SysAntennaSnEntity snEntity = sysAntennaSnService.getOne(new LambdaQueryWrapper<SysAntennaSnEntity>()
                    .eq(SysAntennaSnEntity::getSupplierSn, dto.getSupplierSn())
                    .select(SysAntennaSnEntity::getSupplierSn, SysAntennaSnEntity::getMaterialDetailId
                    ,SysAntennaSnEntity::getMisMatCode,SysAntennaSnEntity::getPickCheckDate)


            );
            Assert.isNull(snEntity, "该SN未入库");
            Assert.is(!snEntity.getMisMatCode().equals(dto.getMisMatCode()), "该SN不属于物料["+dto.getMisMatCode()+"]");
            Assert.is(ObjectUtil.isNotNull(snEntity.getPickCheckDate()), "该SN已检验");

            //得到供应商id
            SysInStockDetailEntity inStockDetail = sysInStockDetailService.getOne(new LambdaQueryWrapper<SysInStockDetailEntity>()
                    .eq(SysInStockDetailEntity::getMisMatCode, dto.getMisMatCode())
                    .eq(SysInStockDetailEntity::getNum, dto.getSupplierSn())
                    .select(SysInStockDetailEntity::getSuprCode)
            );
            Assert.isNull(inStockDetail , "该SN未上架!");
           //查询出需要进行采集的SN并校验 -- 结束
           // 该SN符合采集需求,判断下还需不需这个SN(总数满了没)
            String materialDetailId = this.loadCacheAntennaSN(cacheQuantityKey, dto.getMisMatCode(), dto.getOutboundId(),Boolean.FALSE);
            Assert.is("".equals(materialDetailId), "SN数量已足够!");
            // 还需要,给这个单据详情更新数量
            sysOutboundNoticeDetailService.update(new LambdaUpdateWrapper<SysOutboundNoticeDetailEntity>()
                    .eq(SysOutboundNoticeDetailEntity::getMaterialDetailId, Long.parseLong(materialDetailId))
                    .set(SysOutboundNoticeDetailEntity::getSuprCode,inStockDetail.getSuprCode())
                    .set(SysOutboundNoticeDetailEntity::getPickedQuantity, redisUtils.hIncry(cacheQuantityKey, materialDetailId, -1L))
            );
            // 更新这个SN,加上拣货人以及出库单据等
            sysAntennaSnService.update(new LambdaUpdateWrapper<SysAntennaSnEntity>()
                    .eq(SysAntennaSnEntity::getMaterialDetailId, snEntity.getMaterialDetailId())
                    .set(SysAntennaSnEntity::getPickCheckDate, LocalDateTime.now())
                    .set(SysAntennaSnEntity::getPickCheckBy, userEntity.getUserId())
                    .set(SysAntennaSnEntity::getMaterialStatus, MaterialStatusEnum.OUTBONUDING.getCode())
                    .set(SysAntennaSnEntity::getOutboundId, dto.getOutboundId())
                    .set(SysAntennaSnEntity::getOutboundDetailId, Long.parseLong(materialDetailId))
                    .last(" limit 1")
            );
            return Boolean.TRUE;
        } finally {
            redisUtils.delete(lockKey);
        }

你可能感兴趣的:(redis,分布式,java)