redis使用setnx+lua实现分布式锁

在Redis中,使用SETEX命令(对应RedisTemplate的setIfAbsent方法)可以实现一个最简易的分布锁。SETEX命令当key不存在的话,才会设置key的值,如果可以已经存在,就不做任何操作。

为了避免锁无法被释放,就给这个key(也就是锁)设置一个过期时间。

为了保证解锁操作的原子性,使用Lua脚本进行释放锁操作

为了防止误删其他的锁,在Lua脚本中通过key对应的value(唯一值)来判断是否要释放锁

最简单的分布式锁实现

不使用Lua脚本

// key 的唯一性可以关联业务
String key = "lockKey";
//value 的唯一性通过UUID生成
String value = UUID.randomUUID().toString().replaceAll("-", "");
//添加分布式锁,过期时间30秒
        Boolean absent = redisTemplate.opsForValue().setIfAbsent(key, value, 300000, TimeUnit.MILLISECONDS);
logger.info("执行相关的业务逻辑操作");
//释放锁
redisTemplate.delete(key);


添加Lua脚本保证原子性

1.新建Lua脚本

释放锁时,先比较锁对应的 value 值是否相等,避免锁的误释放。

if redis.call("get",KEYS[1]) == ARGV[1] then
    return redis.call("del",KEYS[1])
else
    return 0
end

2.使用Lua脚本释放锁

logger.info("执行相关的业务逻辑操作");
//redisTemplate.delete(key);
DefaultRedisScript<Long> script = new DefaultRedisScript<Long>();
script.setResultType(Long.class);
script.setScriptSource(new ResourceScriptSource(new ClassPathResource("lock.lua")));
String lockValue = (String) redisTemplate.opsForValue().get(key);
if(lockValue != null && value.equals(lockValue)) {
    List<String> keys = new ArrayList<>();
    keys.add(key);
    //执行脚本
    Long execute = (Long)redisTemplate.execute(script, keys, value);
    logger.info("execute=" + execute);
    logger.info("释放分布锁,key=" + key);
}

参考资料

posted @ 2023-04-03 12:47  享受生活2023  阅读(132)  评论(0编辑  收藏  举报