0
点赞
收藏
分享

微信扫一扫

redis锁和等待锁随机毫秒数解决程序调用方控制执行的先后顺序,避免并发操作造成的数据不一致

redis锁和等待锁随机毫秒数解决程序调用方控制执行的先后顺序,避免并发操作造成的数据不一致

现象:
向第三方服务调用接口,比如更换商品换货,需要先取消,然后再新增操作。
同时可能存在修改并发操作(同时操作换货和修改操作),在取消和新增的间隙中做了修改操作,引起脏数据等数据不一致的问题。
导致修改的数据,在新增操作后,未生效。

解决方案: 基于的前提是在程序接口的调用方来控制先后执行顺序,服务提供方本身提供的是取消,新增,修改3个独立的接口,只是业务上需要将取消和新增组合起来使用。
redis锁定5秒来处理,控制加锁优化

期望:将取消和新增作为一个“事务”来处理,只有这一个“换货”的操作完成之后,才允许做修改操作。

//伪代码
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    
//修改接口{
    String redisKey = "key" + orderDTO.getOrdernumber();
    Boolean haskey = stringRedisTemplate.hasKey(redisKey);
    LocalDateTime startTime = LocalDateTime.now();
    LocalDateTime endTime = startTime.plusSeconds(5);
    
    //已被锁定,直接返回,等待 + redis锁释放和程序时间5秒双重判断,避免redis释放锁异常导致永远在等待的现象。
        while ( haskey && (startTime.isBefore(endTime) || startTime.isEqual(endTime)) ) {
            try {
                long time = (long) (Math.random() * 1000);
                Thread.sleep(time);
                //重新查询
                haskey = stringRedisTemplate.hasKey(redisKey);
                //重新刷新时间
                startTime = LocalDateTime.now();
                log.info("testlogger >> 修改订单 判断锁存在,orderNo=[{}],haskey=[{}],waitTime=[{}]",orderDTO.getOrdernumber(),haskey,time);
            } catch (InterruptedException e) {
                log.error("exceotion:",e);
            }
        }
        
    //继续修改操作 ……    
}


//换货接口(取消和新增){
    String redisKey = "key" + orderDTO.getOrdernumber();
    Boolean haskey = stringRedisTemplate.hasKey(redisKey);
    try {
        //加锁
        if(haskey) {
            //已被锁定,直接返回,无需重复加锁
            log.info("testlogger 锁已经存在,无需重复加锁,orderNo=[{}]",cancelFlowOrderDTO.getOrdernumber());
        } else {
            stringRedisTemplate.opsForValue().set(redisKey, "1", 5, TimeUnit.SECONDS);
            log.info("testlogger 锁不存在,需要加锁,orderNo=[{}]",cancelFlowOrderDTO.getOrdernumber());
        }
        
        //换货操作(取消和新增)……
        

    }catch (Exception e) {
        log.error("取消新增接口异常:",e);
        if(haskey != null && haskey) {
            stringRedisTemplate.delete(redisKey);
            log.info("testlogger 锁存在,异常释放锁,orderNo=[{}]",cancelFlowOrderDTO.getOrdernumber());
        }
    }finally {
        if(haskey != null && haskey) {
            //释放锁
            stringRedisTemplate.delete(redisKey);
            log.info("testlogger 锁存在,正常释放锁,orderNo=[{}]",cancelFlowOrderDTO.getOrdernumber());
        }
    }
}

 

举报

相关推荐

0 条评论