dot Redis 8 发布了——并且是开源的

了解更多

Redis 7.0:多方面的演进

Redis 7.0 的发布正在按计划进行,我们刚刚发布了它的第二个候选版本。 此候选版本是一个计划中的里程碑,旨在完成该版本的功能,但它也是我们展示新版本中其他内容的机会。 例如,Redis Functions 已从 Redis 自 2.6 版本以来对脚本的现有支持演变而来。 同样,Redis 的更多功能也得到了发展。

在自然界中,进化显然是随机发生的,自然选择负责对结果进行分类。 一般来说,在一般的软件中,特别是 Redis 中,这个过程是向后发生的:我们选择期望的路径并相应地发展项目以遵循它。 我们的路线图的规划和执行主要受到用户的反馈以及 Redis 适用情况的新用例的指导,而不是让随机性决定未来。

在 Redis 7.0 中,访问控制列表 (ACL) 也迈上了进化的阶梯。 ACL 在 Redis 6.0 中引入,通过添加管理用户及其权限的机制,扭转了长期以来将安全性视为项目范围之外的观点。 然而,我们的社区很快就告诉我们,虽然这是朝着正确方向迈出的一步,但该功能仍然缺乏必要的功能。

ACL 中的一个差距已经在 Redis 6.2 中得到了解决,即控制 Pub/Sub 通道名称的允许模式。 但这只是一个临时的权宜之计,我们花了更长的时间才想出一个简单有效的方法来解决剩下的问题。

ACL 的原始设计只为基本的权限控制用例提供了配置。 它只能授予或拒绝访问每个用户的单个命令集、键和通道名称模式。 例如,无法将 `SET` 命令 限制为键的一个子集,同时允许给定用户的 `GET` 访问另一个键子集。 实际上,ACL 并不是实现安全策略的有效机制。

Redis 7.0 的访问控制列表(简称 ACLv2)与原始版本兼容,但增加了两个重要的改进。 首先,ACLv2 完全是关于选择器。 其次,ACLv2 允许将访问类型权限设置为特定键。 这种能力使得将用户专门限制为对键子集的只读、只写或读写操作成为可能。

原始的 ACL 设计仅为每个用户提供一个选择器——默认选择器。 该选择器描述了用户可以访问的键和通道、类别和命令。 ACLv2 允许在默认值之上添加任意数量的选择器,并按顺序应用。 这种方法满足了更严格的安全策略的要求,并使 ACL 更接近完整。

服务器的自省能力是 Redis 在 7.0 版本中显着进步的另一方面。 Redis 通过其命令字典公开了一个 API,通过该 API 进行交互。 随着项目的演变,不同命令(及其子命令)的数量不断增加,在 7.0 版本中超过 380 个。 因为每个 Redis 命令都专门用于给定的任务,所以记录每个命令的调用参数和行为是该项目的核心原则。 此文档是服务器与其客户端之间唯一的约定。

从历史上看,命令的文档记录在项目外部的一个单独的代码存储库中。 我们将所有文档都保持为(大部分)人类可读的格式,因为它的目的是供人们阅读。 这对机器(或者更确切地说,是编写机器程序的人)提出了挑战,因为将散文翻译成代码既混乱又脆弱。 具体来说,Redis 客户端的作者只能通过监控文档的更改和阅读发行说明来希望保持他们的项目更新。

因此,到 2.8 版本时,我们意识到我们还需要一种编程方式,允许服务器报告其命令。 恰如其名(但也很拗口)的 `COMMAND` 命令在运行时列出服务器支持的命令。 此外,`COMMAND GETKEYS` 子命令允许客户端发送原文命令及其参数,以使服务器从中提取任何键名。 从命令中提取键名是必需的,因此客户端可以在集群部署中正确地定向操作。

部分受到 ACLv2 相关工作的驱动,但也为了使运行时命令列表对客户端更有用,7.0 版本彻底修改了服务器命令管理的许多内部机制。 此外,我们丰富了服务器保留的关于每个命令的元数据,使得构建复杂的客户端成为可能,这些客户端(几乎)没有关于服务器功能的先验知识。 最后,修改后的命令表以这样一种方式构建,即 Redis 模块可以使用它们各自的命令对其进行扩展,以提供与核心命令相同级别的自省。

新的 命令键规范 允许客户端从原文命令中本地提取键,而无需集群服务器的参与,从而提高延迟并减少网络带宽。 关于 命令参数 的元数据让客户端可以发现并适应服务器版本之间命令语法的变化。 客户端可以从 命令提示 中获得关于在特殊情况下和不同部署类型下执行命令的更多信息。

这项工作还包括将子命令提升为服务器命令表的一等公民。 最初,子命令被引入 Redis 是为了对抗 API 不断增长的基数。 原因是与其为每个任务添加一个新命令,不如通过调用一个“父”命令来调用相关任务。 “父”命令接受子命令的名称作为其第一个参数,该参数反过来决定执行的操作。 从技术角度来看,子命令继承了其父命令的所有特征(例如,ACL 类别、读/写标志、键规范等等),因此无法在其不同的行为之间进行细粒度的区分。

例如,`CLIENT` 命令是连接管理任务的万能篮子,拥有不少于 15 个不同的子命令。 它的一些子命令(如 `CLIENT SETNAME`)通常由正常的客户端连接调用,而另一些子命令(如 `CLIENT KILL`)则有可能被滥用,因此应仅限于管理员使用。 早期版本的 Redis 缺乏支持进行这种区分的内部机制,从而引导开发人员阅读文档并造成混淆。 然而,在 Redis 7.0 中,每个子命令都有自己的一组特征,无论其父命令或同级命令如何,都可以对其进行准确描述。

这篇文章已经变成了相当长的文本墙,也许在其结尾处过于技术细节(如果存在过多的技术细节)。 我们希望它不会太无聊,并且我们已经阐明了新版本及其在项目中的作用。 我们正在努力发布最终候选版本,以实现该版本的全面可用性,但请继续关注我们系列中的下一篇文章,它将继续新版本的巡回展示。