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()); } } }