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 序列化协议 (RESP) 版本之间切换,以便由 redis.call()
和 redis.pcall()
返回的回复。它需要一个数字作为协议的版本。默认协议版本为 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
- 在脚本中可用:是
- 在函数中可用:是
以 Lua 字符串的形式返回当前 Redis 服务器版本。回复的格式为 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 数据类型到 Redis 数据类型的类型转换取决于用户选择的协议(请参阅 HELLO
命令)。
以下部分描述了根据协议的版本,Lua 和 Redis 之间的类型转换规则。
RESP2 到 Lua 类型的转换
以下类型转换规则适用于默认情况下执行的上下文,以及调用 redis.setresp(2)
之后。
- RESP2 整数回复 -> Lua 数字
- RESP2 大块字符串回复 -> Lua 字符串
- RESP2 数组回复 -> Lua 表(可能嵌套其他 Redis 数据类型)
- RESP2 状态回复 -> 包含单个 ok 字段的 Lua 表,该字段包含状态字符串
- RESP2 错误回复 -> 包含单个 err 字段的 Lua 表,该字段包含错误字符串
- RESP2 空大块回复 和 RESP2 空多块回复 -> Lua 布尔值类型 false
Lua 到 RESP2 类型的转换
以下类型转换规则在默认情况下适用,以及在用户调用 HELLO 2
之后适用。
- Lua 数字 -> RESP2 整数回复(数字将转换为整数)
- Lua 字符串 -> RESP2 大块字符串回复
- Lua 表(索引的非关联数组) -> RESP2 数组回复(在表中遇到的第一个 Lua
nil
值处截断,如果有的话) - 包含单个 ok 字段的 Lua 表 -> RESP2 状态回复
- 包含单个 err 字段的 Lua 表 -> 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"
最后一个示例演示了接收和返回 redis.call()
(或 redis.pcall()
)在 Lua 中的精确返回值,就像直接调用该命令时返回的那样。
以下示例显示了如何处理浮点数和包含 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 表,该字段包含一个 Lua 表,表示映射的字段和值。
- RESP 集合回复 -> 包含单个set字段的 Lua 表,该字段包含一个 Lua 表,表示集合的元素作为字段,每个字段的 Lua 布尔值为
true
。 - RESP3 空值 -> Lua
nil
。 - RESP3 真值回复 -> Lua 真值布尔值。
- RESP3 假值回复 -> Lua 假值布尔值。
- RESP3 双精度浮点数回复 -> 包含单个double字段的 Lua 表,该字段包含一个 Lua 数字,表示双精度浮点数值。
- RESP3 大数回复 -> 包含单个big_number字段的 Lua 表,该字段包含一个 Lua 字符串,表示大数数值。
- Redis 字面字符串回复 -> 包含单个verbatim_string字段的 Lua 表,该字段包含一个 Lua 表,其中包含两个字段,string和format,分别表示字面字符串及其格式。
注意:RESP3 大数 和 字面字符串 回复仅在 Redis v7.0 及更高版本中受支持。此外,目前,Redis Lua API 不支持 RESP3 的 属性、流式字符串 和 流式聚合数据类型。
Lua 到 RESP3 类型转换
无论脚本为使用 [redis.setresp()
函数] 设置回复的协议版本选择什么,当它调用redis.call()
或redis.pcall()
时,用户可以选择使用 RESP3(使用HELLO 3
命令)进行连接。尽管传入客户端连接的默认协议是 RESP2,但脚本应尊重用户的偏好并返回类型适当的 RESP3 回复,因此以下规则适用于该情况下的 Lua 到 RESP2 类型转换 部分中指定的规则。
- Lua 布尔值 -> RESP3 布尔值回复(注意,这与 RESP2 不同,在 RESP2 中,返回 Lua
true
布尔值会向 Redis 客户端返回数字 1,返回false
则会返回null
)。 - 包含单个map字段设置为关联 Lua 表的 Lua 表 -> RESP3 映射回复。
- 包含单个set字段设置为关联 Lua 表的 Lua 表 -> RESP3 集合回复。值可以设置为任何内容,并且会被丢弃。
- 包含单个double字段设置为关联 Lua 表的 Lua 表 -> RESP3 双精度浮点数回复。
- Lua nil -> RESP3 空值。
但是,如果连接设置为使用 RESP2 协议,即使脚本用 RESP3 类型响应进行回复,Redis 也会像常规命令一样自动将回复从 RESP3 转换为 RESP2。这意味着,例如,将 RESP3 映射类型返回到 RESP2 连接会导致回复被转换为一个扁平的 RESP2 数组,该数组由交替的字段名称及其值组成,而不是 RESP3 映射。
关于脚本的附加说明
在脚本中使用 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 库
- 自版本:7.4
- 在脚本中可用:是
- 在函数中可用:是
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)
此函数返回结构的大小(以字节为单位)。它接受一个 struct 格式字符串 作为其唯一参数。
用法示例
redis> EVAL "return struct.size('HH')" 0
(integer) 4
cjson 库
- 从版本开始:2.6.0
- 在脚本中可用:是
- 在函数中可用:是
cjson 库提供 Lua 中的快速 JSON 编码和解码。它提供了这些函数。
cjson.encode(x)
此函数为作为参数提供的 Lua 数据类型返回 JSON 编码字符串。
用法示例
redis> EVAL "return cjson.encode({ ['foo'] = 'bar' })" 0
"{\"foo\":\"bar\"}"
cjson.decode(x)
此函数从作为参数提供的 JSON 编码字符串返回 Lua 数据类型。
用法示例
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 位数字,反之亦然。