The first release candidate of Redis 4.0 is out

Salvatore Sanfilippo

Redis 4.0 的第一个候选发布版来了

它还不稳定,但很快就会稳定下来,而且带来了一长串能让 Redis 对我们用户更有用的功能:Redis 4.0 Release Candidate 1 终于来了,并且大胆地称自己为 4.0,而不是 3.4。对我来说,semantic versioning(语义化版本控制)不算什么;我更喜欢的是通过版本号和版本跳跃来传达新版本的情况,而在这个具体案例中,4.0 的意思就是“这玩意儿牛逼了”。

问题在于,Redis 4.0 有很多东西,早就该加入 Redis 了,只是在另一个世界里,一个开发者可以像 Ken The Warrior(拳王健)一样把自己复制成十个人,然后开始写代码。但不管我多么努力地学习新的 vim 快捷键,“复制自己”这件事还是不在我的指法范围内。

不过,4.0 终于把很多这类事情做完了……下面就是其中重要功能的列表,并附上一些细节和进一步了解它们的链接。

1. 模块

你可能已经知道,Redis 4.0 加入了模块系统,而且它允许实现相当花哨的功能,比如实现可通过 RDB/AOF 持久化的新数据类型、创建非阻塞命令等等。关键在于,所有这些都是通过一个更高层次的抽象 API 完成的,并且与核心完全分离,因此你编写的模块可以在 Redis 的新版本中继续工作。利用模块,我写了 Neural Redis,这是一个可以直接在 Redis 内部训练的神经网络数据类型;还有很多人在做非常有趣的事情:新的限流命令(用 Rust 实现!)、构建在 Redis 之上的图数据库、二级索引、时间序列模块、全文索引,以及其他许多东西;我的感觉是,这还只是开始。

这不仅让 Redis 能够不断扩展、覆盖新的领域,同时又让核心部分即使不能说保持最小化,至少也只包含大多数用户有用的东西,以及许多人都需要的相当通用的功能。它还有可能避免许多任务中重写网络服务器的问题,即使你的目标是创建与 Redis、数据库、缓存或 Redis 所代表的任何东西都无关的产品。也就是说,你可以直接写一个模块来使用 Redis 的“基础设施”:它的协议、别人已经编写好的客户端,等等。所以我对此感觉很好:核心没有压力,而想做更疯狂事情的用户则拥有自由。

2. 复制协议 v2

所以,从运维的角度来看,它在生产环境中会非常有用。过去某个时候,我们引入了被称为“PSYNC”的功能。这是一种新的主从协议,允许主节点和从节点在两者之间的连接中断后,从中断处继续进行。在那之前,主从之间的复制链路每中断一次,就会进行一次完整同步:在主节点生成 RDB 文件,把它传输过去,在从节点加载它;好了,你知道这套流程是怎么回事。因此,PSYNC 算得上是一次真正的改进。但还不够……

发生故障转移时,PSYNC 还不够好。如果一个从节点被提升为主节点,那么此前与旧主节点进行复制的从节点就无法连接到这个新提升的从节点,并与之执行 PSYNC:必须进行完整重新同步。这不太理想,对 Redis Cluster 来说也一样。不过,修复这一点需要修改复制协议,因为我确实希望确保在任何可能的拓扑变化之后,只要各实例之间存在共同的复制历史,就能进行部分重新同步。

因此需要进行的第一项修改涉及“链式复制”的工作方式,也就是从节点的从节点的从节点……它们是怎么工作的?比如,A 是主节点,结构如下:

A —> B —> C —> D

所以 A 是 B 的主节点,而 B 是 C 的主节点,以此类推。在 Redis 4.0 之前,情况是 B 从 A 接收复制协议。复制协议通常是一串写命令。B 作为 C 的主节点,只是在内部执行与 A 相同的操作:每次写入时,它都会重新生成一份合适的复制协议并传给 C,依此类推。

现在,B 会把从 A 收到的内容原封不动地代理给 C,C 也会对 D 做同样的事情:由于所有下级从节点现在接收到的是完全相同的字节流,它们可以为某段历史“打标签”,并利用这个标签和偏移量,在彼此存在共同内容时始终尝试从中断处继续。

主节点自身在转换为从节点后,现在也能够与新的主节点执行 PSYNC。即使在一次“干净”的重启之后,从节点通常也能与主节点执行 PSYNC,因为 RDB 文件中现在保存了复制标签和偏移量。

为了让它良好运行,具体细节比这复杂得多;但这里的关键是:如果可能,就别再被完整重新同步烦扰。显然,PSYNC v2 做得不错。请试用它,如果你对这个功能感兴趣,也请告诉我你的反馈。

3. 缓存淘汰改进

