编程 Valkey 9.0 深度拆解:当 Redis 接班者迈过「10 亿 QPS」门槛——从原子槽位迁移、哈希字段过期到集群多数据库的工程全貌(2026)

2026-07-18 04:16:20 +0800 CST views 11

Valkey 9.0 深度拆解:当 Redis 接班者迈过「10 亿 QPS」门槛——从原子槽位迁移、哈希字段过期到集群多数据库的工程全貌(2026)

两年前,一场许可证风波把 Redis 从「事实标准」推下神坛;两年后,从它 7.2.4 分叉出来的 Valkey 已经不是「平替」,而是在多个维度上跑到了原项目前面。Valkey 9.0 带来原子槽位迁移、哈希字段过期、集群模式多数据库、MPTCP,以及可扩展到 2000 节点、单集群每秒超 10 亿请求的处理能力。本文以工程师视角,把这些特性从「新闻标题」拆到「源码逻辑」和「生产落地」。

一、背景:一次许可证地震,如何催生一个更快的内存数据库

要理解 Valkey 9.0 为什么长成今天这样,得先把时间线捋清楚,因为它的每一个技术决策几乎都能在这段历史里找到动机。

2024 年 3 月,Redis Labs(现 Redis Inc.)把 Redis 的许可证从宽松的 BSD 3-Clause 切换成双许可:RSALv2(Redis Source Available License v2)和 SSPLv1(Server Side Public License v1)。这两个协议的核心杀伤力在于:

  • RSALv2 禁止把 Redis 作为「托管服务」提供给第三方——这一条直接对准云厂商的商业模式。
  • SSPLv1 要求你以「程序即服务」形式提供软件时,必须公开整个服务端栈的源码,实践中几乎无法接受。

对依赖 Redis 的云厂商和企业来说,这意味着两件事同时发生:法律风险 + 供应商锁定。于是 2024 年 4 月,Linux 基金会联合 AWS、Google Cloud、Oracle、Snap、Ericsson 等玩家,从最后一个 BSD 版本 Redis 7.2.4 分叉出了 Valkey,取名来自「Valkyrie(女武神)」。

有意思的转折是:2025 年 5 月,Redis 又把许可证切回了开源的 AGPLv3。但此时 Valkey 已经形成了独立的社区、独立的路线图,并且——这是关键——在性能工程上走出了自己的一条路。到 2025 年底的社区调查里,约 42% 的用户表示已经迁移或计划迁移到 Valkey。

我个人的判断是:Valkey 能站住脚,靠的不是「政治正确的开源」,而是它真的把单机性能和集群韧性做上去了。 一个只是「换了皮的 Redis」是留不住云厂商的,只有 8.0 那一波把单节点吞吐拉到百万级 QPS、9.0 把集群扩到 2000 节点,才让「迁移」从情怀变成了 ROI。

1.1 先回顾 8.0:性能地基是怎么打的

Valkey 9.0 的很多东西是站在 8.0 的肩膀上,所以有必要先讲清楚 8.0 干了什么。Redis 长期被贴的标签是「单线程模型」,这句话其实不精确——命令执行是单线程的,但从 Redis 6.0 起,I/O(读取解析、写回)就可以多线程了。Valkey 8.0 把这条路走到了极致:

  1. 异步 I/O 线程重写:把 epoll_wait 这类昂贵的套接字轮询从主线程卸载到独立 I/O 工作线程,但命令执行仍然保持单线程,从而既拿到多核并行的好处,又不引入锁竞争、不破坏原子性。
  2. 数据预取(Prefetch):Valkey 的字典是链式哈希,遍历哈希链表时每次访问 dictEntry、键指针、值对象都很可能触发一次昂贵的内存访问(cache miss)。8.0 引入软件预取,在真正用到某块内存之前提前把它拉进 CPU cache。
  3. 内存访问分摊(MAA, Memory Access Amortization):把多个 key 的查找「批处理」,让多次内存访问的延迟互相重叠,而不是串行等待。

效果非常直观。在 AWS c7g.4xlarge(16 vCPU)上:

指标Valkey 7.2Valkey 8.0变化
吞吐量360K RPS1.19M RPS+230%
平均延迟1.792 ms0.542 ms-69.8%
P99 延迟0.927 ms亚毫秒级显著下降

单节点百万级 QPS,这是 8.0 给 9.0 铺的地基。9.0 的故事,是把这套单机能力延展到「超大集群」维度。

