V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
eefnrowe
V2EX  ›  Java

关于使用 redisson 分布式锁出现的问题

  •  
  •   eefnrowe · 2019-06-05 11:43:25 +08:00 · 4280 次点击
    这是一个创建于 2022 天前的主题,其中的信息可能已经有所发展或是发生改变。
    先贴一段简单 code:

    //红包锁 redis_lock:redpacket:${redPacketId}
    String lockName = RedisKeys.buildDistributedLockKey("redpacket", redPacketId);
    RLock rLock = redissonClient.getLock(lockName);

    //用户锁 redis_lock:redpacket_userid:${userId}
    String lockUserName = RedisKeys.buildDistributedLockKey("redpacket_userid", userId);
    RLock lockUser = redissonClient.getLock(lockUserName);

    try {
    rLock.lock(10, TimeUnit.SECONDS);
    lockUser.lock(10, TimeUnit.SECONDS);

    //抢红包
    return doUnpack(redPacketId);

    } finally {
    lockUser.unlock();
    rLock.unlock();
    }



    ----------------

    问题如果没有加 lockUser 锁的情况下:
    用 jmeter 测试 单用户 10 个线程并发, 持续测试 5-10 个用户左右, 就肯定会出现一个人执行了两次业务逻辑的问题,
    更加奇怪的是, 我业务逻辑也用 redis 做过重复领取红包的行为判断 用的是 redis set isMember 判断的, 那两次重复执行的请求中, 第二次却没有检查出来!!!

    所以我怀疑我是不是把 redis 哪里给玩坏了??

    ps: redis 配置很简单 就个单节点~
    11 条回复    2019-09-12 16:13:58 +08:00
    snappyone
        1
    snappyone  
       2019-06-05 13:26:47 +08:00
    你的目的是一个用户不能抢同一个红包多次吗?你的 isMember 检查是放在什么位置的呢
    eefnrowe
        2
    eefnrowe  
    OP
       2019-06-05 14:02:22 +08:00
    @snappyone 当然是放在 doUnpack 方法里靠前的

    String userIdsKey = RedisKeys.buildRedPacketUserIdsKey(redPacketId);
    if(redisTemplate.hasKey(userIdsKey)){
    Boolean member = redisTemplate.opsForSet().isMember(userIdsKey, userId);
    if(member){
    throw new CommonException(ResponseCode.ERROR, "不可重复领取此红包");
    }
    redisTemplate.opsForSet().add(userIdsKey, userId);
    }else{
    redisTemplate.opsForSet().add(userIdsKey, userId);
    redisTemplate.expire(userIdsKey, 1, TimeUnit.HOURS);
    }
    snappyone
        3
    snappyone  
       2019-06-05 14:27:55 +08:00
    @eefnrowe
    我感觉是你释放锁的问题,如果一个线程堵塞没拿到锁,在 finally 里面也把这个锁释放了,但是此时拥有锁的并不是他自己。试试 tryLock, 如果加锁失败则不要去释放锁
    eefnrowe
        4
    eefnrowe  
    OP
       2019-06-05 14:55:48 +08:00
    @snappyone tryLock 的 waitTime 跟 leaseTime 设值有什么讲究吗
    eefnrowe
        5
    eefnrowe  
    OP
       2019-06-05 15:28:44 +08:00
    @snappyone

    //红包锁 redis_lock:redpacket:${redPacketId}
    String lockName = RedisKeys.buildDistributedLockKey("redpacket", redPacketId);
    RLock rLock = redissonClient.getLock(lockName);
    try {
    boolean b = rLock.tryLock(3, 10, TimeUnit.SECONDS);
    if(b){
    return doUnpack(redPacketId);
    }
    } catch (InterruptedException e) {
    e.printStackTrace();
    } finally {
    rLock.unlock();
    }
    eefnrowe
        6
    eefnrowe  
    OP
       2019-06-05 15:29:15 +08:00
    @snappyone 一样有类似问题
    snappyone
        7
    snappyone  
       2019-06-05 15:34:34 +08:00
    @eefnrowe 我貌似看错了,把 lock 后面的时间看做 waittime 了,不过你可以试下 trylock, 就设置个 5s 就行
    eefnrowe
        8
    eefnrowe  
    OP
       2019-06-05 15:42:36 +08:00
    @snappyone 已经试过啦, 等待 3/5 , 10 超时都试过~ 我有点怀疑是其他环节的问题了..
    ysweics
        9
    ysweics  
       2019-06-05 15:44:38 +08:00
    把红包和用户的两个 key 组装成一个 key,然后只锁一个 key=redpacketid:userid
    eefnrowe
        10
    eefnrowe  
    OP
       2019-06-05 16:13:10 +08:00
    @ysweics 这样红包领取就没有排队机制啦.. 粒度到了用户级别..
    734695609
        11
    734695609  
       2019-09-12 16:13:58 +08:00
    @eefnrowe
    你的问题:如果没有加 lockUser 锁的情况下
    doUnpack 逻辑有问题。
    你只把红包锁住,一个用户的 2 次并发访问,在你的逻辑中,可能都运行到 if(redisTemplate.hasKey(userIdsKey))
    都没有 userIdsKey,所以加了两条,
    当你在 redisson 中加上用户锁时,一个用户的两次并发请求就不会运行到下面的代码了

    我反而有一个问题,在 redisson 的官方文档中
    https://github.com/redisson/redisson/wiki/8.-%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81%E5%92%8C%E5%90%8C%E6%AD%A5%E5%99%A8
    你在 lock 时

    // 加锁以后 10 秒钟自动解锁 官方解释
    // 无需调用 unlock 方法手动解锁 官方解释
    rLock.lock(10, TimeUnit.SECONDS);

    这句代码官方解释是 10 秒后自动解锁,这样的话如果你的业务代码大于 10 秒或者因为某些原因等待超过 10 秒,会不会导致锁被释放掉,锁失效。
    所以我感觉不加时间才是对的,除非你业务需要
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5174 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 09:36 · PVG 17:36 · LAX 01:36 · JFK 04:36
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.