简单应用Redis锁解决实际需求

有一个需求是生成订单编号,按序递增,那我们可以使用redis的incr自增方法使订单编号顺序递增。让redis中存入了一个流水号key,incr之后其值就保持最新流水号。

但是若很不幸的是,旧redis下线了,新的redis里又不存在原来的流水号key,那就需要代码层面自动添加进去,这个时候就需要编写redis锁去数据库中查找最大的流水号值,并赋值到redis。

代码如下

        //获取最大的保单流水号
        String serialNoKey = redisUtil.get(travelSerialNoKey);
        if (StringUtils.isBlank(serialNoKey)) {
            if (redisUtil.tryLock(WS_DATE_SERIAL_NO_KEY, 10000)) {
                try {
                    serialNoKey = redisUtil.get(travelSerialNoKey);
                    if (StringUtils.isBlank(serialNoKey)) {
                        String policyNo = wsPolicyMapper.selectLast().getPolicyNo();
                        Integer integer = Integer.valueOf(StringUtils.substring(policyNo, policyNo.length() - 8));
                        redisUtil.set(travelSerialNoKey, integer.toString());
                    }
                } finally {
                    redisUtil.del(WS_DATE_SERIAL_NO_KEY);
                }
            } else {
                throw new BizException(WsReturnCodeEnum.DIY_ERROR.getResultCode(), "系统繁忙,请稍后再试");
            }
        }

实际上,上面的要点如下:
1、tryLock之前和之后,都要查询一次并判空。
2、删除key使用finally
3、若获取不到锁,千万不要写成自旋,而是直接返回请求。若这里自旋的话,在生产环境里假若有突然极大的流量进来,高并发现象会严重占用系统资源,可能导致JVM内存和CPU负载极高。

问题:
这个锁的代码一般可以解决大部分场景。但是还可以继续优化,原因是,第一个线程拿到锁了之后,业务逻辑太慢太卡,超过了key的过期时间了,那就有可能出问题了。

比如其中一个场景,a线程拿到锁WS_DATE_SERIAL_NO_KEY,业务卡住了过了10s,锁WS_DATE_SERIAL_NO_KEY过期,b就可以拿到锁WS_DATE_SERIAL_NO_KEY了,b正常完成逻辑的时候,线程a执行完了,并finally删除了锁WS_DATE_SERIAL_NO_KEY,【注意,此时b还以为自己是获得锁的情况下操作】,接下里,c就可以继续抢到锁了。

这样的锁就有问题了,根本问题就是多个锁共用一个key,解决办法,每个线程tryLock的时候对同一个key设置不同的value,当删除的时候,也要先判断这个key对应的value是不是本线程的,再做决定删不删key。

【完,喜欢就点个赞呗】

正在去BAT的路上修行

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值