二、Valkey 9.0 的四张王牌

Valkey 9.0 在 8.0 发布约一周年后推出。它的核心卖点可以浓缩成四句话:

  1. 原子槽位迁移(Atomic Slot Migration):让集群扩缩容从「痛苦」变「可预测」。
  2. 哈希字段过期(Hash Field Expiration):终于能给 Hash 里的单个 field 设 TTL。
  3. 集群模式多数据库(Numbered Databases in Cluster):打破「集群只能用 db0」的限制。
  4. 规模与协议:可扩展到 2000 节点、单集群 >10 亿 RPS,并新增 MPTCP、客户端命令过滤器等。

下面逐个深挖。

三、原子槽位迁移:把「在线重分片」这件苦差事重做一遍

3.1 老模式的痛:逐 key 迁移的一致性缺口

要理解为什么这是 9.0 最重要的特性,得先讲清楚 Valkey/Redis Cluster 的槽位(slot)模型。

在集群里,所有 key 通过 CRC16(key) mod 16384 映射到 16384 个槽位之一,每个节点负责其中一部分槽位。当你要扩容(加节点)或缩容(减节点)时,本质上就是把一部分槽位从旧节点搬到新节点,这个过程叫「重分片(resharding)」。

老的迁移模式是**逐 key(key-by-key)**的,大致流程是这样的:

# 传统 Redis/Valkey 重分片的核心命令序列(简化)
# 1. 目标节点:准备导入某个槽位
CLUSTER SETSLOT <slot> IMPORTING <source-node-id>
# 2. 源节点:准备导出某个槽位
CLUSTER SETSLOT <slot> MIGRATING <target-node-id>
# 3. 循环:从源节点取出该槽位下的 key,逐个 MIGRATE 到目标
CLUSTER GETKEYSINSLOT <slot> <count>
MIGRATE <target-host> <target-port> "" 0 <timeout> KEYS key1 key2 ...
# 4. 全部搬完后:通知集群该槽位归属已变更
CLUSTER SETSLOT <slot> NODE <target-node-id>

这套流程能用,但它有几个「工程上让人夜里睡不好」的缺陷:

  • 过渡态很长且脆弱:在 MIGRATING/IMPORTING 状态期间,同一个槽位的 key 一部分在源节点、一部分在目标节点。客户端访问时,如果 key 已迁走,源节点返回 ASK 重定向,客户端得再问一次目标节点。这套 ASK 重定向逻辑对客户端 SDK 是有要求的,写得不好就会报错。
  • 迁移过程中槽位归属可能变化:逐 key 迁移是渐进的,中途如果发生故障转移或人为干预,槽位归属可能出现不一致,键路由变得不可预测。
  • 大 key 是定时炸弹MIGRATE 是同步阻塞的,遇到一个几百 MB 的大 Hash / 大 List,源节点会被卡住,整个节点的其它请求延迟飙升。
  • 容量规划靠玄学:因为迁移耗时和风险不可预测,很多团队干脆不敢在业务高峰做重分片,扩容变成一件需要「排期 + 祈祷」的事。

3.2 新模式:整槽原子迁移,走 AOF 通道

Valkey 9.0 的原子槽位迁移,核心思路来自 Valkey 开源负责人 Kyle Davis 的一句话总结:

在 Valkey 9.0 中,迁移不再是按 key 迁移,而是一次迁移整个槽位,并通过 AOF 格式进行原子移动。

拆开看,这里有三个关键设计:

1)以「槽位」为迁移的原子单元,而不是 key。

老模式的最小单元是单个 key,所以过渡态下一个槽位可能「半迁移」。新模式下,一个槽位要么完全在源节点,要么完全在目标节点,不存在半迁移的中间态。这从根本上消除了「槽位归属在传输过程中改变」的问题,键路由变得一致且可预测。

2)用 AOF 格式做数据传输载体。

这一步很巧妙。AOF(Append Only File)本来是 Valkey 的持久化机制,它把写命令以协议格式追加记录。原子槽位迁移复用了这套机制:源节点把目标槽位的数据以 AOF 命令流的形式「回放」给目标节点,目标节点像重放 AOF 一样把数据吃进去。这样做的好处是:

  • 复用了成熟的序列化/反序列化路径,不用为迁移单独造一套编码。
  • 天然支持流式传输,边生成边发送,内存占用可控。
  • 迁移期间源节点新产生的写入,可以作为增量 AOF 追加同步,最后做一次短暂的切换。

