Redis实现可重入锁?(短链例子)

DBC 997 0

为什么要用可重入锁?

解析

在前面的文章说到,我们短链采用了冗余双写的方案,这里就会有出现一个问题,那么就是当我们插入C端(用户端)和插入B端(商家端)的时候是两个不同的服务。这时候在高并发的情况下就可能会出现一些奇奇怪怪的问题。比如说以下这些:

  • 用户A生成短链码AABBCC ,C端先插入,B端还没插入
  • 用户B也生成短链码AABBCC ,B端先插入,C端还没插入
  • 用户A生成短链码AABBCC ,B端插入
  • 用户B生成短链码AABBCC ,C端插入

在平时我们在解决使用分布式锁的时候,通常都会采用Redis的一个Key来完成操作,但是这里有两步,一步是插入C端,一步是插入B端,我们是冗余双写,那么两边都是插入一样的短链,那么RedisKey就会相同,这样无论是先插入B端还是先插入C端,都会有一端被拒绝插入,这显然不是我们想要的!

让我们来了解一个新名词:“业务线程”

“业务线程”

我们C、B端的插入,是两步走的操作,这里我们需要看成是一个整体,也就是相当于我们的一个线程,这就是“业务线程”了。那我们应该怎么实现呢?直接上代码

采用 lua脚本+redis, 由于【判断和删除】是lua脚本执行,所以要么全成功,要么全失败

流程一览

实际代码

    @Autowired
    private RedisTemplate<Object,Object> redisTemplate;
    @GetMapping("test")
    public JsonData test(@RequestParam(name = "code")String code, @RequestParam(name = "accountNo") Long accountNo ){


        //key1是短链码,ARGV[1]是accountNo,ARGV[2]是过期时间
        String script = "if redis.call('EXISTS',KEYS[1])==0 then redis.call('set',KEYS[1],ARGV[1]); redis.call('expire',KEYS[1],ARGV[2]); return 1;" +
                " elseif redis.call('get',KEYS[1]) == ARGV[1] then return 2;" +
                " else return 0; end;";

        Long result = redisTemplate.execute(new
                DefaultRedisScript<>(script, Long.class), Arrays.asList(code), accountNo,100);

        return JsonData.buildSuccess(result);
    }

大功告成[aru_50]

发表评论 取消回复
表情 图片 链接 代码

分享