使用lua实现redis分布式锁

Lua官网

http://doc.redisfans.com/script/eval.html

问题重现:预约功能,用jemter测试,30个不同用户同时预约,2个用户重复发送请求,即重复发送消息场景,结果导致重复消费

避免重复消费方式

1.如第6行,重复原因:第10行,业务逻辑耗时比较长,还没执行第11行,导致6行,执行通过,重复的数据都进来了

2.如第5行+12行,高并发下,加分布式锁,重复请求(具体场景是什么),只有一个能执行业务逻辑。这样不够,6行还是要保留,保证幂等。

分析:分布式锁只能保证高并发下幂等行,无法保证持久幂等。这里注意:分布式锁与existKey,都不是一个固定值,保证10业务逻辑可以并发执行。

高并发与幂等行没有关系。

1 String key = RedisEnum.APPOINT_RECORD_KEY.toKey(reqVO.getLiveId(), reqVO.getUid());
2 String uniqueVal = UUID.randomUUID().toString();
3 String lockKey = RedisEnum.APPOINT_RECORD_LOCK_KEY.toKey(reqVO.getLiveId(), reqVO.getUid());
4 try {
5    if (redisManager.tryLock(lockKey, uniqueVal, APPOINT_RECORD_LOCK_KEY.expired)) {
 6      Boolean exist = redisManager.existsKey(key);
 7      if (Boolean.TRUE.equals(exist)) {
  8         log.error("saveAppoint exception already appointed reqParam:{}", JSON.toJSONString(reqVO));
   9        throw ExceptionUtils.throwException(LIVE_APPOINTED);
       }
10 todo 处理业务逻辑
11 redisManager.put(key, RedisEnum.APPOINT_RECORD_KEY.expired, JSON.toJSONString(reqVO));
    }
} catch (Exception e) {
    log.error("Saving appoint exception", e);
    throw ExceptionUtils.throwException(LIVE_APPOINTED);
} finally {
  12  redisManager.releaseLock(lockKey, uniqueVal);
}
return false;

参考:

https://blog.csdn.net/qq_39736103/article/details/116452752?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-0&spm=1001.2101.3001.4242

lua实现

加锁:

 public boolean tryLock(String lockKey, String randomUnValue, int expireTime) {
        Jedis jedis = null;

        boolean var6;
        try {
            jedis = this.jedisPool.getResource();
            Long result = (Long)jedis.eval(LuaSecript.SETNX_AND_EXPIRE.getScript(), 1, new String[]{lockKey, randomUnValue, String.valueOf(expireTime)});
            if (result != 1L) {
                return false;
            }

            var6 = true;
        } catch (Exception var10) {
            this.logger.error("Try lock failed", var10);
            return false;
        } finally {
            if (jedis != null) {
                jedis.close();
            }

        }

        return var6;
    }
public enum LuaSecript {
    SETNX_AND_EXPIRE("local is_existed = redis.call('setnx', KEYS[1], ARGV[1]);if (is_existed == 1) then redis.call('expire', KEYS[1], ARGV[2]);return 1; end; return 0;"),
    DEL_KEY_AND_EQUAL_VAL("if (redis.call('get', KEYS[1]) == ARGV[1]) then return redis.call('del', KEYS[1]);else return 0;end; "),
    ZRANGEBYSCORE_ZREM("local data = redis.call('ZRANGEBYSCORE', KEYS[1], ARGV[1], ARGV[2], 'WITHSCORES', 'LIMIT', ARGV[3], ARGV[4]);if table.getn(data) > 0 then for k,v in pairs(data) do if (k % 2) ~=0 then redis.call('ZREM', KEYS[1], v);end;end;return data;end;");

    private String script;

    private LuaSecript(String script) {
        this.script = script;
    }

    public String getScript() {
        return this.script;
    }
}

解锁:

 public boolean releaseLock(String lockKey, String randomUnValue) {
        Jedis jedis = null;

        boolean var5;
        try {
            jedis = this.jedisPool.getResource();
            jedis.eval(LuaSecript.DEL_KEY_AND_EQUAL_VAL.getScript(), 1, new String[]{lockKey, randomUnValue});
            return true;
        } catch (Exception var9) {
            this.logger.error("Release lock failed", var9);
            var5 = false;
        } finally {
            if (jedis != null) {
                jedis.close();
            }

        }

        return var5;
    }

依赖

<dependency>
			<groupId>redis.clients</groupId>
			<artifactId>jedis</artifactId>
		</dependency>

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值