3)交接是原子的、可预测的。

迁移完成时,槽位归属的切换是一个原子操作:在切换点之前,所有请求由源节点服务;切换点之后,由目标节点服务。中间没有那段冗长易错的 ASK 重定向窗口。

Momento 的联合创始人 Khawaja Shams 和 AWS Hero Allen Helton 对此的评价,我认为一针见血:

对于在集群环境中运行 Valkey 的团队而言,这从根本上改变了容量规划和运维风险管理方式。扩容将变得可预测,而不再是痛苦的过程。

3.3 从工程角度看,它到底改变了什么

我把新旧模式做一个对照表,这样最直观:

维度逐 key 迁移(旧)原子槽位迁移(9.0)
迁移单元单个 key整个槽位
中间态半迁移,源/目标各持一部分无半迁移态
数据通道MIGRATE 同步阻塞AOF 命令流,可流式
大 key 影响同步阻塞,可能卡死节点流式传输,压力平摊
客户端重定向大量 ASK 重定向交接前后清晰,重定向窗口极短
归属一致性传输中可能变化原子切换,可预测
容量规划靠经验和排期可预测,可自动化

对一线 SRE 来说,最实际的收益是:重分片终于可以放心地在业务时段做,甚至可以交给自动扩缩容系统来触发。 这对需要弹性伸缩的在线业务(比如电商大促、直播峰值)是质变。

一个典型的 9.0 重分片操作会简化成类似这样(具体命令以官方文档为准,这里表达设计意图):

# 9.0 风格:以槽位为单位发起原子迁移(示意)
# 把 slot 5000~5100 原子迁移到目标节点
valkey-cli --cluster reshard <any-node>:6379 \
  --cluster-from <source-node-id> \
  --cluster-to   <target-node-id> \
  --cluster-slots 101 \
  --cluster-yes
# 底层不再逐 key MIGRATE,而是整槽 AOF 流式 + 原子交接

关键不在命令长啥样,而在于底层语义:你不再需要担心「迁移到一半集群状态诡异」这种事。

四、哈希字段过期:一个憋了很多年的刚需

4.1 为什么这是「刚需」而不是「锦上添花」

Redis/Valkey 的 Hash 是最常用的数据结构之一,典型用法是「用一个 Hash 存一个对象」:

HSET user:1000 name "张工" age 30 email "a@example.com" session_token "abc123"

问题来了:TTL(过期时间)在很长时间里只能设在整个 key 上,即 EXPIRE user:1000 3600 会让整个 Hash 一起消失。但真实业务里,我们经常需要字段级别的过期:

  • session_token 需要 30 分钟过期,但 nameage 要永久保留。
  • 购物车里每个商品有各自的「保留时长」。
  • 限流计数器里,不同维度的窗口过期时间不同。

在 9.0 之前,解决这个需求的办法很别扭:把需要独立过期的字段拆成单独的 key

# 老办法:被迫拆 key
HSET user:1000 name "张工" age 30
SET  user:1000:session_token "abc123" EX 1800   # 单独一个 key 才能设 TTL

这带来一堆副作用:key 数量膨胀、失去了 Hash 的内存紧凑优势、原本一次 HGETALL 能取回的对象现在要多次访问、原子性也没了。

4.2 9.0 的实现:字段级 TTL + 主动过期

Valkey 9.0 允许 Hash 里每个字段独立过期。对应的命令族(沿用 Redis 7.4 引入、Valkey 跟进的语义)大致是:

# 给 Hash 的指定字段设置过期时间(秒)
HEXPIRE user:1000 1800 FIELDS 1 session_token
# 毫秒精度
HPEXPIRE user:1000 60000 FIELDS 1 temp_flag
# 设置为某个绝对时间戳过期
HEXPIREAT user:1000 1893456000 FIELDS 1 promo_code
# 查询字段剩余 TTL
HTTL user:1000 FIELDS 1 session_token
# 返回:1) (integer) 1795
# 移除某字段的过期时间,让它变回永久
HPERSIST user:1000 FIELDS 1 session_token

于是前面那个用户对象可以优雅地写成:

HSET user:1000 name "张工" age 30 email "a@example.com" session_token "abc123"
HEXPIRE user:1000 1800 FIELDS 1 session_token
# name/age/email 永久保留,session_token 30 分钟后自动消失

