使用 Lua 和 Redis 函数扩展 Redis
Redis 提供了一个编程接口,可让您在服务器本身上执行自定义脚本。在 Redis 7 及更高版本中,您可以使用 Redis 函数
来管理和运行脚本。在 Redis 6.2 及更低版本中,您可以使用 Lua 脚本和 EVAL 命令
对服务器进行编程。
背景
根据定义, Redis 是一种*“抽象数据类型的特定领域语言”*。Redis 所说的语言由它的 命令
组成。大多数命令专门以不同的方式处理核心 数据类型
。在许多情况下,这些命令提供了开发人员在 Redis 中管理应用程序数据所需的所有功能。
Redis 中的可编程性一词意味着能够由服务器执行任意用户定义的逻辑。我们将这样的逻辑片段称为脚本。在我们的例子中,脚本可以处理它所在的数据,也就是数据局部性。此外,在 Redis 服务器中负责任地嵌入编程工作流有助于减少网络流量并提高整体性能。开发人员可以使用此功能来实现健壮的、特定于应用程序的 API。此类 API 可以封装业务逻辑并跨多个键和不同数据结构维护数据模型。
用户脚本由嵌入式沙盒脚本引擎在 Redis 中执行。目前,Redis 支持单一的脚本引擎 Lua 5.1
解释器。
有关完整文档,请参阅 Redis Lua API 参考
页面。
运行脚本
Redis 提供了两种运行脚本的方法。
首先,从 Redis 2.6.0 开始,该 EVAL
命令可以运行服务器端脚本。Eval 脚本提供了一种让 Redis 临时运行脚本的快速而直接的方法。但是,使用它们意味着脚本逻辑是应用程序的一部分(而不是 Redis 服务器的扩展)。每个运行脚本的应用程序实例都必须具有随时可供加载的脚本源代码。那是因为脚本只被服务器缓存并且是易变的。随着应用程序的增长,这种方法可能会变得更难开发和维护。
其次,在 v7.0 中添加的 Redis Functions 本质上是脚本,是一流的数据库元素。因此,函数将脚本与应用程序逻辑分离,并支持脚本的独立开发、测试和部署。要使用函数,需要先加载它们,然后它们可供所有连接的客户端使用。在这种情况下,将一个函数加载到数据库就变成了一个管理部署任务(例如加载一个 Redis 模块),它将脚本与应用程序分开。
请参阅以下页面了解更多信息:
在运行脚本或函数时,Redis 保证其原子执行。脚本的执行在其整个时间内阻塞所有服务器活动,类似于 事务
的语义。这些语义意味着脚本的所有效果要么尚未发生,要么已经发生。执行脚本的阻塞语义始终适用于所有连接的客户端。
请注意,这种阻塞方法的潜在缺点是执行慢速脚本不是一个好主意。创建快速脚本并不难,因为脚本的开销非常低。但是,如果您打算在应用程序中使用慢速脚本,请注意所有其他客户端都被阻止并且在运行时无法执行任何命令。
只读脚本
只读脚本是只执行不修改 Redis 中任何键的命令的脚本。可以通过将no-writes
标志
添加到脚本或通过使用以下只读脚本命令变体之一执行脚本来执行只读脚本: EVAL_RO
、 EVALSHA_RO
或 FCALL_RO
. 它们具有以下特性:
它们总是可以在副本上执行。
他们总是可以被 SCRIPT KILL
命令杀死。
当 redis 超过内存限制时,它们永远不会因 OOM 错误而失败。
它们在写入暂停期间不会被阻止,例如在协调故障转移期间发生的那些。
他们无法执行任何可能修改数据集的命令。
目前 PUBLISH
, SPUBLISH
和 PFCOUNT
也被认为是脚本中的写入命令,因为它们可能会尝试将命令传播到副本和 AOF 文件。
除了所有只读脚本提供的好处外,只读脚本命令还有以下优点:
只读脚本历史
Redis 7.0 引入了只读脚本和只读脚本命令
在 Redis 7.0.1 之前,PUBLISH
不考虑在脚本中写入命令 SPUBLISH
、PFCOUNT
在 Redis 7.0.1 之前,该no-writes
标志
并未暗示allow-oom
在 Redis 7.0.1 之前,该no-writes
标志不允许脚本在写入暂停期间运行。
推荐的方法是使用带有no-writes
标志的标准脚本命令,除非您需要前面提到的功能之一。
沙盒脚本上下文
Redis 将执行用户脚本的引擎放置在沙箱中。沙盒试图防止意外误用并减少来自服务器环境的潜在威胁。
脚本不应尝试访问 Redis 服务器的底层主机系统,例如文件系统、网络,或尝试执行 API 支持的系统调用以外的任何其他系统调用。
脚本应仅对存储在 Redis 中的数据和作为其执行参数提供的数据进行操作。
最长执行时间
脚本受最大执行时间的限制(默认设置为 5 秒)。这个默认超时是巨大的,因为脚本通常在不到一毫秒的时间内运行。该限制用于处理开发期间创建的意外无限循环。
redis.conf
可以通过或使用 CONFIG SET
命令来修改脚本可以以毫秒精度执行的最长时间。调用影响最大执行时间的配置参数busy-reply-threshold
。
当脚本达到超时阈值时,Redis 不会自动终止它。这样做会违反 Redis 与确保脚本是原子的脚本引擎之间的合同。中断脚本的执行有可能使数据集留下半写的更改。
因此,当脚本执行时间超过配置的超时时间时,会发生以下情况:
Redis 记录脚本运行时间过长。
它开始再次接受来自其他客户端的命令,但会以 BUSY 错误回复所有发送正常命令的客户端。此状态下允许的唯一命令是 SCRIPT KILL
、 FUNCTION KILL
和SHUTDOWN NOSAVE
。
SCRIPT KILL
可以使用and命令终止只执行只读命令的脚本 FUNCTION KILL
。这些命令不违反脚本语义,因为脚本尚未将数据写入数据集。
如果脚本甚至已经执行了一次写入操作,则唯一允许的命令是SHUTDOWN NOSAVE
停止服务器而不将当前数据集保存在磁盘上(基本上,服务器被中止)。