An update about Redis developments in 2019

Salvatore Sanfilippo

2019 年 Redis 开发进展更新

原文由 Salvatore Sanfilippo 发布,订阅该博客

昨天,一位关注 Redis 的用户在 Hacker News 上写下了这样一段话:

https://news.ycombinator.com/item?id=19204436

我很喜欢 Redis,但对目前正在开发的一些改动持保留态度。RESP3 协议有一些听起来很酷的功能,但也可能会让客户端库的代码变得复杂得多。另外还有大量工作投入到了细粒度的 ACL 上。我想不通为什么这会是必要的,甚至比多线程支持、更好的持久化模型、数据类型等其他改动优先级更高。

— 用户评论结束 —

我感觉他/她(不确定)并非唯一一个把 ACL 看作是因 Redis Labs 的目标而被强加上去的功能的人,理由无非是“为了企业用户”之类。评论中的其他观点也很有意思,我认为非常值得逐一回应,以便向 Redis 社区更清晰地传达未来的方向。

为了便于说明,我会把这篇博文按原评论中提到的各个功能分节来写。

RESP3

RESP3 的目标,正如我之前在这个博客里写过的那样,其实是简化客户端的生态。理想情况下,每个客户端都会有一个底层,不再试图去重新发明某种高层接口:redis.call(“get”,”foo”)。不再需要费心去协调各种类型转换,因为现在的协议在语义上已经足够丰富,能够直接告诉客户端某个回复在调用者手里应该呈现为什么样子,对于绝大多数命令也不再需要预先知道命令的特征。我想这位用户所指的,应该是 RESP3 对带外通信的支持,也就是回复中的“属性(attributes)”。

我坚信,“客户端缓存”在 Redis 的未来会非常重要。这是任何可扩展系统的必然一步。然而,如果没有服务端的协助,客户端缓存的失效会是一场噩梦。这正是 RESP3 主要要在回复中支持属性的原因。不过,Redis 6 很可能*不会实现其中的任何一项*。即将成为 Redis 6 的不稳定版(unstable)中已经有了一套几乎完整的 RESP3 实现,但里面并没有属性。实现 RESP3 的客户端如果想真正做到面向未来,完全可以选择直接丢弃属性,而且即便在未来的 Redis 版本中,如果用户没有开启某种特殊功能,属性很可能根本就不会被发送。例如,要使用客户端缓存,连接必须被置于某种特殊模式。此外,如你所知,Redis 6 将完全向后兼容 RESP2。实际上,我甚至开始觉得 RESP2 的支持可能永远都不会被移除,因为保留它的成本几乎为零,而既然我们已经花力气在 RESP2 和 RESP3 之间做了抽象层,就没有理由再去打破向后兼容性。

通常我不喜欢无缘无故地改动东西,但 RESP2 的局限性已经对客户端生态产生了很大影响。我希望的客户端生态是,用户在不同客户端之间切换时都能感到熟悉,API 就是 Redis 自身的 API,而不是客户端作者另起炉灶的那一层。顺便说一下,我并不反对在底层之上再提供一个*更高层*的 API,但应该有一个共同的基础,而且客户端应该能够在对命令一无所知的情况下发送命令。

ACL

ACL 的规范是我本人在四年前起草的。我等了这么久才决定实现它,是为了确信现在确实是合适的时机:这么多年来我们一直没有 ACL,只是靠一些技巧,主要是重命名命令,倒也一路走了过来。不过,请不要以为 ACL 的主要动机是为了满足企业客户的安全需求。ACL 确实也会带来用户认证等安全方面的副作用,但这个功能的主要目标是*运维*。

举个例子。假设你有一个 Redis 实例,打算用它来做一件新事:处理延迟任务。你从网上找来一个库,看起来运行得不错。可这个库——你并没有逐行审查过——凭什么就能调用“FLUSHALL”并瞬间清空你的整个数据库?也许这个库的测试代码里就包含了这样的命令,等你发现时已经太晚了。又或者,你刚招来一位初级开发者,总是在 Redis 实例上执行“KEYS *”,而你们公司的 Redis 使用规范明明是“禁止使用 KEYS 命令”。

再比如云服务商的场景:他们需要小心地重命名管理类命令,甚至要想办法防止这些命令因某种原因被泄露出去。又要耍各种花招:比如让 MONITOR 的输出里不显示这些命令。有了 ACL,你就可以这样配置 Redis:让默认用户在未经认证的情况下,无法执行任何具有管理性质或危险性的操作。我认为这对运维来说会是巨大的改进。

而且,据我所知,ACL 是我为 Redis 写过的最好的代码之一。几乎没有任何 CPU 开销,除非你使用了 key 模式匹配,即便如此开销也很小。实现完全自包含在 acl.c 这一个文件里,核心其余部分只有寥寥几处对 ACL API 的调用。由于完全模块化,它没有给系统增加任何复杂性。实际上,ACL 的代码还让我们得以对 AUTH 命令周边做了不错的重构。

多线程

