Redis:Lua脚本

本文将讲解如何在Redis-cli和RedisTemplate中使用Lua脚本,以及Lua脚本的部分语法知识。

Lua具体语法请看:Lua 教程 | 菜鸟教程 (runoob.com)

介绍

Lua脚本可以确保一系列Redis命令在同一事务中执行,进而保证命令执行的原子性操作(虽然在redis集群中涉及多个槽位的操作可能无法保证原子性,但是只处理单槽位的key时是可以保证原子性的);有时,需要执行一系列Redis操作,这些操作必须按照特定的顺序和逻辑进行,就可以用Lua脚本将这些操作封装成一个单一的原子事务。

redis-cli

命令方式:EVAL "Lua脚本" numkeys key [key ...] arg [arg ...]

  • "Lua脚本":替换为您要执行的Lua脚本。
  • numkeys:指定Lua脚本中引用的键的数量。
  • key [key ...]:指定Lua脚本引用的键。
  • arg [arg ...]:指定Lua脚本引用的参数。

参数方式:redis-cli --eval 脚本地址 keys... , args...

示例

(分布式锁中为了保证一个线程只能解除自己的锁,使用setnx命令后的del命令必须判断该锁是否是它自身定下的,以免网络延迟导致del命令删除其他线程定下的锁)

先使用setnx命令,模拟一个线程获得锁:

127.0.0.1:6379> set lock lock-user1 NX EX 30
1

然后使用lua脚本进行释放锁:

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 lock-user1
1

该命令中使用redis.call调用get lock并判断返回值是否是ARGV[1],即lock-user1,如果是,说明该锁就是它定下的,就使用del命令解锁并返回值,否则直接返回0说明执行失败。

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 lock-user2 0

user2就无法解除该锁。

也可以使用参数方式:

lua.lua脚本文件:

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

执行:

$ redis-cli -a 20030322  --eval /home/chilun/Redis/lua.lua lock , lock-user1
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
(integer) 1

RedisTemplate

常用方法:

public <T> T execute(RedisScript<T> script, List<K> keys, Object... args) {
   return scriptExecutor.execute(script, keys, args);
}

List keys即key [key ...],Object... args即arg [arg ...]。

示例

同样是模拟分布式锁:

@RestController
public class LockController {
    @Autowired
    RedisTemplate redisTemplate;
    private static final String KEY = "lock";

    @GetMapping("/lock")
    public boolean lock(@RequestParam String user) {
        return redisTemplate.opsForValue().setIfAbsent(KEY, user, 30L, TimeUnit.SECONDS);
    }

    @GetMapping("/unlock")
    public boolean unlock(@RequestParam String user) {
        String LuaScript =
                "if (redis.call('get',KEYS[1]) == ARGV[1]) then " +
                    "return redis.call('del',KEYS[1]) " +
                "else " +
                    "return 0 " +
                "end";
        return (boolean) redisTemplate.execute(new DefaultRedisScript<>(LuaScript, Boolean.class), Arrays.asList(KEY), user);
    }
}

user1上锁:

user2无法上锁:

30s后user2可上锁:

30s内user1无法解锁:

30s内user2可解锁:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值