4.3 底层:主动过期机制与内存开销

字段级过期看起来简单,实现上其实有讲究。AWS 高级软件工程师 Ran Shidlansik 解释,Valkey 采用主动过期(active expiration)机制来清理已过期的哈希字段——也就是说,不完全依赖「访问时才发现过期」的惰性删除,而是有一个共享的后台任务主动扫描并回收过期字段占用的内存。

这里的工程难点在于三个「不能牺牲」:

  1. 不能牺牲内存效率:Hash 在字段少、值小的时候用 listpack(紧凑编码),字段多了才转成哈希表。给每个字段挂 TTL,需要额外存过期时间戳,如果处理不好会破坏 listpack 的紧凑性。Valkey 的实现让额外内存开销保持可控。
  2. 不能牺牲延迟:主动过期任务不能在扫描时长时间占用主线程(记住,命令执行还是单线程的),否则会拉高尾延迟。Valkey 用共享的主动过期任务,在高写入压力下仍能高效回收。
  3. 不能牺牲吞吐:基准测试显示,加入字段级过期后指令吞吐未受影响。

Shidlansik 的总结值得抄下来:

基准测试表明,字段级过期可以在不牺牲内存效率或延迟的情况下加入 Valkey。额外内存开销保持可控,指令吞吐未受影响,而共享的主动过期任务在高写入压力下仍能高效回收内存。

4.4 实战:用 HFE 重写一个会话管理器

我用 Go 客户端(valkey-go)写一个「字段级过期」的会话/多设备登录管理示例,这是 HFE 最典型的落地场景:一个用户在多个设备登录,每个设备的 token 独立过期。

package main

import (
    "context"
    "fmt"
    "time"

    "github.com/valkey-io/valkey-go"
)

type SessionManager struct {
    client valkey.Client
}

func NewSessionManager(addr string) (*SessionManager, error) {
    c, err := valkey.NewClient(valkey.ClientOption{
        InitAddress: []string{addr},
    })
    if err != nil {
        return nil, err
    }
    return &SessionManager{client: c}, nil
}

// Login 记录某用户在某设备上的会话,token 独立过期
func (s *SessionManager) Login(ctx context.Context, userID, device, token string, ttl time.Duration) error {
    key := fmt.Sprintf("sessions:%s", userID)

    // 1. 用一个 Hash 存该用户所有设备的 token
    if err := s.client.Do(ctx,
        s.client.B().Hset().Key(key).FieldValue().FieldValue(device, token).Build(),
    ).Error(); err != nil {
        return err
    }

    // 2. 给这个设备字段单独设置过期时间(HFE 的核心)
    return s.client.Do(ctx,
        s.client.B().Hexpire().Key(key).
            Seconds(int64(ttl.Seconds())).
            Fields().Numfields(1).Field(device).Build(),
    ).Error()
}

// ActiveDevices 返回当前仍有效(未过期)的设备列表
func (s *SessionManager) ActiveDevices(ctx context.Context, userID string) (map[string]string, error) {
    key := fmt.Sprintf("sessions:%s", userID)
    // 过期字段会被主动过期任务清理,HGETALL 只会返回仍有效的字段
    return s.client.Do(ctx,
        s.client.B().Hgetall().Key(key).Build(),
    ).AsStrMap()
}

func main() {
    sm, err := NewSessionManager("127.0.0.1:6379")
    if err != nil {
        panic(err)
    }
    ctx := context.Background()

    // 手机端 30 分钟过期,Web 端 2 小时过期
    _ = sm.Login(ctx, "1000", "mobile", "tok_mobile_xxx", 30*time.Minute)
    _ = sm.Login(ctx, "1000", "web", "tok_web_yyy", 2*time.Hour)

    devices, _ := sm.ActiveDevices(ctx, "1000")
    fmt.Printf("当前活跃设备: %v\n", devices)
    // 30 分钟后 mobile 字段自动消失,无需业务侧扫描清理
}

对比老办法(每个设备一个独立 key + SCAN 清理),这套代码的优势是:内存紧凑、一次 HGETALL 拿全、过期自动化、不用写清理 cron。这就是 HFE 的价值。

五、集群模式多数据库:命名空间的回归

5.1 一个被「集群」阉割掉的老功能