几个月前我已经为此写过一篇完整的文章:http://antirez.com/news/109。所以这里就只给出 TLDR(太长不看)版本。我们现在有 LFU(Least Frequently Used,最不经常使用),而其他所有策略也都改用了更稳健、更快速、更精确的实现。因此,对于缓存使用场景来说,这是个重大消息。如果你关心这些内容,请阅读那篇完整文章,里面有大量信息。

4. 非阻塞的 DEL 和 FLUSHALL/FLUSHDB

代码名称是“对象的惰性释放”,但这个名字很没劲,功能却很漂亮。现在有了一个名为 UNLINK 的新命令,它只会删除数据库中的键引用,并在一个独立线程中执行实际的内存释放,因此,如果你对一个巨大的键使用 UNLINK 而不是 DEL,服务器就不会阻塞。更棒的是,借助 FLUSHALL 和 FLUSHDB 的 ASYNC 选项,你还可以对整个数据库,或实例中的全部数据执行同样的操作。结合能够交换两个 Redis 数据库内容的新命令 SWAPDB,FLUSHDB ASYNC 会非常有意思。比如,你可以先用新版本的数据填充 DB 1,然后执行 SWAPDB 0 1,再对包含旧数据的数据库执行 FLUSHDB ASYNC,之后创建更新的版本并重复这一过程。现在之所以能够做到这一点,只是因为清空整个数据库不再会阻塞。

UNLINK 没有成为 DEL 的默认行为是有原因的。我知道一些事情……但我不能说(**)。

5. 混合 RDB-AOF 持久化格式

现在,如果启用该选项,AOF 重写会在 AOF 文件前面加上一个 RDB 文件;RDB 文件生成和加载起来都更快。这在某些环境中会非常有用,但也会让 AOF 文件变得不那么透明,所以目前它还是一个可选功能。这个功能已经被讨论了很多年,现在终于“进来了”。

6. 新的 MEMORY 命令

我很喜欢它,就像我喜欢 LATENCY DOCTOR 一样;LATENCY DOCTOR 推出后,邮件列表中“我的 Redis 很慢”这类抱怨的比例就降到了很小的一部分。现在,内存问题也有对应的工具了。

127.0.0.1:6379> MEMORY DOCTOR
Hi Sam, this instance is empty or is using very little memory, my issues detector can't be used in these conditions. Please, leave for your mission on Earth and fill it with some data. The new Sam and I will be back to our programming as soon as I finished rebooting.

电影版权方可能会因为我从科幻对白中获取灵感而起诉我,不过没关系。等我进了监狱,记得给我带几个橙子。

MEMORY 的功能远不止这些。

127.0.0.1:6379> MEMORY HELP
1) "MEMORY USAGE <key> [SAMPLES <count>] - Estimate memory usage of key"
2) "MEMORY STATS                         - Show memory usage details"
3) "MEMORY PURGE                         - Ask the allocator to release memory"
4) "MEMORY MALLOC-STATS                  - Show allocator internal stats"

USAGE 子命令提供的内存使用报告会非常有用,“STATS”提供的深入信息也同样如此。

目前这一切完全没有文档,所以尽情自己搞清楚它到底是干什么的吧。

7. Redis Cluster 现在兼容 NAT / Docker

但这其实也是个坏消息,因为节点用于通信的“Cluster bus”二进制协议发生了变化,所以要升级到 4.0,你需要让 Redis Cluster 进行一次大规模重启。很抱歉,我被这些 NAT / Docker 修复哄骗了,请原谅我。我也试过让它向后兼容,但无论采用简单还是不那么简单的方式,都无法在不做出极其别扭的处理的情况下实现。

该功能在示例 redis.conf 文件中有说明。你应该能看出来,相比其他功能,我对这个功能没那么兴奋,对吧?

嗯……我想就是这样了,这些是主要功能。如果你还想阅读发行说明,可以看这里:https://raw.githubusercontent.com/antirez/redis/4.0/00-RELEASENOTES

至于何时稳定下来,和往常一样,目前还不知道:我计划大约每 2 到 4 周发布一个新的 RC。等 bug 的严重程度和报告频率都显著降低后,就会进入 Redis 4.0-final 的发布阶段。不过,还有大量文档需要更新,所以我会有很多事情要做。

非常感谢所有为这次发布做出贡献的人:很多人都以重要的方式参与其中。上面的发行说明中列出了所有提交,你可以浏览它们,看看提交者的名字。

感谢 Redis 社区和 Redis Labs,是你们让这一切成为可能;但尤其要感谢所有使用 Redis、并在日常问题中恰当地应用 Redis 来把事情搞定的开发者,因为这才是全部意义所在——当然,写代码时能获得乐趣也很重要。

附言:获取新代码的最快方式,是从 Github 获取 '4.0' 分支。仓库和往常一样是 antirez/redis。

** 关于 UNLINK 为什么没有成为 DEL 的默认行为,请查看 https://news.ycombinator.com/item?id=13091370,了解更多信息。