Lua脚本解决误删原子性问题

# 使用Lua操作Redis

get命令

127.0.0.1:6379> EVAL "return redis.call('get','stock')" 0
"5000"
1
2

set命令

127.0.0.1:6379> EVAL "return redis.call('set','name','jack')" 0
OK
127.0.0.1:6379> get name
"jack"
1
2
3
4

改为动态参数:

127.0.0.1:6379> EVAL "return redis.call('set',KEYS[1],ARGV[1])" 1 name berry
OK
127.0.0.1:6379> get name
"berry"
1
2
3
4

# 设计Lua脚本

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

key: lock 
arg: uuid
1
2
3
4
5
6
7
8
9

将脚本整理成一行然后测试运行:

127.0.0.1:6379> set lock 123213123-341123-123123-12321312
OK
127.0.0.1:6379> EVAL "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end" 1 lock 123213123-341123-123123-12321312
(integer) 1
1
2
3
4

# 代码实现Lua脚本

将Lua脚本字符串复制到代码中:

String script = "if redis.call('get',KEYS[1]) == ARGV[1] " +
                    "then " +
                    "return redis.call('del',KEYS[1]) " +
                    "else " +
                    "return 0 " +
                    "end";
1
2
3
4
5
6

使用redisTemplate.execute()方法,其中execute()内要传三个参数:

<T> T execute(RedisScript<T> script, List<K> keys, Object... args);
1

其中RedisScript源码如下:

image-20221009161645846

我们实例化一个DefaultRedisScript对象即可

redisTemplate.execute(new DefaultRedisScript<>(script), Arrays.asList("lock"), uuid);
1

这里可能会出现不支持的异常,原因是new DefaultRedisScript<>(script)中确实返回值,我们设置一下即可

redisTemplate.execute(new DefaultRedisScript<>(script, Boolean.class), Arrays.asList("lock"), uuid);
1