Redis 可编程性
使用Lua和Redis函数扩展Redis
Redis 提供了一个编程接口,允许你在服务器本身上执行自定义脚本。在 Redis 7 及以上版本中,你可以使用 Redis Functions 来管理和运行你的脚本。在 Redis 6.2 及以下版本中,你可以使用 Lua scripting with the EVAL command 来对服务器进行编程。
背景
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文件。
除了所有只读脚本提供的好处外,只读脚本命令还具有以下优势:
- 它们可以用于配置ACL用户,使其只能执行只读脚本。
- 许多客户端还更好地支持将只读脚本命令路由到副本,适用于希望使用副本来扩展读取的应用程序。
只读脚本历史
只读脚本和只读脚本命令在 Redis 7.0 中引入
- 在 Redis 7.0.1 之前,
PUBLISH
、SPUBLISH
和PFCOUNT
在脚本中不被视为写命令 - 在 Redis 7.0.1 之前,
no-writes
flag 并不包含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 KILL
、FUNCTION KILL
和SHUTDOWN NOSAVE
。 - 可以使用
SCRIPT KILL
和FUNCTION KILL
命令终止仅执行只读命令的脚本。这些命令不会违反脚本语义,因为脚本尚未向数据集写入任何数据。 - 如果脚本已经执行了至少一次写操作,唯一允许的命令是
SHUTDOWN NOSAVE
,该命令停止服务器而不将当前数据集保存到磁盘(基本上,服务器被中止)。