版本号永远停在 7.2.4,Valkey 两年半重写了 Redis 的半个底座

发布于 · 3,174 字 · 约 8 分钟#Github 解读#DevOps原文链接
版本号永远停在 7.2.4,Valkey 两年半重写了 Redis 的半个底座 封面图
  • Valkey 将 redis_version 冻结在 7.2.4 作为兼容行为快照,对外承诺 drop-in 替换,对内放手重构底座
  • 过期结构、哈希表与有序集合、槽位迁移三处核心重写:Volatile Set 时间桶支持字段级过期,rehash 增量页释放消毛刺,整槽原子迁移支撑 2000 节点集群
  • 9.1 引入 Lua 模块化、CLUSTERSCAN 跨节点扫键、Space-Saving 算法热点键追踪,性能上 pipeline 预取加 40%、SIMD BITCOUNT 最高快 200%
  • 缺失向量检索能力,Active/Active 双活、io_uring 等需求仍在排队,与 Redis 8.x 形成选型分水岭
  • 社区近半 commit 来自 AWS 工程师,中立基金会外衣下仍是厂商主导,bus factor 风险值得纳入选型考量

大家好,我是若风。

先看一个有点反直觉的东西。找台装了 Valkey 的机器,连上去敲一句 INFO,redis_version 一栏写的是 7.2.4。

这不是装了老版本,这是 2026 年 9 月刚发的 9.1.2。而且不管你怎么配置,这一栏永远是 7.2.4,src/server.c 第 6262 行写死的不带任何条件。

一个主版本号都跑到 9 的项目,为什么对外报版本时死咬着 7.2.4 不放。答案在 src/version.h 里,那行注释写得直白,Redis OSS compatibility version, should never exceed 7.2.x。兼容版本号永远不许超过 7.2.x,这是写进源码的誓言。

把这个决策看懂,你才能看懂 Valkey 这两年半在干什么。

48 小时里的分家

时间拨回 2024 年 3 月 20 日,Redis Ltd. 宣布放弃 BSD 协议,转向 RSALv2 和 SSPL 双许可,云厂商想继续提供托管 Redis,要么谈商业授权,要么自己动手。两天后的 3 月 22 日,一个 fork 自 Redis 7.2.4(最后一个 BSD 版本)的仓库悄悄建了起来,后来交给 Linux Foundation 托管,改名 Valkey。

站队速度快得惊人。AWS、Google Cloud、Oracle、Snap、Ericsson 相继公开背书,ElastiCache 直接把默认引擎换成了它。到今天我抓数据这会儿,27121 个 star,1306 个 fork,协议还是原来的 BSD-3-Clause。

你想想看,一个数据库项目,出生的第一天就带着数百家公司的生产流量。它没有试错期,第一天就得是 Redis。

所以 Valkey 给自己的定位从头到尾只有一句话,drop-in replacement,直接替换。make install 装完之后,安装目录里会顺手建一组 redis-server、redis-cli 的软链接,指回 valkey 的二进制。你现有的一整套运维脚本、监控探针、客户端库,不用改一行代码。

而版本号冻结就是这套策略最深的一颗钉子。大量客户端库和周边工具会拿版本号做能力判断,见到 8.0、9.0 就启用新语法,见到 7.x 走老路径。Valkey 如果跟着 Redis 的版本号跑,就得追着别人的 release 节奏模拟别人的行为。把版本冻在 7.2.4,等于宣布我只承诺 7.2.4 这个行为快照,多的一概不装。

源码里这条线拉得很清晰。INFO 里的 redis_version 是无条件报 7.2.4,因为几乎所有监控工具都解析这个字段。再往深一层,src/config.c 里还有个 extended-redis-compatibility 开关,默认关闭。src/networking.c 第 5967 行,HELLO 回复版本号的那行代码就一个三元表达式,开关关着就报 valkey 自己的真版本,开着就报 7.2.4,连报错文案都跟着切换,src/server.c 里为 loading 错误、慢脚本错误这些提示各准备了两套措辞。需要骗过某些只认 Redis 的老工具时,一条 CONFIG SET 就能切换。

坦白讲,这是我能想到的对 fork 项目最清醒的身份设计。对外永远扮演 7.2.4,对内放开手脚干自己的。

下面才是这篇文章真正想拆的部分。兼容层冻住了,那底座呢?

底座上的三台手术

答案是动得比谁都狠。翻 unstable 分支的源码树,你会发现 Redis 还是那副老骨架,里面好几处器官已经换过了。

第一台,过期结构

