Redis中的Lua脚本
Redis中的Lua脚本
参考学习资料
- todo
Lua 脚本
1.Redis 中的Lua 脚本
Redis 的 Lua 脚本功能允许用户在 Redis 服务器端执行自定义的 Lua 脚本,以实现原子操作和复杂逻辑
其核心点包括:
原子性:Lua 脚本的所有命令在执行过程中是原子的,避免了并发修改带来的问题
减少网络往返次数:通过在服务器端执行脚本,减少了客户端和服务器之间的网络往返次数,提高了性能
复杂操作:可以在 Lua 脚本中执行复杂的逻辑,比如批量更新、条件更新等,超过了单个 Redis 命令的能力
例如常见基于 Redis 实现分布式锁就需要结合 lua 脚本来实现
lua 本身是不具备原子性的,但由于 Redis 的命令是单线程执行的,它会把整个lua 脚本作为一个命令执行,会阻塞其间接受到的其他命令,这就保证了lua 脚本的原子性
2.Lua脚本使用
核心概念
Lua 语言是一门轻量级的脚本语言,使用C语言编写,具有简洁的语法,易于学习和使用,广泛应用于游戏开发嵌入式系统等领域。它设计的主要目的就是为了嵌入其他程序,实现灵活的扩展和定制功能,并且具备快速的执行速度,能够满足各种开发需求
Lua 语言具有以下优点:
1.轻量级:占用资源少,易于嵌入其他程序
2.简洁高效:语法简单,执行效率高
3.跨平台:可在多种操作系统上运行
4.扩展性强:方便与其他语言集成
5.快速开发:减少开发时间和成本
6.灵活性高:适应各种不同的项目需求
7.易于学习:入门难度低,容易掌握
8.解释执行:无需编译,便于调试
9.资源占用少:对系统资源的需求相对较低
10.社区活跃:有丰富的文档和工具支持
基本语法
Lua 脚本基本规则
- 调用Redis命令
- 使用
redis.call
调用Redis原生命令 - 使用
redis.pcall
进行安全调用(如果脚本中某些命令执行失败可以捕获错误,而不是中断执行,可以支持在脚本内处理错误)
- 使用
- 参数传递
KEYS
表示传入的键名数组ARGV
表示传入的参数数组
语法规则
# 语法规则
EVAL script numkeys key [key ...] arg [arg ...]
script
是要执行的Lua脚本numkeys
是传递给脚本的键数量key [key ...]
是被Lua脚本使用的键arg [arg ...]
是传递给Lua脚本的参数
应用场景
原子计数器
使用INCR
原子性递增counter_key的值:
- 调用
redis.call('INCR', KEYS[1])
实现递增 - 返回递增后的值
EVAL "local current = redis.call('INCR', KEYS[1]) return current" 1 counter_key
限流控制
简单限流器的实现,每分钟允许访问100次
- 检查当前访问次数是否超出限制
- 如果未超出限制,则增加计数并设置过期时间
EVAL [[
local current = tonumber(redis.call('GET', KEYS[1]) or 0)
if current < ARGV[1] then
redis.call('INCR', KEYS[1])
redis.call('EXPIRE', KEYS[1], 60)
return true
else
return false
end
]] 1 rate_limit_key 100
预加载脚本
可以使用 SCRIPT LOAD
将 Lua 脚本预加载到 Redis,以提高执行效率
SCRIPT LOAD "return redis.call('GET', KEYS[1])"
应用注意点
(1)lua 脚本执行
lua 本身不具备原子性,是因为Redis是单线程执行指令,因此将多个命令打包到lua脚本中,再由redis执行进而确保操作的原子性。
因此 Redis 执行 lua 脚本期间是无法处理其他命令,因此如果 lua 脚本的业务过于复杂,则会产生长时间的阻塞,所以编写 Lua 脚本时应尽量保持简短和高效。Redis 默认限制 lua 执行脚本时间为 5s,如果超过这个时间则会终止且抛错,可以通过 lua-time-limit
调整时长
(2)如果lua脚本中的部分命令执行失败会怎样?
-- 正确的 SET 命令
redis.call("SET", "my_key", "mianshiya")
-- 错误的 HSET 命令
redis.call("HSET", "my_hash", "field_mianshiya") -- 缺少值参数
如果执行上述lua脚本,则会发现my_key会被正常执行,但由于hset缺少值参数导致命令执行报错,但my_key是不会回滚的。因此实际上lua脚本并不能完全保证原子性,但官方认为这种错误正常情况下不可能发生(属于开发者的人为因素:例如语法错误、执行了不该执行的命令 导致的问题)