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
相关推荐
朝九晚五ฺ12 小时前
【Redis学习】持久化机制(RDB/AOF)
数据库·redis·学习
疯狂吧小飞牛18 小时前
Lua 中的 __index、__newindex、rawget 与 rawset 介绍
开发语言·junit·lua
怪兽201421 小时前
什么是 Redis?
java·数据库·redis·缓存·面试
疯狂吧小飞牛21 小时前
Lua C API 中的注册表介绍
java·c语言·lua
wangmengxxw21 小时前
Redis概述
数据库·redis·缓存
摇滚侠1 天前
Spring Boot 3零基础教程,Spring Boot 日志的归档与切割,笔记22
spring boot·redis·笔记
爬山算法1 天前
Redis(64)Redis的Lua脚本有哪些常见场景?
数据库·redis·lua
疯狂吧小飞牛1 天前
Lua C API 中一段LUA建表过程解释
c语言·junit·lua
疯狂吧小飞牛1 天前
Lua中,表、元表、对象、类的解析
开发语言·junit·lua
❀͜͡傀儡师1 天前
OpenResty + Lua + Redis 鉴权案例,适用于 x86 和 ARM 架构的 Docker 环境。
redis·lua·openresty