Redis 可编程性

使用 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 函数本质上是作为一等数据库元素的脚本。因此,函数将脚本与应用逻辑解耦,并支持脚本的独立开发、测试和部署。要使用函数,首先需要加载它们,然后所有连接的客户端都可以使用。在这种情况下,将函数加载到数据库成为一个管理部署任务(例如加载 Redis 模块),从而将脚本与应用分开。

请参考以下页面了解更多信息

运行脚本或函数时,Redis 保证其原子性执行。脚本的执行会阻塞所有服务器活动,直到其完成,这类似于事务的语义。这些语义意味着脚本的所有效果要么尚未发生,要么已经发生。执行脚本的阻塞语义始终适用于所有连接的客户端。

请注意,这种阻塞方法的一个潜在缺点是,执行慢速脚本不是一个好主意。由于脚本开销非常低,创建快速脚本并不难。然而,如果您打算在应用中使用慢速脚本,请注意,在它运行时,所有其他客户端都会被阻塞,无法执行任何命令。

只读脚本

只读脚本是仅执行不修改 Redis 中任何键的命令的脚本。可以通过向脚本添加no-writes标志或使用以下只读脚本命令变体之一来执行只读脚本:EVAL_ROEVALSHA_ROFCALL_RO。它们具有以下属性

  • 它们总是可以在副本上执行。
  • 它们总是可以通过SCRIPT KILL命令终止。
  • 当 Redis 超过内存限制时,它们永远不会因 OOM(内存溢出)错误而失败。
  • 它们在写入暂停期间不会被阻塞,例如协调故障转移期间发生的写入暂停。
  • 它们不能执行任何可能修改数据集的命令。
  • 目前,PUBLISHSPUBLISHPFCOUNT 在脚本中也被视为写入命令,因为它们可能尝试将命令传播到副本和 AOF 文件。

除了所有只读脚本提供的优势外,只读脚本命令还具有以下优势

  • 它们可以用于配置一个 ACL 用户,使其只能执行只读脚本。
  • 许多客户端还更好地支持将只读脚本命令路由到副本,适用于希望使用副本进行读扩展的应用。

只读脚本历史

只读脚本和只读脚本命令在 Redis 7.0 中引入

  • 在 Redis 7.0.1 之前,PUBLISHSPUBLISHPFCOUNT 在脚本中不被视为写入命令
  • 在 Redis 7.0.1 之前,no-writes标志不隐含allow-oom
  • 在 Redis 7.0.1 之前,no-writes 标志不允许脚本在写入暂停期间运行。

推荐的方法是使用带有no-writes标志的标准脚本命令,除非您需要前面提到的其中一项功能。

沙箱脚本上下文

Redis 将执行用户脚本的引擎放置在沙箱中。沙箱旨在防止意外误用并减少来自服务器环境的潜在威胁。

脚本不应尝试访问 Redis 服务器底层的宿主系统,例如文件系统、网络,或尝试执行 API 支持之外的任何其他系统调用。

脚本应仅对存储在 Redis 中的数据以及作为执行参数提供的数据进行操作。

最大执行时间

脚本受到最大执行时间的限制(默认为五秒)。这个默认超时时间相当长,因为脚本通常在不到一毫秒内即可运行。设置此限制是为了处理开发过程中意外创建的无限循环。

可以通过redis.conf或使用CONFIG SET命令修改脚本的最大执行时间,精度可达毫秒。影响最大执行时间的配置参数称为busy-reply-threshold

当脚本达到超时阈值时,Redis 不会自动终止它。这样做会违反 Redis 与脚本引擎之间确保脚本原子性的约定。中断脚本的执行可能会导致数据集留下半写状态的更改。

因此,当脚本执行时间超过配置的超时时间时,会发生以下情况

  • Redis 会记录日志,指示某个脚本运行时间过长。
  • 它开始重新接受来自其他客户端的命令,但对所有发送正常命令的客户端回复 BUSY 错误。在此状态下,唯一允许的命令是SCRIPT KILLFUNCTION KILL以及SHUTDOWN NOSAVE
  • 可以使用SCRIPT KILLFUNCTION KILL命令终止只执行只读命令的脚本。由于脚本尚未写入任何数据到数据集,这些命令不会违反脚本的语义。
  • 如果脚本已经执行了哪怕一个写入操作,唯一允许的命令是SHUTDOWN NOSAVE,该命令会在不将当前数据集保存到磁盘的情况下停止服务器(基本上,服务器被中止)。
评价本页
返回顶部 ↑