redis执行lua脚本
文档
官方文档
- 官网操作命令指南页面:https://redis.io/docs/latest/commands/?name=get&group=string
- Redis cluster specification
- Distributed Locks with Redis
说明
- redis版本:7.0.0
- springboot版本:3.2.0
redis执行lua脚本
安装单机版redis
- 安装单机版redis参考文档:redis单机安装
编写lua脚本
-
在 Redis 中原子地尝试给 key 设值并设置过期时间,成功返回 1,失败(key 已存在)返回 0,用作分布式加锁。
-
编写一个lua脚本,为指定的 key 设置一个值并同时设置毫秒级过期时间,成功则返回 1,失败则返回 0。
-
脚本示例
javascript-- KEYS[1] : 锁的 key -- ARGV[1] : 锁的唯一标识 -- ARGV[2] : 过期时间(毫秒) -- 第一步:只有在 key 不存在时才设置值,相当于 SET key value NX if redis.call('SETNX', KEYS[1], ARGV[1]) == 1 then -- 第二步:设置过期时间,相当于 PX ttl redis.call('PEXPIRE', KEYS[1], ARGV[2]) return 1 -- 加锁成功 else return 0 -- 加锁失败(已有锁) end -
简化成一行,保存为脚本文件
tryLock.luajavascriptif redis.call('SETNX', KEYS[1], ARGV[1]) == 1 then redis.call('PEXPIRE', KEYS[1], ARGV[2]); return 1 else return 0 end -
什么是
KEYS和ARGVKEYS是一个数组,存放"所有 key 名"ARGV是一个数组,存放"所有非 key 的参数"
-
为什么区分
KEYS和ARGV- 技术上"都用 ARGV"在单机上多数能跑,但违反规范且在集群/路由/工具支持上会出问题
- Redis 之所以设计区分
KEYS和ARGV,就是为了让服务器明确知道哪些参数是 key,用于槽位路由、读写保护和各种内部/客户端工具对 key 的正确识别与处理。
lua脚本常见用法
-
常见用法是使用
eval命令,传入lua脚本字符及参数,格式如下shellEVAL script numkeys key [key ...] arg [arg ...]- script:Lua 脚本内容(字符串)
- numkeys:后面有多少个 key
- key [key ...]:传给脚本的 key 列表(用 KEYS[i] 访问)
- arg [arg ...]:传给脚本的参数列表(用 ARGV[i] 访问)
-
示例,执行lua脚本
shell127.0.0.1:6379> EVAL "if redis.call('SETNX', KEYS[1], ARGV[1]) == 1 then redis.call('PEXPIRE', KEYS[1], ARGV[2]); return 1 else return 0 end" 1 myLockKey lockValue 30000 (integer) 11:有 1 个 keymyLockKey→KEYS[1]lockValue→ARGV[1]30000→ARGV[2](过期时间,毫秒)
加载lua脚本到内存
-
将lua脚本加载到内存中
shell127.0.0.1:6379> SCRIPT LOAD "if redis.call('SETNX', KEYS[1], ARGV[1]) == 1 then redis.call('PEXPIRE', KEYS[1], ARGV[2]); return 1 else return 0 end" "9aab773b2f1a82e88fcb760f3c42bbd9b9d3b1c3"- 返回值是一个 SHA1,使用
EVALSHA命令时会用到 - 用同一段脚本内容再执行一次
SCRIPT LOAD,会得到同一个 SHA
- 返回值是一个 SHA1,使用
-
把 Lua 脚本加载到内存的作用
核心作用:为了后面可以用
EVALSHA复用脚本,减少网络传输和解析开销。- 性能更好:
- 第一次用
SCRIPT LOAD把脚本送到 Redis,服务器记住脚本内容和它的 SHA1。 - 后面每次只发一个短短的 SHA1(
EVALSHA ...),不用再把整段 Lua 文本发过去,也不用每次重新解析脚本。
- 第一次用
- 避免重复发送脚本:
- 对于固定的业务脚本(比如分布式锁、库存扣减),脚本内容都是不变的,加载一次后就可以被很多次复用。
- 方便客户端封装:
- 客户端可以在启动时统一
SCRIPT LOAD一批脚本,以 SHA1 为 ID 管理它们,调用时只用EVALSHA即可。
- 客户端可以在启动时统一
- 性能更好:
-
在shell中,可以通过读取lua脚本文件内容的方式将lua脚本加载到内存
shellcd /opt/module/redis/bin ./redis-cli -a 123456 SCRIPT LOAD "$(cat ../lua-data/tryLock.lua)"- 这种方式只能在 shell 中用
redis-cli执行,不能在redis-cli里直接执行
- 这种方式只能在 shell 中用
检查lua脚本是否已加载
-
检查lua脚本是否已加载,实际上是检查lua脚本加载后返回的 SHA1是否存在
-
命令示例
shell127.0.0.1:6379> SCRIPT EXISTS 9aab773b2f1a82e88fcb760f3c42bbd9b9d3b1c3 1) (integer) 1- 如果返回
1,表示已加载 - 如果返回
0,表示这个 SHA 对应的脚本当前没在内存中
- 如果返回
执行lua脚本
-
脚本先加载到内存:
SCRIPT LOAD,得到SHA值,用EVALSHA执行这个SHA值shell127.0.0.1:6379> EVALSHA 9aab773b2f1a82e88fcb760f3c42bbd9b9d3b1c3 1 myLockKey lockValue 30000 (integer) 1 -
直接执行脚本字符串:
EVAL,这也是常见的用法shell127.0.0.1:6379> EVAL "if redis.call('SETNX', KEYS[1], ARGV[1]) == 1 then redis.call('PEXPIRE', KEYS[1], ARGV[2]); return 1 else return 0 end" 1 myLockKey lockValue 30000 (integer) 1 -
在shell中,通过脚本文件执行:redis-cli --eval tryLock.lua
shell./redis-cli -a 123456 --eval ../lua-data/tryLock.lua myLockKey , lockValue 30000- 逗号前是 KEYS,逗号后是 ARGV
参考资料
注意事项
- 部分内容由AI生成
- 如有不对,欢迎指正!!!