Redis 可能获得的多线程支持有两种。我想用户所指的应该是类似 memcached 的多线程,也就是让单个 Redis 实例能够扩展到多个线程,以提升 GET、SET 等简单命令的每秒操作数。这需要把 I/O、命令解析等都做成多线程的。我们就把这种模式叫做“I/O 多线程”。

另一种多线程方案则是,让耗时较长的命令在另一个线程中执行,从而不会阻塞其他客户端。我们把这种线程模型称为“慢命令多线程”。

好吧,计划是这样的:据我所知,I/O 多线程不会在 Redis 中实现,因为经过深思熟虑,我认为那会在没有充分理由的情况下引入大量复杂性。实际上,很多 Redis 部署的瓶颈在于网络或内存。此外,我非常信奉无共享(share-nothing)架构,所以我想要的扩展 Redis 的方式,是改进对在同一台主机上运行多个 Redis 实例的支持,尤其是通过 Redis Cluster。2019 年在这方面会有两件事:

A) Redis Cluster 的多个实例将能够协同工作,更合理地利用本地实例的磁盘,也就是说,避免同时进行 AOF 重写。

B) 我们将作为 Redis 项目的一部分发布一个 Redis Cluster 代理,让用户无需在客户端侧实现一套完善的 Cluster 协议,就能对集群进行抽象访问。

还有一点需要注意,Redis 不是 Memcached,但和 memcached 一样,它是一个内存系统。把像 memcached 这样数据模型非常简单的内存系统做成多线程,是很有意义的。一个基于磁盘的多线程存储则是必需的。而一个复杂的多线程内存系统则处在中间地带,事情会变得棘手:Redis 的客户端并不是相互隔离的,数据结构也很复杂。一个执行 LPUSH 的线程需要与其他执行 LPOP 的线程协作。收益更少,却要增加大量复杂性。

相反,我*真正非常想要*的是慢操作的多线程,而借助 Redis 模块系统,我们已经走在了正确的方向上。不过未来(不确定是在 Redis 6 还是 7)我们会在模块系统中引入 key 级别的锁,让线程可以完全取得某个 key 的控制权来处理耗时操作。现在,模块已经可以以完全分离的方式为客户端实现命令并生成回复,但在访问共享数据集时仍需要一把全局锁:这一点将会被去掉。

更好的持久化

最近我们为改进 Redis 这类基础功能做了多次努力。最近实现的一项很棒的改进是 AOF 文件中的 RDB 前置内容。另外,在 Redis 4 和 5 中,复制方面也做了大量工作,现在的水平已经与过去完全不可同日而语。是的,继续改进这些部分仍然是我主要的关注点之一。

数据结构

从 Redis 5 开始,Redis 有了 Streams。对于 Redis 6 和 7,计划首先是通过改变某些实现的内部结构,让现有的数据结构在内存使用上更加高效。然而,要添加新的数据结构则需要考虑很多因素。我花了好几年才想明白,如何用 streams 在时间序列和流处理的场景下,填补 lists、pub/sub 和 sorted sets 之间的空白。我真心希望 Redis 是一组正交的数据结构,让用户可以自行组合,而不是一组开箱即用的*工具*。Streams 是一个抽象的日志,所以我认为这是一个非常值得的补充。不过,对于其他东西,如果没有经过非常长期的考量,我还不确定是否值得放进核心。无论如何,近年来在添加新数据结构方面的投入确实更多了。HyperLogLog、更强大的位操作、streams、阻塞式有序集合操作(ZPOP* 和 BZPOP*)都是很好的例子。

结论

我认为 Redis 社区应该了解为什么要做某件事、为什么又要推迟另一件事。我常犯的一个错误是通过 Twitter 来沟通,好像人人都在上面似的,但很多人其实是有现实生活的 :-D,根本不关心。博客是告知社区的更好方式,我需要多花时间来写博客。顺便说,我本身就喜欢写文章,所以这是双赢。需要认识到的一件重要事情是,Redis 并没有一个牢固的路线图,多年来我发现,机会驱动的开发比起固定的路线图要有效得多。有需求吗?我看到了必要性吗?我有心情去写代码吗?现在是合适的时机吗,因为没有其他更紧迫的大事?有一群用户在帮助设计过程,提供提示、想法、测试吗?时机到了,那就动手做。给 Redis 定一个牢固的路线图是很不明智的,因为开源核心团队规模很小,有时我会因为某个随机的崩溃而卡上好几周……任何固定的长期计划都行不通。而且,随着 Redis 社区给出反馈,我的想法也会发生很大变化,所以我每个月都得重写路线图。不过,写博客至少是一个好办法,能展示当前优先级/想法的版本,也能说明为什么某些想法被放弃了。

最后说明一点:我在 Redis Labs 对于要在开源项目这一侧加入什么内容,拥有几乎无限的自由度。我觉得这在行业内算得上是一种奇迹,或者说,我在 Redis Labs 共事的人都是通情达理的好人,他们明白我们所做的一切都源于开源运动,保持这种方式继续下去是明智的。但这并不常见。如果我在 Redis 的路线图上犯了错,那一定是我的错。

本文章由 muse-spark-1.2-contributor 进行翻译

评论