Redis 的键过期有个老缺陷,粒度是整个键。一个 hash 里塞了十万个字段,想做「每个字段独立过期」,原生做不到,只能业务侧起定时任务扫。Valkey 9.0 把这事解决了,一口气加了 HEXPIRE、HSETEX、HGETEX、HTTL 等十一个命令。

真正有意思的是底下的实现。支撑这功能的是 src/vset.c,一个叫 Volatile Set 的自适应结构,光是这个结构的头注释就写了上百行。

它把「什么时候过期」组织成时间桶。桶的粒度不是固定的,最小 16 毫秒,最大 8192 毫秒,靠把过期时间戳向上对齐到 2 的幂次窗口来分桶。临近的过期事件挤在细粒度桶里,远期的自动归到粗粒度桶,扫描时从最近的桶开始捞,天然就有优先级。

更巧的是容器形态。整个结构就一个指针,指针最低 3 位被打上 tag,区分四种状态,指向单个条目的 SINGLE、指向有序数组的 VECTOR、指向哈希表的 HT、指向基数树的 RAX。一个字段过期时就是 SINGLE,两个字段升成 VECTOR,超过 127 个条目升成 RAX。小的场景零开销,大的场景扛得住,中间态全自动流转。

Volatile Set 容器晋升与时间桶
Volatile Set 容器晋升与时间桶

一张图看两层设计。上排是容器怎么随规模升级,下排是时间桶怎么按远近分粒度,扫描那支深蓝箭头永远从最近的桶下手。

复制逻辑也跟着变了。src/t_hash.c 里,HINCRBY 这类命令在主从复制时会改写成 HSETEX 带 PXAT 绝对时间戳的形式,为的是字段上设置的过期时间在 replica 那边不丢。这种细节,没真踩过坑的人写不出来。

第二台,哈希表与有序集合

Redis 7.2 的 dict 哈希表在 rehash 时会有延迟毛刺,桶数组扩容那一下,页分配的抖动直接反映到 P99。Valkey 重写了 src/hashtable.c,9.1 又叠加了增量页释放,把 rehash 期间释放的内存拆成小步走,专门治这个尖刺。

有序集合那边更激进。skiplist 节点原本和数据元素分离,9.1 直接把元素嵌进 skiplist 节点里,省一层指针解引用,skiplist 头部也做了内嵌优化。两个 PR 加起来,ZADD 那条热路径少了不少内存往返。

然后是我最喜欢的一处「诚实现场」。unstable 分支里躺着 src/fbtree.c,一个用 SIMD 加速的 cache-optimized B+tree,头注释说 zset 相关的适配逻辑在 zset_fbtree_adapter.c。我去翻了文件树。

这个文件不存在。t_zset.c 里对 fbtree 的引用次数是零。

也就是说 B+tree 的地基已经打进了主线,但 zset 搬家还在施工。你要问我怎么看,我反倒觉得这是好事,大重构切成小步走,每一步主分支都是可发布状态。但也得承认,看 unstable 源码时你得多个心眼,注释描述的架构和实际落地的架构之间存在时间差。

第三台,槽位迁移

集群扩缩容是 Redis 老痛点。逐键迁移的年代,一个百万成员的大集合能把迁移卡死,客户端还得配合重试。Valkey 9.0 改成整槽原子迁移,传输层直接用 AOF 格式流式发送,大集合按元素逐条传,不再要求目标节点一口气吞下整个键。官方博客给的数字是支持扩到 2000 节点,聚合吞吐超过 10 亿请求每秒。

对运维同学,这个特性的分量可能比前面所有加起来都重。

Valkey 源码分层架构
Valkey 源码分层架构

上面这张图是 unstable 分支 src 目录的真实分层。server.c、cluster.c、t_hash.c 这些老文件一个不少,右边那排 vset、fbtree、space_saving、cluster_migrateslots 全是 fork 之后长出来的新器官。我给这套打法起了个名字,叫「行为冻结,器官替换」,目录结构就是这八个字的证据。

9.1 里几个值得单独拎出来的细节

版本节奏先看一眼。8 月 31 日到 9 月 1 日这两天,8.0.11、8.1.10、9.0.6、9.1.2 排着队发布。往前翻一个多月,7 月 21 日那天一口气发了五个版本,从 7.2.14 一路发到 9.1.1。五条分支同时在维护,backport 的纪律性比很多商业公司都强。

具体功能里我挑三个。

Lua 引擎拆成了模块。原来内嵌在 server 里的脚本引擎,9.1 起挪进独立的 Valkey module,为后面接更多脚本引擎铺路。这个方向跟「去耦合」的大趋势是同频的。