Redis 单机模式一直支持 16 个「编号数据库」(db0 ~ db15),用 SELECT n 切换。它本质上是一种轻量的命名空间机制,用来隔离数据、防止 key 冲突。

但一旦你开启集群模式,这个功能就被禁用了——集群模式下只能用 db0。这是 Redis 的历史限制,Valkey 早期版本也继承了它。原因是集群的槽位路由和多数据库的语义有冲突,实现起来复杂。

结果是很多团队在上集群时被迫改造代码:原本靠 SELECT 隔离的多租户/多应用数据,要么改成 key 前缀,要么拆成多个集群,运维成本陡增。

5.2 9.0:集群里也能用编号数据库了

Valkey 9.0 取消了这个限制,引入了对编号数据库的完整集群支持。Kyle Davis 把它定位为一种命名空间机制,并说明最直接的使用场景:

需要逻辑上隔离数据,同时能够接受资源共享带来的影响。例如,将不同客户的数据分隔开,或在资源不成问题的情况下整合多个应用到同一个集群中。

用起来就回到了熟悉的姿势:

# 集群模式下也能切换数据库了
valkey-cli -c -h 127.0.0.1 -p 6379
127.0.0.1:6379> SELECT 1
OK
127.0.0.1:6379[1]> SET tenant_a:config "..."
OK
127.0.0.1:6379[1]> SELECT 2
OK
127.0.0.1:6379[2]> SET tenant_b:config "..."
OK

5.3 什么时候该用它,什么时候别用

这里我要泼一点冷水,因为「能用」不等于「该用」。编号数据库是逻辑隔离,不是资源隔离——同一个集群里所有 db 共享同样的 CPU、内存、网络。所以:

适合用的场景:

  • 多租户但租户之间资源需求相近、互相信任,只是想在逻辑上分开、避免 key 冲突。
  • 把多个小应用合并到一个集群,降低运维实例数(在资源不成问题的前提下)。
  • 从单机多 db 架构迁移到集群,希望尽量少改代码。

不适合用的场景:

  • 需要强资源隔离(某个租户的热 key 不能拖垮别人)——这种应该上独立集群或独立节点。
  • 需要按租户独立扩缩容、独立备份恢复。
  • 大量使用跨 db 的操作(SELECT 之后跨库操作在集群语义下依然受限)。

我的经验法则:多数据库是「省事」的工具,不是「隔离」的银弹。 把它当命名空间用,别指望它做 QoS。

六、规模、协议与其它:把「大」这件事做扎实

6.1 2000 节点、10 亿 RPS

社区讨论展示了 9.0 在架构上的韧性增强:系统可扩展到 2000 个节点,实现每秒超 10 亿次请求的处理能力。这个数字要拆开理解:

  • 集群理论上限还是 16384 个槽位,2000 节点意味着平均每个节点管约 8 个槽位——这个量级下,集群总线(cluster bus)的 gossip 通信、故障检测、配置传播都会面临压力,9.0 对这些路径做了韧性优化。
  • 10 亿 RPS 是集群聚合吞吐,不是单节点。按 8.0 单节点百万级 QPS 估算,配合 9.0 的架构优化,2000 节点堆到 10 亿量级是合理的。

Shams 和 Helton 对性能来源的解释是:

9.0 的性能提升来自对现代 CPU 能力的智能利用。Valkey 9.0 能在现代硬件上发挥更高效的表现……最终带来更低的尾延迟、更高的单节点吞吐量,以及可量化的成本效率。

关键词是成本效率。对云厂商和大规模用户来说,「同样的 QPS 用更少的节点」直接等于省钱,这是 Valkey 相对 Redis 最硬的竞争力。

6.2 Multipath TCP(MPTCP)支持

9.0 新增了 MPTCP 支持。MPTCP 是 TCP 的扩展,允许一条逻辑连接同时使用多条网络路径(多网卡/多链路),带来两个好处:

  • 带宽聚合:多条链路的带宽可以叠加。
  • 无缝故障切换:一条链路断了,连接不中断,流量自动切到其它链路。

对跑在多网卡服务器、或者对网络可用性要求极高的场景(金融、电信),这是一个实打实的可用性升级。

# valkey.conf 示意:开启 MPTCP(具体参数以官方文档为准)
# 让 Valkey 监听时启用多路径 TCP
mptcp yes

6.3 客户端命令过滤器

