Redis Lua API 参考
在 Redis 中执行 Lua
Redis 包含一个嵌入式 Lua 5.1 解释器。解释器运行用户定义的 临时脚本 和 函数。脚本在沙盒环境中运行,只能访问特定的 Lua 包。此页面描述了执行环境中可用的包和 API。
沙盒环境
沙盒 Lua 环境尝试防止意外误用并减少来自服务器环境的潜在威胁。
脚本绝不应尝试访问 Redis 服务器的基础主机系统。其中包括文件系统、网络以及除 API 支持之外执行系统调用的任何其他尝试。
脚本应仅对存储在 Redis 中的数据和作为其执行参数提供的数据进行操作。
全局变量和函数
沙盒 Lua 执行环境阻止声明全局变量和函数。阻止全局变量是为了确保脚本和函数不会尝试维护除存储在 Redis 中的数据之外的任何运行时环境。在(不太常见)需要在执行之间维护环境的情况下,应将环境存储在 Redis 的键空间中。
在尝试执行以下代码段时,Redis 将返回“脚本尝试创建全局变量 'my_global_variable'”错误
my_global_variable = 'some value'
对于以下全局函数声明,情况类似
function my_global_function()
-- Do something amazing
end
当脚本尝试访问在运行时的上下文中未定义的任何全局变量时,也会出现类似的错误
-- The following will surely raise an error
return an_undefined_global_variable
相反,所有变量和函数定义都必须声明为局部变量。为此,您需要在声明前加上 local 关键字。例如,以下代码段将被 Redis 视为完全有效的
local my_local_variable = 'some value'
local function my_local_function()
-- Do something else, but equally amazing
end
注意:沙箱尝试阻止使用全局变量。使用 Lua 的调试功能或其他方法(例如更改用于实现全局保护的元表)来规避沙箱并不难。但是,很难意外地规避保护。如果用户弄乱了 Lua 全局状态,则无法保证 AOF 和复制的一致性。换句话说,就是不要这样做。
导入的 Lua 模块
在沙箱执行上下文中不支持使用导入的 Lua 模块。沙箱执行上下文通过禁用 Lua 的 require
函数 来阻止加载模块。
Redis 随附的并且您可以在脚本中使用的唯一库列在 运行时库 部分下。
运行时全局变量
虽然沙箱阻止用户声明全局变量,但执行上下文预先填充了其中几个。
redis 单例
redis 单例是一个对象实例,可以从所有脚本中访问。它提供了从脚本与 Redis 交互的 API。其描述如下 所示。
KEYS 全局变量
- 版本:2.6.0 起
- 在脚本中可用:是
- 在函数中可用:否
重要:为了确保脚本在独立和集群部署中正确执行,函数访问的所有键的名称都必须明确地作为输入键参数提供。脚本只能访问其名称作为输入参数提供的键。脚本绝不能访问具有以编程方式生成名称的键或基于存储在数据库中的数据结构的内容的键。
KEYS 全局变量仅适用于 临时脚本。它预先填充了所有键名输入参数。
ARGV 全局变量
- 版本:2.6.0 起
- 在脚本中可用:是
- 在函数中可用:否
ARGV 全局变量仅在 临时脚本 中可用。它预先填充了所有常规输入参数。
redis 对象
- 版本:2.6.0 起
- 在脚本中可用:是
- 在函数中可用:是
Redis Lua 执行上下文始终提供一个名为 redis 的对象的单例实例。redis 实例使脚本能够与正在运行它的 Redis 服务器进行交互。以下是 redis 对象实例提供的 API。
redis.call(command [,arg...])
- 版本:2.6.0 起
- 在脚本中可用:是
- 在函数中可用:是
redis.call()
函数调用给定的 Redis 命令并返回其回复。其输入是命令和参数,调用后,它在 Redis 中执行命令并返回回复。
例如,我们可以从脚本中调用 ECHO
命令并返回其回复,如下所示
return redis.call('ECHO', 'Echo, echo... eco... o...')
如果 redis.call()
触发运行时异常,则原始异常会自动作为错误返回给用户。因此,尝试执行以下临时脚本将失败并生成运行时异常,因为 ECHO
只接受一个参数
redis> EVAL "return redis.call('ECHO', 'Echo,', 'echo... ', 'eco... ', 'o...')" 0
(error) ERR Wrong number of args calling Redis command from script script: b0345693f4b77517a711221050e76d24ae60b7f7, on @user_script:1.
请注意,调用可能会由于各种原因而失败,请参阅 低内存条件下的执行 和 脚本标志
要处理 Redis 运行时错误,请改用 redis.pcall()
。
redis.pcall(command [,arg...])
- 版本:2.6.0 起
- 在脚本中可用:是
- 在函数中可用:是
此函数可以处理 Redis 服务器引发的运行时错误。redis.pcall()
函数的行为与 redis.call()
完全相同,除了它
- 始终返回回复。
- 绝不会抛出运行时异常,并且如果服务器抛出运行时异常,则会返回
redis.error_reply
。
以下演示如何使用 redis.pcall()
来拦截和处理临时脚本上下文中的运行时异常。
local reply = redis.pcall('ECHO', unpack(ARGV))
if reply['err'] ~= nil then
-- Handle the error sometime, but for now just log it
redis.log(redis.LOG_WARNING, reply['err'])
reply['err'] = 'ERR Something is wrong, but no worries, everything is under control'
end
return reply
使用多个参数评估此脚本将返回
redis> EVAL "..." 0 hello world
(error) ERR Something is wrong, but no worries, everything is under control
redis.error_reply(x)
- 版本:2.6.0 起
- 在脚本中可用:是
- 在函数中可用:是
这是一个返回 错误回复 的帮助函数。该帮助函数接受一个字符串参数,并返回一个 Lua 表,其中 err 字段设置为该字符串。
以下代码的结果是 error1 和 error2 在所有意图和目的上都是相同的
local text = 'ERR My very special error'
local reply1 = { err = text }
local reply2 = redis.error_reply(text)
因此,两种形式都可用作从脚本返回错误回复的方法
redis> EVAL "return { err = 'ERR My very special table error' }" 0
(error) ERR My very special table error
redis> EVAL "return redis.error_reply('ERR My very special reply error')" 0
(error) ERR My very special reply error
有关返回 Redis 状态回复,请参阅 redis.status_reply()
。有关返回其他响应类型,请参阅 数据类型转换。
注意:根据惯例,Redis 使用错误字符串的第一个单词作为特定错误的唯一错误代码,或使用 ERR
作为通用错误。建议脚本遵循此惯例,如上例所示,但这并不是强制性的。
redis.status_reply(x)
- 版本:2.6.0 起
- 在脚本中可用:是
- 在函数中可用:是
这是一个帮助函数,它返回一个简单字符串回复。“OK”是标准 Redis 状态回复的一个示例。Lua API 将状态回复表示为具有单个字段(ok)的表,并设置一个简单的状态字符串。
以下代码的结果是,status1 和 status2 在所有意图和目的上都是相同的
local text = 'Frosty'
local status1 = { ok = text }
local status2 = redis.status_reply(text)
因此,这两种形式都是从脚本返回状态回复的有效方法
redis> EVAL "return { ok = 'TICK' }" 0
TICK
redis> EVAL "return redis.status_reply('TOCK')" 0
TOCK
有关返回 Redis 错误回复,请参阅 redis.error_reply()
。有关返回其他响应类型,请参阅数据类型转换。
redis.sha1hex(x)
- 版本:2.6.0 起
- 在脚本中可用:是
- 在函数中可用:是
此函数返回其单个字符串参数的 SHA1 十六进制摘要。
例如,你可以获取空字符串的 SHA1 摘要
redis> EVAL "return redis.sha1hex('')" 0
"da39a3ee5e6b4b0d3255bfef95601890afd80709"
redis.log(level, message)
- 版本:2.6.0 起
- 在脚本中可用:是
- 在函数中可用:是
此函数写入 Redis 服务器日志。
它需要两个输入参数:日志级别和消息。消息是要写入日志文件的一个字符串。日志级别可以是以下之一
redis.LOG_DEBUG
redis.LOG_VERBOSE
redis.LOG_NOTICE
redis.LOG_WARNING
这些级别映射到服务器的日志级别。日志仅记录级别等于或大于服务器的 loglevel
配置指令的消息。
以下代码段
redis.log(redis.LOG_WARNING, 'Something is terribly wrong')
将在服务器日志中生成类似于以下内容的一行
[32343] 22 Mar 15:21:39 # Something is terribly wrong
redis.setresp(x)
- 版本:6.0.0 起
- 在脚本中可用:是
- 在函数中可用:是
此函数允许执行脚本在 redis.call()
和 redis.pcall()
返回的回复中切换 Redis 序列化协议 (RESP) 版本。它需要一个数字参数作为协议的版本。默认协议版本为 2,但可以切换到版本 3。
以下是如何切换到 RESP3 回复的示例
redis.setresp(3)
有关类型转换的更多信息,请参阅数据类型转换。
redis.set_repl(x)
- 版本:3.2.0 起
- 在脚本中可用:是
- 在函数中可用:否
注意:此功能仅在使用脚本效果复制时可用。在使用逐字脚本复制时调用它会导致错误。从 Redis 版本 2.6.0 开始,脚本被逐字复制,这意味着脚本的源代码被发送到副本执行并存储在 AOF 中。在版本 3.2.0 中添加的另一种复制模式允许仅复制脚本的效果。从 Redis 版本 7.0 开始,不再支持脚本复制,唯一可用的复制模式是脚本效果复制。
警告:这是一个高级功能。滥用会导致损坏,因为它违反了 Redis 主服务器、其副本和 AOF 内容保持相同逻辑内容的契约。
此函数允许脚本断言对其效果如何传播到副本和 AOF 的控制权。脚本的效果是它调用的 Redis 写入命令。
默认情况下,脚本执行的所有写入命令都会被复制。但是,有时更好地控制此行为会很有帮助。例如,当仅将中间值存储在主服务器中时,可能会出现这种情况。
考虑一个脚本,它使用 SUNIONSTORE
对两个集合求交集并将结果存储在临时键中。然后它从交集中选取五个随机元素 (SRANDMEMBER
) 并将它们 (SADD
) 存储在另一个集合中。最后,在返回之前,它删除存储两个源集合交集的临时键。
在这种情况下,只需要复制包含五个随机选择的元素的新集合。复制 SUNIONSTORE
命令和临时键的 DEL
是不必要的,也是浪费的。
redis.set_repl()
函数指示服务器如何根据复制处理后续写入命令。它接受一个输入参数,该参数只能是以下之一
redis.REPL_ALL
:将效果复制到 AOF 和副本。redis.REPL_AOF
:仅将效果复制到 AOF。redis.REPL_REPLICA
:仅将效果复制到副本。redis.REPL_SLAVE
:与REPL_REPLICA
相同,维护向后兼容性。redis.REPL_NONE
:完全禁用效果复制。
默认情况下,当脚本开始执行时,脚本引擎会初始化为 redis.REPL_ALL
设置。您可以在脚本执行期间随时调用 redis.set_repl()
函数,以在不同的复制模式之间切换。
下面是一个简单的示例
redis.replicate_commands() -- Enable effects replication in versions lower than Redis v7.0
redis.call('SET', KEYS[1], ARGV[1])
redis.set_repl(redis.REPL_NONE)
redis.call('SET', KEYS[2], ARGV[2])
redis.set_repl(redis.REPL_ALL)
redis.call('SET', KEYS[3], ARGV[3])
如果您通过调用 EVAL "..." 3 A B C 1 2 3
运行此脚本,结果将是仅在副本和 AOF 上创建键 A 和 C。
redis.replicate_commands()
- 版本:3.2.0 起
- 版本之前:7.0.0
- 在脚本中可用:是
- 在函数中可用:否
此函数将脚本的复制模式从逐字复制切换到效果复制。您可以使用它来覆盖 Redis 在 7.0 版本之前使用的默认逐字脚本复制模式。
注意:Redis v7.0 版本不再支持逐字脚本复制。支持的默认且唯一的脚本复制模式是脚本效果复制。有关更多信息,请参阅 复制命令而不是脚本
redis.breakpoint()
- 版本:3.2.0 起
- 在脚本中可用:是
- 在函数中可用:否
使用 Redis Lua 调试器 时,此函数触发断点。
redis.debug(x)
- 版本:3.2.0 起
- 在脚本中可用:是
- 在函数中可用:否
此函数在 Redis Lua 调试器 控制台中打印其参数。
redis.acl_check_cmd(command [,arg...])
- 版本:7.0.0 起
- 在脚本中可用:是
- 在函数中可用:是
此函数用于检查运行脚本的当前用户是否具有使用给定参数执行给定命令的 ACL 权限。
如果当前用户有权执行命令(通过调用 redis.call 或 redis.pcall),则返回值为布尔值 true
,否则为 false
。
如果传递的命令或其参数无效,该函数将引发错误。
redis.register_function
- 版本:7.0.0 起
- 脚本中不可用:否
- 在函数中可用:是
此函数仅可从 FUNCTION LOAD
命令的上下文中使用。调用时,它会将一个函数注册到已加载的库中。该函数可以使用位置参数或命名参数调用。
位置参数:redis.register_function(name, callback)
redis.register_function
的第一个参数是表示函数名称的 Lua 字符串。redis.register_function
的第二个参数是 Lua 函数。
用法示例
redis> FUNCTION LOAD "#!lua name=mylib\n redis.register_function('noop', function() end)"
命名参数:redis.register_function{function_name=name, callback=callback, flags={flag1, flag2, ..}, description=description}
命名参数变体接受以下参数
- function_name:函数的名称。
- callback:函数的回调。
- flags:字符串数组,每个字符串都是一个函数标志(可选)。
- description:函数的描述(可选)。
function_name 和 callback 都是必需的。
用法示例
redis> FUNCTION LOAD "#!lua name=mylib\n redis.register_function{function_name='noop', callback=function() end, flags={ 'no-writes' }, description='Does nothing'}"
脚本标志
重要:谨慎使用脚本标志,如果使用不当可能会产生负面影响。请注意,Eval 脚本的默认值与下面提到的函数的默认值不同,请参阅 Eval 标志
注册函数或加载 Eval 脚本时,服务器不知道它如何访问数据库。默认情况下,Redis 假设所有脚本都读写数据。这会导致以下行为
- 它们可以读写数据。
- 它们可以在集群模式下运行,并且无法运行访问不同哈希槽的键的命令。
- 拒绝对过时副本执行操作,以避免不一致的读取。
- 拒绝在内存不足的情况下执行操作,以避免超过配置的阈值。
您可以使用以下标志并指示服务器以不同的方式处理脚本的执行
-
no-writes
:此标志表示脚本只读取数据,从不写入。默认情况下,Redis 将拒绝对只读副本执行带标志的脚本(带有 shebang 的函数和 Eval 脚本),因为它们可能尝试执行写入。类似地,服务器将不允许使用
FCALL_RO
/EVAL_RO
调用脚本。最后,当由于磁盘错误导致数据持久性面临风险时,执行也会被阻止。使用此标志允许执行脚本
- 使用
FCALL_RO
/EVAL_RO
- 在只读副本上。
- 即使存在磁盘错误(Redis 无法持久化,因此拒绝写入)。
- 当超过内存限制时,因为它意味着脚本不会增加内存消耗(请参见下面的
allow-oom
)
但是,请注意,如果脚本尝试调用写入命令,服务器将返回错误。另请注意,目前
PUBLISH
、SPUBLISH
和PFCOUNT
在脚本中也被视为写入命令,因为它们可能尝试将命令传播到副本和 AOF 文件。有关更多信息,请参阅 只读脚本
- 使用
-
allow-oom
:使用此标志允许在服务器内存不足(OOM)时执行脚本。除非使用,否则 Redis 将在 OOM 状态下拒绝执行带有标志的脚本(带有 shebang 的函数和 Eval 脚本)。此外,当您使用此标志时,脚本可以调用任何 Redis 命令,包括在此状态下通常不允许的命令。指定
no-writes
或使用FCALL_RO
/EVAL_RO
也意味着脚本可以在 OOM 状态下运行(无需指定allow-oom
) -
allow-stale
:当replica-serve-stale-data
配置设置为no
时,启用对过时副本运行带标志的脚本(带有 shebang 的函数和 Eval 脚本)的标志。可以通过让过时副本返回运行时错误,将 Redis 设置为防止因使用旧数据而导致数据一致性问题。对于不访问数据的脚本,可以设置此标志以允许过时的 Redis 副本运行脚本。但请注意,脚本仍然无法执行任何访问过时数据的命令。
-
no-cluster
:此标志会导致脚本在 Redis 集群模式下返回错误。Redis 允许在独立模式和集群模式下执行脚本。设置此标志可防止针对集群中的节点执行脚本。
-
allow-cross-slot-keys
:允许脚本访问来自多个槽位的键的标志。Redis 通常会阻止任何单个命令访问哈希到多个槽位的键。此标志允许脚本打破此规则并访问脚本中访问多个槽位的键。对脚本声明的键仍然始终需要哈希到单个槽位。不建议访问来自多个槽位的键,因为应用程序应设计为一次仅访问来自单个槽位的键,从而允许槽位在 Redis 服务器之间移动。
当集群模式禁用时,此标志无效。
redis.REDIS_VERSION
- 版本:7.0.0 起
- 在脚本中可用:是
- 在函数中可用:是
将当前 Redis 服务器版本作为 Lua 字符串返回。回复的格式为 MM.mm.PP
,其中
- MM: 是主版本。
- mm: 是次要版本。
- PP: 是补丁级别。
redis.REDIS_VERSION_NUM
- 版本:7.0.0 起
- 在脚本中可用:是
- 在函数中可用:是
将当前 Redis 服务器版本作为数字返回。回复是一个十六进制值,其结构为 0x00MMmmPP
,其中
- MM: 是主版本。
- mm: 是次要版本。
- PP: 是补丁级别。
数据类型转换
除非引发运行时异常,否则 redis.call()
和 redis.pcall()
将执行命令的回复返回给 Lua 脚本。这些函数的 Redis 回复会自动转换为 Lua 的原生数据类型。
类似地,当 Lua 脚本使用 return
关键字返回回复时,该回复会自动转换为 Redis 协议。
换句话说;Redis 的回复和 Lua 的数据类型之间存在一对一映射,Lua 的数据类型和 Redis 协议 数据类型之间也存在一对一映射。底层设计是这样的:如果 Redis 类型转换为 Lua 类型并转换回 Redis 类型,则结果与初始值相同。
从 Redis 协议回复(即 redis.call()
和 redis.pcall()
的回复)到 Lua 数据类型的类型转换取决于脚本使用的 Redis 序列化协议版本。脚本执行期间的默认协议版本为 RESP2。脚本可以通过调用 redis.setresp()
函数来切换回复的协议版本。
从脚本返回的 Lua 数据类型的类型转换取决于用户选择的协议(请参阅 HELLO
命令)。
以下部分描述了 Lua 和 Redis 之间根据协议版本进行的类型转换规则。
RESP2 到 Lua 类型转换
以下类型转换规则默认情况下适用于执行的上下文,以及在调用 redis.setresp(2)
之后。
- RESP2 整数回复 -> Lua 数字
- RESP2 批量字符串回复 -> Lua 字符串
- RESP2 数组回复 -> Lua 表(可能嵌套其他 Redis 数据类型)
- RESP2 状态回复 -> Lua 表,其中包含一个 ok 字段,其中包含状态字符串
- RESP2 错误回复 -> Lua 表,其中包含一个 err 字段,其中包含错误字符串
- RESP2 空批量回复 和 空多批量回复 -> Lua false 布尔类型
Lua 到 RESP2 类型转换
以下类型转换规则默认情况下适用,以及在用户调用 HELLO 2
之后。
- Lua 数字 -> RESP2 整数回复(数字转换为整数)
- Lua 字符串 -> RESP 批量字符串回复
- Lua 表(索引的非关联数组) -> RESP2 数组回复(如果表中遇到第一个 Lua
nil
值,则截断) - Lua 表,其中包含一个 ok 字段 -> RESP2 状态回复
- Lua 表,其中包含一个 err 字段 -> RESP2 错误回复
- Lua 布尔 false -> RESP2 空批量回复
有一个额外的 Lua 到 Redis 转换规则,没有相应的 Redis 到 Lua 转换规则
- Lua 布尔
true
-> RESP2 整数回复,值为 1。
关于将 Lua 转换为 Redis 数据类型,还有三条额外的规则需要注意
- Lua 具有单一的数字类型,即 Lua 数字。整数和浮点数之间没有区别。因此,我们总是将 Lua 数字转换为整数回复,如果存在小数部分,则将其删除。如果您想返回 Lua 浮点数,则应将其作为字符串返回,就像 Redis 本身所做的那样(例如,请参阅
ZSCORE
命令)。 - 由于 Lua 表的语义,在 Lua 数组中没有简单的方法来包含 nil。因此,当 Redis 将 Lua 数组转换为 RESP 时,当遇到 Lua
nil
值时,转换将停止。 - 当 Lua 表格是包含键及其各自值关联数组时,转换后的 Redis 回复将不包含它们。
Lua 到 RESP2 类型转换示例
redis> EVAL "return 10" 0
(integer) 10
redis> EVAL "return { 1, 2, { 3, 'Hello World!' } }" 0
1) (integer) 1
2) (integer) 2
3) 1) (integer) 3
1) "Hello World!"
redis> EVAL "return redis.call('get','foo')" 0
"bar"
最后一个示例演示如何接收并返回 Lua 中 redis.call()
(或 redis.pcall()
)的确切返回值,就像直接调用命令时返回一样。
以下示例展示了如何处理包含 nil 和键的浮点数和数组
redis> EVAL "return { 1, 2, 3.3333, somekey = 'somevalue', 'foo', nil , 'bar' }" 0
1) (integer) 1
2) (integer) 2
3) (integer) 3
4) "foo"
如你所见,浮点值3.333 被转换为整数3,somekey 键及其值被省略,字符串“bar”不会返回,因为它前面有一个 nil
值。
RESP3 到 Lua 类型转换
RESP3 是 Redis 序列化协议 的较新版本。从 Redis v6.0 开始,它作为一种选择加入选项提供。
执行脚本可以在执行期间调用 redis.setresp
函数,并切换用于从 Redis 命令(可以通过 redis.call()
或 redis.pcall()
调用)返回回复的协议版本。
一旦 Redis 的回复采用 RESP3 协议,所有 RESP2 到 Lua 转换 规则均适用,并添加以下内容
- RESP3 map 回复 -> Lua 表格,其中包含一个 map 字段,该字段包含一个表示 map 的字段和值的 Lua 表格。
- RESP set 回复 -> Lua 表格,其中包含一个 set 字段,该字段包含一个表示 set 元素的 Lua 表格作为字段,每个字段都具有 Lua 布尔值
true
。 - RESP3 null -> Lua
nil
。 - RESP3 true 回复 -> Lua true 布尔值。
- RESP3 false 回复 -> Lua false 布尔值。
- RESP3 double 回复 -> Lua 表格,其中包含一个 double 字段,该字段包含一个表示 double 值的 Lua 数字。
- RESP3 big number 回复 -> Lua 表格,其中包含一个 big_number 字段,该字段包含一个表示 big number 值的 Lua 字符串。
- Redis 原样字符串回复 -> Lua 表格,其中包含一个 verbatim_string 字段,该字段包含一个 Lua 表格,其中包含两个字段,string 和 format,分别表示原样字符串及其格式。
注意:RESP3 大数字 和 原样字符串 回复仅在 Redis v7.0 及更高版本中受支持。此外,目前,RESP3 的 属性、流式字符串 和 流式聚合数据类型 不受 Redis Lua API 支持。
Lua 到 RESP3 类型转换
无论脚本在调用 redis.call()
或 redis.pcall()
时为带有回复的协议版本设置的选择是什么(使用 [redis.setresp()
函数]),用户都可以选择对连接使用 RESP3(使用 HELLO 3
命令)。尽管传入客户端连接的默认协议是 RESP2,但脚本应尊重用户的偏好并返回类型适当的 RESP3 回复,因此,在这种情况下的 Lua 到 RESP2 类型转换 部分中指定的规则之上应用以下规则。
- Lua 布尔值 -> RESP3 布尔值回复(请注意,这与 RESP2 相比发生了变化,在 RESP2 中,返回布尔值 Lua
true
会向 Redis 客户端返回数字 1,而返回false
则返回null
。 - Lua 表格,其中一个 map 字段设置为关联 Lua 表格 -> RESP3 map 回复。
- Lua 表格,其中一个 set 字段设置为关联 Lua 表格 -> RESP3 set 回复。值可以设置为任何值,并且无论如何都会被丢弃。
- Lua 表格,其中一个 double 字段设置为关联 Lua 表格 -> RESP3 double 回复。
- Lua nil -> RESP3 null。
但是,如果连接设置为使用 RESP2 协议,即使脚本以 RESP3 类型回复,Redis 也会自动对回复执行 RESP3 到 RESP2 转换,就像常规命令一样。这意味着,例如,将 RESP3 map 类型返回到 RESP2 连接将导致回复转换为扁平 RESP2 数组,该数组由交替的字段名及其值组成,而不是 RESP3 map。
关于脚本的其他说明
在脚本中使用 SELECT
您可以从 Lua 脚本中调用 SELECT
命令,就像使用任何普通客户端连接一样。但是,行为的一个细微方面在 Redis 版本 2.8.11 和 2.8.12 之间发生了变化。在 Redis 版本 2.8.12 之前,Lua 脚本选择的数据库被 设置为调用它的客户端连接的当前数据库。从 Redis 版本 2.8.12 开始,Lua 脚本选择的数据库仅影响脚本的执行上下文,并且不会修改调用脚本的客户端选择的数据库。补丁级别版本之间的这种语义更改是必需的,因为旧行为与 Redis 的复制本质上不兼容,并且引入了错误。
运行时库
Redis Lua 运行时上下文总是附带几个预导入的库。
以下 标准 Lua 库 可供使用
此外,以下外部库已加载且脚本可访问
os 库
- 版本:8.0.0 起
- 在脚本中可用:是
- 在函数中可用:是
os 提供了一组用于处理日期、时间和系统命令的函数。更多详细信息请参阅 操作系统工具。请注意,出于沙盒安全考虑,目前仅公开以下 os 函数
os.clock()
struct 库
- 版本:2.6.0 起
- 在脚本中可用:是
- 在函数中可用:是
struct 是一个用于在 Lua 中打包和解包类似 C 的结构的库。它提供以下函数
struct 的所有函数都希望其第一个参数是 格式字符串。
struct 格式
以下是 struct 函数的有效格式字符串
>
:大端<
:小端![num]
:对齐x
:填充b/B
:有符号/无符号字节h/H
:有符号/无符号短整型l/L
:有符号/无符号长整型T
:size_ti/In
:大小为 n 的有符号/无符号整数(默认为 int 的大小)cn
:n 个字符的序列(从/到字符串);打包时,n == 0 表示整个字符串;解包时,n == 0 表示使用先前读取的数字作为字符串的长度。s
:以零结尾的字符串f
:浮点数d
:双精度浮点数
struct.pack(x)
此函数从值返回一个结构编码的字符串。它接受 struct 格式字符串 作为其第一个参数,后跟要编码的值。
用法示例
redis> EVAL "return struct.pack('HH', 1, 2)" 0
"\x01\x00\x02\x00"
struct.unpack(x)
此函数从结构返回解码的值。它接受 struct 格式字符串 作为其第一个参数,后跟编码的结构字符串。
用法示例
redis> EVAL "return { struct.unpack('HH', ARGV[1]) }" 0 "\x01\x00\x02\x00"
1) (integer) 1
2) (integer) 2
3) (integer) 5
struct.size(x)
此函数返回一个结构的大小(以字节为单位)。它接受一个 结构 格式字符串 作为其唯一参数。
用法示例
redis> EVAL "return struct.size('HH')" 0
(integer) 4
cjson 库
- 版本:2.6.0 起
- 在脚本中可用:是
- 在函数中可用:是
cjson 库提供了从 Lua 快速 JSON 编码和解码。它提供了这些函数。
cjson.encode(x)
此函数返回一个 JSON 编码的字符串,用于作为其参数提供的 Lua 数据类型。
用法示例
redis> EVAL "return cjson.encode({ ['foo'] = 'bar' })" 0
"{\"foo\":\"bar\"}"
cjson.decode(x)
此函数返回一个 Lua 数据类型,用于作为其参数提供的 JSON 编码的字符串。
用法示例
redis> EVAL "return cjson.decode(ARGV[1])['foo']" 0 '{"foo":"bar"}'
"bar"
cmsgpack 库
- 版本:2.6.0 起
- 在脚本中可用:是
- 在函数中可用:是
cmsgpack 库提供了从 Lua 快速 MessagePack 编码和解码。它提供了这些函数。
cmsgpack.pack(x)
此函数返回作为参数给定的 Lua 数据类型的打包字符串编码。
用法示例
redis> EVAL "return cmsgpack.pack({'foo', 'bar', 'baz'})" 0
"\x93\xa3foo\xa3bar\xa3baz"
cmsgpack.unpack(x)
此函数返回从解码其输入字符串参数中解包的值。
用法示例
redis> EVAL "return cmsgpack.unpack(ARGV[1])" 0 "\x93\xa3foo\xa3bar\xa3baz"
1) "foo"
2) "bar"
3) "baz"
bit 库
- 自版本:2.8.18
- 在脚本中可用:是
- 在函数中可用:是
bit 库提供了对数字的按位运算。其文档位于 Lua BitOp 文档 中。它提供了以下函数。
bit.tobit(x)
将数字标准化为按位运算的数字范围并返回它。
用法示例
redis> EVAL 'return bit.tobit(1)' 0
(integer) 1
bit.tohex(x [,n])
将其第一个参数转换为十六进制字符串。十六进制数字的数量由可选的第二个参数的绝对值给出。
用法示例
redis> EVAL 'return bit.tohex(422342)' 0
"000671c6"
bit.bnot(x)
返回其参数的按位 非。
bit.bnot(x)
bit.bor(x1 [,x2...])
, bit.band(x1 [,x2...])
和 bit.bxor(x1 [,x2...])
返回其所有参数的按位 或、按位 与或按位 异或。请注意,允许超过两个参数。
用法示例
redis> EVAL 'return bit.bor(1,2,4,8,16,32,64,128)' 0
(integer) 255
bit.lshift(x, n)
, bit.rshift(x, n)
和 bit.arshift(x, n)
返回其第一个参数的按位逻辑 左移、按位逻辑 右移或按位 算术右移,位数由第二个参数给出。
bit.rol(x, n)
和 bit.ror(x, n)
返回其第一个参数的按位 左旋转或按位 右旋转,位数由第二个参数给出。从一侧移出的位在另一侧移回。
bit.bswap(x)
交换其参数的字节并返回它。这可用于将小端 32 位数字转换为大端 32 位数字,反之亦然。