CLUSTERSCAN 命令。跨节点扫键一直是客户端自己拼 MOVE 重定向的逻辑,现在服务端原生支持,配合可配置的 DB hash seed,SCAN 在不同节点上能拿到一致的 key 分布。

热点键追踪的底层数据结构是 src/space_saving.c,实现的是 2005 年那篇经典论文的 Space-Saving 算法,O(K) 内存近似追踪 top-K 频率,双窗口设计,一个 live 窗口累积,一个 frozen 窗口给查询,读的人永远看不到半截数据。算法是老的,工程化是新的。

性能账单也摆一下,都来自 9.0 官方博客。pipeline 内存预取吞吐最高加 40%,零拷贝回复大请求加 20%,Multipath TCP 延迟降 25%,SIMD 版 BITCOUNT 和 HyperLogLog 最高快 200%。

数字都很漂亮。但拆项目不能只看发布会,得看它没什么。

它没做什么

第一个缺口,vector set。

Redis 8.0 在 2025 年就把向量检索做进了内核,VADD、VSIM 直接用。AI 时代越来越多的场景是「缓存旁边还得挂个向量库」,这块需求是真实的。我在 Valkey 的源码树里搜了 vectorset 相关的文件和命令。

零个。

9.1 的 release notes 里也没提。如果你要做 AI 应用的存储选型,这是当下两边最实的分水岭。Valkey 队伍里不可能没人看到这个需求,大概率是在排队。issue 区里挂着 80 条评论的 Active/Active 双活复制请求(#512,2024 年开的)、46 条评论的 io_uring 批量写(#112)、forkless save(#4460),全都还开着。需求排队很长,做核心稳定性的手就没法松,这是 fork 项目现实的节奏。

第二个要说的事实,社区结构。

近三个月 unstable 分支 215 个 commit,头五个人贡献了 99 个,占了 46%。榜首 enjoy-binbin 一个人 53 个,第二名 madolson 15 个。再往下翻这些 ID,一串是 AWS 的工程师,项目的核心 maintainer 也大多来自 AWS。

说好的 Linux Foundation 中立社区,实际上是一支厂商雇佣军在做主力开发。你当然可以说 Redis 原来也是 Redis Ltd. 的员工主导,这没什么不体面,钱的去向决定了代码的去向。但如果你是当年因为「不想被一家厂商绑死」才从 Redis 搬出来的用户,这个事实值得放进你的选型天平。BSD 协议保住了你的自由,bus factor 还是那个味道。

横向摆一张表看会更清楚。

项目协议与 Redis 生态关系现状
ValkeyBSD-3冻结在 7.2.4 行为快照,drop-in五分支活跃维护,AWS 主导
Redis OSSRSALv2/SSPL/AGPLv3 三选一原厂,8.x 加了向量集功能最全,云托管受限
RedictLGPLv3另一个 fork,更保守社区小,节奏慢
KeyDBMPL-2.0多线程 forkSnap 收购后基本停更
DragonflyBSL 1.1另起炉灶,多线程架构活跃,但不是 drop-in

一句话总结这张表,想零成本逃离 Redis 的协议束缚又不丢云厂商支持,Valkey 目前是唯一选项。想要最新最全的功能,还得回 Redis。至于 Redict 和 KeyDB,说句实话已经不在主赛道了。

冻结的那层,才是自由的那层

拆完这个项目,我一直在想它到底给了我们一个什么可带走的东西。

想来想去是这句,兼容层冻结,底座才能自由。

这个模式不只适用于数据库 fork。你做任何「替代品」,都会面临同一道选择题,是追着被替代者的每个新特性跑,把自己活成镜像,还是画一条清晰的兼容线,线以上的行为永久冻结,线以下放手重构。Valkey 选了后者,于是它敢在两年半里换哈希表、换过期结构、换迁移协议,因为客户端看到的永远是那个 7.2.4,里面怎么动都不会外溢。

反过来看,那些试图全兼容的替代品,最后的宿命往往是永远慢半拍的影子。兼容是手段,不是目的,把手段当目的的项目会被手段拖死。

所以回到选型。你的业务如果就是缓存、队列、计数器这些经典场景,Valkey 的成熟度和那五条维护分支给你的安全感,配得上生产环境。如果你的路线图上有向量检索、有多活,那现在还是 Redis 8 的地盘,或者老老实实缓存旁边单挂一个向量库。

源码我放在下面了,纯 C,比你想象的易读。挑一个安静的晚上,从 src/version.h 那行注释开始读起,你会看到一个非常清醒的项目。

valkey-io/valkey

评论互动

© 2026 王若风的技术博客 · Powered by Astro