9.0 引入了新的客户端命令过滤器机制。它的价值在于在服务端侧对命令做拦截/审计/限制,典型用途包括:

  • 禁用危险命令(FLUSHALLKEYS *)对特定客户端生效。
  • 做命令级别的审计日志。
  • 为多租户环境做细粒度的命令权限控制。

配合 ACL,Valkey 的安全治理能力比早期版本完整了很多。

七、迁移与选型:从 Redis 迁到 Valkey,到底要注意什么

聊了这么多特性,落到实处,最多人问的还是:「我现在用 Redis,能无痛迁到 Valkey 吗?」我给一个务实的清单。

7.1 兼容性现状

Valkey 从 Redis 7.2.4 分叉,协议、数据结构、客户端 SDK 高度兼容,绝大多数应用不需要改代码。valkey-cli、valkey-server 的命令和 redis 基本一一对应。

但有一个历史坑要注意:Redis 7.4 引入了闭源的 RDB 算法,这导致 Valkey 无法直接从 Redis 7.4+ 做 replica(主从复制)迁移。如果你的 Redis 已经是 7.4 或更高版本,迁移路径要绕一下:

# 无法用主从复制迁移时的兜底方案:SCAN + 重写
# 1. 从 Redis 侧 SCAN 所有 key
# 2. 逐批读出并写入 Valkey

一个简化的迁移脚本骨架(Python,用于 Redis 7.4+ → Valkey):

import redis  # 连 Redis
import valkey # 连 Valkey(或直接用兼容的 redis 客户端连 valkey)

src = redis.Redis(host="redis-host", port=6379, db=0)
dst = redis.Redis(host="valkey-host", port=6379, db=0)  # 协议兼容

cursor = 0
BATCH = 500
while True:
    cursor, keys = src.scan(cursor=cursor, count=BATCH)
    if keys:
        pipe = dst.pipeline(transaction=False)
        for k in keys:
            # DUMP/RESTORE 在跨大版本时可能不兼容,稳妥起见按类型逐个重写
            ttl = src.pttl(k)
            t = src.type(k).decode()
            if t == "string":
                pipe.set(k, src.get(k))
            elif t == "hash":
                pipe.hset(k, mapping=src.hgetall(k))
            elif t == "list":
                vals = src.lrange(k, 0, -1)
                if vals:
                    pipe.rpush(k, *vals)
            elif t == "set":
                members = src.smembers(k)
                if members:
                    pipe.sadd(k, *members)
            elif t == "zset":
                members = src.zrange(k, 0, -1, withscores=True)
                if members:
                    pipe.zadd(k, dict(members))
            # 迁移完补回 TTL
            if ttl and ttl > 0:
                pipe.pexpire(k, ttl)
        pipe.execute()
    if cursor == 0:
        break
print("迁移完成")

注意:DUMP/RESTORE 在跨大版本、跨闭源 RDB 算法时可能失败,所以上面按类型逐个重写更稳妥。生产环境还要考虑迁移期间的双写/增量同步,避免数据漂移。

7.2 什么时候值得迁

我的判断标准很简单:

  • 纯缓存、追求成本效率 → 迁 Valkey,8.0/9.0 的单机吞吐直接省节点。
  • 跑集群、经常扩缩容 → 强烈建议 9.0,原子槽位迁移能救命。
  • 重度用 Hash 且需要字段级 TTL → 9.0 的 HFE 能删掉一大堆自研的清理逻辑。
  • 深度绑定 Redis 商业模块(RediSearch、RedisJSON 等闭源模块)→ 谨慎,需要评估 Valkey 生态的对应替代(Valkey 有自己的模块生态,但要逐一核对)。

7.3 性能压榨:几个不该忽略的配置

无论 Redis 还是 Valkey,默认配置都不会榨干硬件。几个高杠杆参数:

# 开启 I/O 线程(Valkey 8.0+ 的性能核心,9.0 延续)
io-threads 8
# 内存上限与驱逐策略(缓存场景推荐 allkeys-lru)
maxmemory 16gb
maxmemory-policy allkeys-lru
maxmemory-samples 5
# 大集群集群总线缓冲区调大(2000 节点场景尤其重要)
cluster-node-timeout 5000
# 持久化:缓存可关 AOF 求吞吐,数据库场景开 AOF 求安全
appendonly no
# 惰性删除,避免大 key 删除阻塞主线程
lazyfree-lazy-expire yes
lazyfree-lazy-eviction yes
lazyfree-lazy-server-del yes

