Lua脚本使用手册(Redis篇)

Lua脚本

**简介:**Lua是一种功能强大的,高效,轻量级,可嵌入的脚本语言。它是动态类型语言,通过使用基于寄存器的虚拟机解释字节码运行,并具有增量垃圾收集的自动内存管理,是配置,脚本和快速原型设计的最佳选择

Redis中使用lua脚本的原因
  • 减少网络延迟:Lua脚本将多个Redis命令组合成一个脚本,减少了客户端与服务器之间的网络交互。同时,Redis服务器还提供了EVALSHA命令,可以将脚本的SHA1值缓存在服务器中,下次再执行同样的脚本时,只需传递SHA1值即可,减少了网络传输时间。

  • 原子操作:Lua脚本可以保证多个Redis命令的原子性,避免了并发问题

Redis怎么执行Lua脚本
1、lua函数

Lua脚本使用两个不同的Lua函数来调用任意的Redis命令

bash 复制代码
redis.call()
redis.pcall()

当redis命令执行结果返回错误时

  • call() 将返回给调用者一个错误,

  • pcall() 将捕获的错误以Lua表的形式返回

bash 复制代码
127.0.0.1:6379> EVAL "return redis.call('xxxx')" 0
(error) ERR Error running script (call to f_1e6efd00ab50dd564a9f13e5775e27b966c2141e): @user_script:1: @user_script: 1: Unknown Redis command called from Lua script

127.0.0.1:6379> EVAL "return redis.pcall('xxxx')" 0
(error) @user_script: 1: Unknown Redis command called from Lua script
2、脚本执行

Redis Lua脚本可以通过 EVAL 命令或者 EVALSHA 命令执行

bash 复制代码
EVAL script numkeys [key [key ...]] [arg [arg ...]]
EVALSHA sha1 numkeys [key [key ...]] [arg [arg ...]]
  • numkeys: the number of input key name arguments,keyname的数量
  • key : the name of key,多个keyname空格隔开。在Lua 脚本中通过 KEYS[INDEX]来获取对应的name,其中1 <= INDEX <= numkeys
  • arg : all the keys accessed by the script,多个key空格隔开,在Lua脚本中通过ARGV[INDEX]来获取对应的key,其中1 <= INDEX <= numkeys

EVAL EVALSHA 命令的区别

  • EVAL 命令要求你在每次执行脚本的时候都发送一次脚本主体(script body)。Redis 有一个内部的脚本缓存机制,因此它不会每次都重新编译脚本
  • EVALSHA 命令, Evaluate a script from the server's cache by its SHA1 digest. 。在执行EVALSHA的时候根据

具体的使用方法如下

使用 EVAL 命令

bash 复制代码
127.0.0.1:6379> eval "return redis.call('set',KEYS[1],ARGV[1])" 1 username zhangsan
 OK
127.0.0.1:6379> EVAL "return redis.call('GET', KEYS[1])" 1 username
"zhangsan"

在较新的版本中,lua脚本又新增了许多命令,比如下面的 EVAL_RO,是 EVAL 的一个变种,表示只读,修改数据的命令是不能被执行的

SCRIPT LOADEVALSHA 命令

使用 SCRIPT LOAD 命令加载lua脚本到服务器脚本缓存中,以达到重复使用,避免多次加载浪费带宽,加载但不会执行这个lua脚本,并返回一个sha校验值。需要配合EVALSHA命令来执行缓存后的脚本

bash 复制代码
127.0.0.1:6379> SCRIPT LOAD "return 'hello'"
"1b936e3fe509bcbc9cd0664897bbe8fd0cac101b"
127.0.0.1:6379> EVALSHA 1b936e3fe509bcbc9cd0664897bbe8fd0cac101b 0
"hello"

不会经常变动的脚本推荐使用 EVALSHA

官方文档:https://redis.io/docs/latest/commands/?group=scripting

应用场景
  • 事务操作:Lua脚本可以保证一组Redis命令的原子性
springboot实战
实现分布式锁
Java 复制代码
public String tryLock() {
    DefaultRedisScript<Long> script = new DefaultRedisScript<>();
    script.setScriptSource(new ResourceScriptSource(new ClassPathResource("tryLock.lua")));
    script.setResultType(Long.class);
    Long state = stringRedisTemplate.execute(script, Lists.newArrayList("lock-good"), "uuid");
    if (state == 1){
        log.info("获取锁成功");
    }else {
        log.info("获取锁失败");
    }
    return "";
}

public String releaseLock() {
    DefaultRedisScript<Boolean> script = new DefaultRedisScript<>();
    script.setScriptSource(new ResourceScriptSource(new ClassPathResource("releaseLock.lua")));
    Boolean releaseLock = stringRedisTemplate.execute(script, Lists.newArrayList("lock-good"));
    if (releaseLock){
        log.info("释放锁成功");
    }else {
        log.info("释放锁失败");
    }
    return "";
}

lua脚本

tryLock.lua

lua 复制代码
local key = KEYS[1];
local value = ARGV[1];
local expireTime = ARGV[2];
if redis.call('SETNX', key, value) == 1 then
    return redis.call('PEXPIRE', key, expireTime);
else
    return 0;
end

releaseLock.lua

lua 复制代码
local key = KEYS[1];
local value = ARGV[1];
if redis.call('GET', key) == value then
    redis.call('DEL', key);
    return true;
else
    return false;
end
数据更新

update.lua

lua 复制代码
local key = KEYS[1];
local value = ARGV[1];
local pexpire = ARGV[1];
if redis.call('GET',key) == value then
    redis.call('SET', key, value);
    redis.call('PEXPIRE', key, pexpire)
    return 1;
else
    return 0;
end
相关推荐
风向决定发型丶2 小时前
redis集群搭建
数据库·redis·缓存
梦想的颜色4 小时前
硬核实践:使用 Docker 部署生产级 Redis(持久化 + 安全配置 + 高可用)
redis·docker·redis持久化·docker compose·redis哨兵·rdb aof
宠友信息5 小时前
多端数据互通场景下Spring Boot仿小红书源码结构设计
数据库·spring boot·redis·缓存·架构
长不胖的路人甲6 小时前
Redis 缓存的数据持久化方案讲解
数据库·redis·缓存
长不胖的路人甲6 小时前
Redis 单线程为什么速度很快
数据库·redis·缓存
彦为君6 小时前
算法思维与经典智力题
java·前端·redis·算法
彦为君8 小时前
Redis最新版本特性
java·数据库·redis·算法·bootstrap
长不胖的路人甲8 小时前
Redis 数据删除策略
数据库·redis·spring
尽兴-9 小时前
Redis 为什么快?
数据库·redis·内存
一嘴一个橘子11 小时前
redis.windows.conf 的 保护模式
redis