redis实现分布式锁

1.分布式并发问题

提交订单:商品超卖问题

 2.如何解决分布式并发问题呢 ?

使⽤redis实现分布式锁

 3.使⽤Redis实现分布式锁-代码实现

    @Transactional
    public Map<String,String> addOrders(Orders orders, String cartId) {
        logger.info("增加订单");
        // 查询购物车列表
        ArrayList<Integer> integers = new ArrayList<>();
        String[] split = cartId.split(",");
        for (String s : split) {
            integers.add(Integer.parseInt(s));
        }
        // 根据用户在购物车列表中选择的购物车记录id查询到对应的购物记录
        List<ShoppingCartVo> shoppingCartVos = shoppingCartMapper.listShoppingCartByCartId(integers);
        boolean isTrue = true;
        // 设置数组,为后面释放锁做准备 数组里是被锁住的id ["1","2","null"]
        String[] skuIds = new String[shoppingCartVos.size()];
        // 存储redis中的内容为后面验证是否是该线程的锁做准备
        HashMap<String, String> values = new HashMap<>();
        for (int i = 0; i < shoppingCartVos.size(); i++) {
            ShoppingCartVo shoppingCartVo = shoppingCartVos.get(i);
            String skuId = shoppingCartVo.getSkuId();
            String value = UUID.randomUUID().toString();
            try {
                Boolean sku = stringRedisTemplate.boundValueOps(skuId).setIfAbsent(value,10, TimeUnit.MINUTES);
                if (sku){
                    skuIds[i] = skuId;
                    values.put(skuId,value);
                }
                isTrue = isTrue && sku;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        //相当于加锁
        if (isTrue){
            try {
                // 比较库存,当第一次查询购物车记录之后,在加索成功之前,可能被其他的并发线程修改库存
                shoppingCartVos = shoppingCartMapper.listShoppingCartByCartId(integers);
                boolean isEnough = true;
                // 查询库存是否充足
                String untitled = "";
                for (ShoppingCartVo shoppingCartVo : shoppingCartVos) {
                    if (Integer.parseInt(shoppingCartVo.getCartNum()) > shoppingCartVo.getStock()) {
                        isEnough = false;
                        break;
                    }
                    untitled = untitled + shoppingCartVo.getProductName();
                }
                // 库存充足,进行  添加订单,保存快照,修改库存,删除购物车
                if (isEnough) {
                    // 生成订单id
                    //  保存订单
                    String orderId = UUID.randomUUID().toString().replace("-", "");
                    orders.setUntitled(untitled);
                    orders.setOrderId(orderId);
                    orders.setStatus("1");
                    orders.setCreateTime(new Date());
                    int insert = ordersMapper.insert(orders);

                    if (insert > 0) {
                        // 生成订单快照
                        for (ShoppingCartVo sc : shoppingCartVos) {
                            OrderItem orderItem = new OrderItem();
                            String itemId = System.currentTimeMillis() + "" + (new Random().nextInt(8999) + 1000);
                            orderItem.setItemId(itemId);
                            orderItem.setOrderId(orderId);
                            orderItem.setProductId(sc.getProductId());
                            orderItem.setProductImg(sc.getUrl());
                            orderItem.setSkuId(sc.getSkuId());
                            orderItem.setSkuName(sc.getSkuName());
                            orderItem.setProductPrice(sc.getProductPrice());
                            orderItem.setProductName(sc.getProductName());
                            int cnum = Integer.parseInt(sc.getCartNum());
                            orderItem.setBuyCounts(cnum);
                            orderItem.setTotalAmount(BigDecimal.valueOf(sc.getSellPrice() * cnum));
                            orderItem.setBasketDate(new Date());
                            orderItem.setBuyTime(new Date());
                            orderItem.setIsComment(0);
                            orderItemMapper.insert(orderItem);
                        }
                        // 更新库存
                        for (ShoppingCartVo sc : shoppingCartVos) {
                            String skuId = sc.getSkuId();
                            int newStoken = sc.getStock() - Integer.parseInt(sc.getCartNum());
                            ProductSku productSku = new ProductSku();
                            productSku.setSkuId(skuId);
                            productSku.setStock(newStoken);
                            int k = productSkuMapper.updateByPrimaryKeySelective(productSku);
                        }
                        // 删除购物车
                        for (Integer integer : integers) {
                            shoppingCartMapper.deleteByPrimaryKey(integer);
                        }

                        HashMap<String, String> map = new HashMap<>();
                        map.put("orderId", orderId);
                        map.put("productNames", untitled);
                        System.out.println("map = " + map.get("productNames"));
                        System.out.println("map = " + map.get("orderId"));
                        return map;
                    }
                    return null;
                } else {
                    return null;
                }
            } catch (NumberFormatException e) {
                e.printStackTrace();
            } finally {
                //释放锁
                for (int i = 0; i < skuIds.length; i++) {
                    String skuId = skuIds[i];
                    if (skuIds != null && !"".equals(skuIds)){
                        // 释放锁之前,先查询当前的value
                        String s = stringRedisTemplate.boundValueOps(skuId).get();
                        //  如果从redis中获取 的值与生成的value是一至的,则表示此锁是当前线程的锁
                        if (s != null && s.equals(values.get(skuId))){
                            stringRedisTemplate.delete(skuId);
                        }
                    }
                }
            }
        }else {
            // 表示加锁失败,天大订单失败
            // 当加锁失败时,有可能部分商品已经加锁,要释放锁定的部分商品
            for (int i = 0; i < skuIds.length; i++) {
                String skuId = skuIds[i];
                if (skuIds != null && !"".equals(skuIds)){
                    // 释放锁之前,先查询当前的value
                    String s = stringRedisTemplate.boundValueOps(skuId).get();
                    //  如果从redis中获取 的值与生成的value是一至的,则表示此锁是当前线程的锁
                    if (s != null && s.equals(values.get(skuId))){
                        stringRedisTemplate.delete(skuId);
                    }
                }
            }
        }
        return null;
    }

问题:
1.如果订单中部分商品加锁成功,但是某⼀个加锁失败,导致最终加锁状态失败——需要对
已经锁定的部分商品释放锁
2.在成功加锁之前,我们根据购物⻋记录的id查询了购物⻋记录(包含商品库存),能够直接
使⽤这个库存进⾏库存校验?
——不能,因为在查询之后加锁之前可能被并发的线程修改了库存;因此在进⾏库存⽐较之
前需要重新查询库存。
3.当当前线程加锁成功之后,执⾏添加订单的过程中,如果当前线程出现异常导致⽆法释放
锁,这个问题⼜该如何解决呢?

4.解决因线程异常导致⽆法释放锁的问题

解决⽅案:在对商品进⾏加锁时,设置过期时间,这样⼀来及时线程出现故障⽆法释放
锁,在过期时间结束时也会⾃动“释放锁”

 问题:当给锁设置了过期时间之后,如果当前线程t1因为特殊原因,在锁过期前没有完成业
务执⾏,将会释放锁,同时其他线程(t2)就可以成功加锁了,当t2加锁成功之后,t1执⾏结
束释放锁就会释放t2的锁,就会导致t2在⽆锁状态下执⾏业务。

5.解决因t1过期释放t2锁的问题

在加锁的时候,为每个商品设置唯⼀的value

 在释放锁的时候,先获取当前商品在redis中对应的value,如果获取的值与当前value相
同,则释放锁

5.看⻔狗机制

 看⻔⼝线程:⽤于给当前key延⻓过期时间,保证业务线程正常执⾏的过程中,锁不会过期。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值