其中 io-threads 要根据 CPU 核数调,不是越大越好——I/O 线程和主线程会争 CPU,一般设成物理核数的一半到 3/4 是比较稳的起点,然后压测调优。

八、横向对比:Valkey、Redis、Dragonfly 三者怎么选

内存数据库这条赛道现在其实是三国杀:坚持商业化的 Redis、社区驱动的 Valkey,以及主打「多线程、drop-in 替代」的 Dragonfly。我把关键维度列个表:

维度Redis(Inc.)Valkey 9.0Dragonfly
许可证AGPLv3(2025 切回)BSD 3-ClauseBSL(商业友好度介于两者)
治理单一公司主导Linux 基金会 + 多云厂商单一公司主导
线程模型命令单线程 + I/O 多线程命令单线程 + I/O 多线程 + Prefetch/MAA全多线程(shared-nothing)
集群重分片逐 key 迁移原子槽位迁移内建垂直扩展,弱化分片
字段级 TTL7.4+ 支持9.0 支持支持程度需核对版本
生态兼容原生高度兼容 Redis兼容 Redis 协议
云厂商支持部分AWS/Google/Oracle 一线支持逐步增加

我的选型建议(带个人立场):

  • 要开源纯粹 + 云厂商背书 + 集群弹性 → Valkey 9.0,目前综合最稳。
  • 要极致单机纵向扩展、不想折腾分片 → Dragonfly 值得一试,但要接受它的许可证和相对年轻的生态。
  • 已重度绑定 Redis 商业模块生态 → 留在 Redis,或做混合架构。

九、总结与展望:内存数据库进入「社区治理 + 硬件榨取」的双主题时代

把 Valkey 9.0 放到更大的图景里看,我觉得它标志着内存数据库这个品类进入了两个新的主题:

主题一:治理模式的胜利。 Valkey 用两年时间证明了,一个由基金会 + 多家云厂商共同治理的开源项目,不仅能活下来,还能在工程上跑赢原项目。这背后是 AWS、Google、Oracle 这些巨头把「不被单一供应商锁定」当成战略级诉求在投入。对用户来说,这意味着更低的锁定风险和更可预测的路线图。

主题二:把现代硬件榨干。 从 8.0 的 Prefetch/MAA,到 9.0 对现代 CPU 的智能利用,Valkey 的性能故事本质上是「软件如何追上硬件」的故事。摩尔定律在 CPU 算力上持续生效,但内存墙(memory wall)越来越高,谁能在 cache 局部性、内存访问模式上做得更好,谁就能用更少的节点扛更多的请求——这就是成本效率,也是云时代最硬的通货。

具体到 9.0 的四张王牌,我的价值排序是:

  1. 原子槽位迁移——对跑集群的团队是质变,把「扩容」从运维噩梦变成日常操作。
  2. 哈希字段过期——删掉大量自研清理逻辑,是「优雅」层面的大升级。
  3. 集群多数据库——降低迁移和整合成本,务实但要理解它是逻辑隔离而非资源隔离。
  4. MPTCP / 命令过滤器 / 2000 节点——面向超大规模和高可用场景的地基加固。

如果你现在还在纠结「要不要从 Redis 迁走」,我的建议是:先把 Valkey 8.0/9.0 拉起来压测一轮,用你自己的业务负载去跑,让数字说话。 大概率你会发现,同样的 SLA,节点数能砍一截——而这,就是最好的迁移理由。

至于未来,Valkey 的路线图里还有更激进的方向:更细粒度的多线程、更智能的自动分片、与向量检索/AI 负载的深度结合。内存数据库这场仗,才刚打到中局。


本文基于 Valkey 官方发布信息、Linux 基金会公告及社区技术讨论整理,技术细节以官方文档为准。代码示例为教学演示,生产使用请结合实际压测。

推荐文章

Vue中的表单处理有哪几种方式?
2024-11-18 01:32:42 +0800 CST
OpenCV 检测与跟踪移动物体
2024-11-18 15:27:01 +0800 CST
Golang - 使用 GoFakeIt 生成 Mock 数据
2024-11-18 15:51:22 +0800 CST
Shell 里给变量赋值为多行文本
2024-11-18 20:25:45 +0800 CST
CSS 媒体查询
2024-11-18 13:42:46 +0800 CST
程序员茄子在线接单