🤖
AI审核中

Valkey 9.1 不只是 Redis 替代品:从性能、安全到生产迁移实战

Java 20分钟 102浏览 0评论

很长一段时间里,只要项目需要缓存、分布式锁、排行榜、会话存储或延迟队列,Redis 几乎都是默认答案。

2024 年 3 月,Redis 宣布从 7.4 开始采用 RSALv2 与 SSPLv1 双重源码可用许可证。Linux Foundation 随后推动成立 Valkey 项目,基于 Redis OSS 7.2.4 的代码继续演进。Redis 后来又在 2025 年为 Redis 8 增加了 AGPLv3 许可证选项,但此时 Valkey 已经形成了独立的社区、版本和技术路线。(Redis)

刚诞生时,Valkey 最重要的标签是“Redis 兼容替代品”。到了 9.1,这个描述已经不够准确。

Valkey 9.1 于 2026 年 5 月发布,由超过 80 位贡献者共同完成,改动覆盖安全、可观测性、I/O 性能、内存效率、新命令和集群工具。它正在从一个兼容分支,逐渐成长为一套拥有独立工程取舍的高性能键值数据库。(Valkey)

本文不讨论“Redis 和 Valkey 谁一定更好”,而是回答三个更实际的问题:

  1. Valkey 9.1 到底改变了什么?
  2. 现有 Java 项目接入它需要改多少代码?
  3. 一个正在运行的 Redis 项目,应该怎样安全迁移?

一、先划清边界:兼容不等于永远完全一致

Valkey 7.2.4 直接继承自 Redis OSS 7.2.4,因此 Redis OSS 7.2 及更早版本与 Valkey 之间具有较完整的兼容基础。

这种兼容不仅是命令名称相似,还包括多个层面:

兼容层面 具体表现
网络协议 支持 RESP2 和 RESP3
客户端 支持 Redis OSS 7.2 的客户端通常可以直接连接
配置文件 支持 Redis OSS 7.2 的常用配置项
持久化 可以读取对应版本的 RDB 和 AOF 文件
命令行工具 redis-cli 可以连接 Valkey,valkey-cli 也可以连接 Redis OSS
Lua 脚本 原有 redis 命名空间脚本可以继续运行
模块接口 基于旧版 RedisModule_ API 编写的模块通常可以继续加载

但这里有一个非常重要的版本边界:Redis Community Edition 7.4 及以后版本生成的数据文件,不能直接通过 RDB 或 AOF 文件迁移到 Valkey。

因此,“Valkey 可以无缝替换 Redis”只对特定版本和特定功能范围成立。Redis OSS 7.2 及以前版本的迁移相对直接,而 Redis CE 7.4 之后通常需要逻辑迁移、应用双写或专门的数据同步方案。(Valkey)

还有一个容易误判的细节:为了兼容部分旧客户端,Valkey 的 INFO 结果可能继续返回 redis_version:7.2.4。识别真实服务端时,应同时检查 server_namevalkey_version,不能只读取 redis_version。(Valkey)

二、Valkey 9.1 的安全升级,不只是增加几个配置项

1. ACL 可以限制到具体数据库

过去的 ACL 可以限制用户执行哪些命令、访问哪些 Key,但对逻辑数据库的控制不够细。

Valkey 9.1 增加了数据库级 ACL,可以限制某个账号只能访问指定编号的数据库。例如,只允许订单服务访问数据库 0:

ACL SETUSER order-service on >StrongPassword +@read +@write ~order:* db=0

这样即使多个业务共用一个 Valkey 实例,也可以从数据库编号、Key 前缀和命令类别三个维度共同限制权限。

对于中小型系统,这种能力可以降低账号误用风险;对于多租户平台,它则意味着权限边界不必完全依赖应用代码维护。(Valkey)

不过,逻辑数据库不能真正替代实例级隔离。不同租户仍然共享内存、CPU、连接数和持久化资源。对安全性、资源隔离要求较高的业务,仍然应该使用独立实例或独立集群。

2. Lua 从内核能力变成独立模块

Lua 脚本一直是 Redis 生态中的重要能力,分布式锁释放、限流、库存扣减等场景都经常依赖它。

但脚本引擎也意味着更大的执行和安全边界。Valkey 9.1 将 Lua 从核心服务中拆分为独立模块,默认场景仍然可以保持兼容,但不需要脚本的部署可以选择不加载 Lua,从而减少服务端攻击面。

同时,INFO 增加了脚本引擎相关信息,运维人员可以明确知道当前实例加载了哪些脚本能力。(Valkey)

这项变化提醒我们:升级到 9.1 之前,不能只检查普通命令,还应盘点项目中的 Lua 脚本、脚本缓存和客户端 EVALSHA 调用。

3. TLS 证书轮换不再依赖重启

Valkey 9.1 可以通过 INFO 查看 TLS 证书过期时间,并支持在后台自动重新加载证书。

这意味着证书更新后,不必为了让新证书生效而重启整个缓存实例。同时,它还支持通过证书 SAN URI 进行 TLS 身份认证,更便于接入基于工作负载身份的 mTLS 体系。(Valkey)

对于缓存服务来说,证书轮换看似是一个小功能,但证书过期导致的连接中断,往往会迅速放大为数据库流量激增、接口超时和连锁故障。

三、CPU 接近 100%,不一定代表 Valkey 已经跑满

排查缓存性能问题时,人们经常先看 CPU。

但 Valkey 的主线程和 I/O 线程可能采用忙等待方式等待任务。线程即使没有真正处理大量命令,也可能在操作系统层面呈现较高 CPU 使用率。

因此,仅凭服务器 CPU 接近 100%,不能直接判断实例已经饱和。

Valkey 9.1 增加了主线程和 I/O 线程的累计使用指标,可以帮助运维人员区分两种情况:

  • 线程只是处于忙等待状态;
  • 线程确实在持续处理大量请求。

同时,Valkey 9.1 支持直接输出 JSON 格式日志:

log-format json

启用后,每一行日志都是一个独立 JSON 对象,可以直接被 Filebeat、Fluent Bit、Vector、Loki 或其他日志平台采集,不再需要为传统文本日志维护复杂的正则表达式。(Valkey)

一个完整的 Valkey 可观测体系,不应该只有 CPU 和内存,还应同时覆盖:

维度 建议关注的内容
请求性能 吞吐量、平均延迟、P95、P99、最大延迟
线程状态 主线程使用情况、I/O 线程使用情况
内存 已用内存、峰值内存、碎片率、淘汰数量
连接 当前连接、拒绝连接、阻塞客户端
缓存效果 命中数、未命中数、命中率
持久化 RDB 状态、AOF 重写状态、最后成功时间
复制 复制延迟、连接状态、复制积压缓冲区
安全 认证失败、ACL 拒绝、证书剩余有效期

四、2.1 万还是 210 万?性能数据必须看测试条件

Valkey 9.1 官方测试曾在单实例上达到每秒 210 万次请求,但这个结果有明确条件:

  • 数据大小为 512 字节;
  • 使用 9 个 I/O 线程;
  • Pipeline 深度为 10;
  • 测试的是特定硬件与特定负载模型。

因此,这个数字证明的是 Valkey 9.1 的性能上限和多线程改进效果,而不是所有项目升级后都能直接达到 210 万 QPS。(Valkey)

9.1 中比较有代表性的性能改进包括:

改进方向 官方测试结果
新的 I/O 线程通信模型 部分负载吞吐量最高提升约 17%
XRANGEXREVRANGE 最高提升约 30%
字符串 GET 部分场景最高提升约 30%
硬件时钟默认启用 GETSET 整体最高提升约 3%
小字符串内存占用 部分场景明显下降
跳表结构 排行榜等有序集合场景减少内存占用

这些优化并不是让每条命令都获得相同提升,而是分别针对网络通信、数据编码、时间调用、Stream 热路径和跳表查询进行了改进。

真正评估升级价值时,应把自己的命令比例带入测试。例如:

  • 会话缓存主要关注 GETSET、TTL 和淘汰;
  • 排行榜主要关注 ZADDZRANGEZRANGEBYSCORE
  • 消息流主要关注 XADDXREADGROUPXRANGE
  • 分布式锁主要关注延迟尾部,而不是最高吞吐量。

五、内存优化可能比吞吐量提升更有价值

对于生产缓存,真正昂贵的资源往往不是 CPU,而是内存。

Valkey 9.1 对小字符串的内部对象布局进行了优化。过去,使用 embstr 编码的小字符串对象仍然保留了一个可以通过地址计算得到的指针。9.1 移除了这个冗余指针,并将 embstr 的适用范围提升到更大的对象。

在官方测试中,小字符串对象的内存开销下降约 17% 至44%,平均约为 26%。对于使用跳表编码的有序集合,长度在 10 至40 字节之间的成员,每个成员大约可以减少 6 至8.5 字节开销,降幅约为 11% 至15%。具体效果会受到 Key 长度、Value 长度、编码方式以及 jemalloc 内存分级影响。(Valkey)

假设系统存在 5000 万个能够节省 8 字节的小对象,仅结构层面的理论节省就可以达到约 400MB。

不过,实际内存节省不能只靠乘法估算。jemalloc 会按照固定大小等级分配内存,结构体减少 8 字节后,只有跨过某个分配等级边界,进程实际占用才会明显下降。

升级前后可以使用下面两个命令抽样验证:

MEMORY USAGE session:abc123
OBJECT ENCODING session:abc123

建议分别挑选以下类型的代表性数据:

  • 短字符串;
  • 较长的序列化对象;
  • 带有过期时间的会话;
  • 大型 Hash;
  • 排行榜使用的 Sorted Set;
  • Stream 消息。

不要只比较实例总内存,因为后台任务、碎片率、Key 数量和测试数据差异都可能干扰结果。

六、三个新命令,减少业务代码中的“伪原子操作”

1. HGETDEL:读取并删除 Hash 字段

过去,如果业务需要读取某个 Hash 字段后立即删除,通常要执行 HGETHDEL,或者使用事务、Lua 脚本保证中间状态不被其他请求打断。

Valkey 9.1 增加了 HGETDEL,可以在一个命令中完成读取和删除:

HSET job:42 status "pending" payload "{\"type\":\"send_email\"}" retries "3"

HGETDEL job:42 FIELDS 2 status payload

它适合一次性任务参数、临时授权信息和消费后即删除的数据,但不能直接替代完整消息队列,因为它本身不提供消费确认、重试和死信机制。

2. MSETEX:批量写入并设置统一过期时间

多个缓存数据需要同时写入且使用相同 TTL 时,过去通常要通过多条 SETEX,或者组合 Pipeline、MSETEXPIRE

现在可以直接执行:

MSETEX 3 session:a "user:1" session:b "user:2" session:c "user:3" EX 3600

它可以减少网络往返,也能避免应用在批量写入过程中遗漏某个 Key 的过期时间。

3. CLUSTERSCAN:统一扫描整个集群

传统 Redis Cluster 中,普通 SCAN 只能扫描当前连接节点负责的数据。想遍历整个集群,客户端必须获取全部主节点,然后分别执行 SCAN 并合并结果。

Valkey 9.1 增加了 CLUSTERSCAN

CLUSTERSCAN 0 MATCH "session:*"

它为集群运维、数据排查和迁移工具提供了统一入口,还可以按照 Key 类型或槽位进行过滤。(Valkey)

不过,CLUSTERSCAN 仍然不应该被当成业务查询接口。集群中存在数千万个 Key 时,全量扫描依然会消耗 CPU、网络和客户端内存。正确做法是使用游标分批处理,并避开业务高峰。

七、Java 项目接入 Valkey,需要修改多少代码

从协议层面看,大部分 Java 应用并不关心服务端叫 Redis 还是 Valkey。

只要应用使用的是 Redis OSS 7.2 及以前的通用命令,现有客户端通常可以直接连接 Valkey。Valkey 官方客户端列表中也提供了 valkey-java 和 Valkey GLIDE Java 等选择,其中 GLIDE Java 支持 Valkey 7.2 及以上版本。(Valkey)

对于使用 Spring Data Redis 的项目,最小改动方案通常只是切换连接地址:

spring:
  data:
    redis:
      host: valkey
      port: 6379
      password: ${VALKEY_PASSWORD}
      timeout: 2s

配置名称中继续出现 redis 并不代表连接的服务端必须是 Redis。Spring Data Redis 提供的是基于 Redis 协议和数据结构的抽象,底层仍然通过客户端与兼容服务通信。(Home)

Java 项目可以根据迁移阶段选择不同策略:

策略 适用场景 改造成本
保留现有客户端 只使用通用命令,希望快速验证
更换为 Valkey 官方客户端 新项目或准备长期使用 Valkey
封装统一缓存访问层 需要兼容 Redis 和 Valkey
大量使用 Valkey 9.x 新命令 已确定不再回退旧版本 较高

更稳妥的做法,是把 Valkey 特有命令封装在 Repository 或基础设施层,而不是让业务代码直接到处调用 HGETDELMSETEX

例如,业务层只依赖下面这样的语义接口:

public interface TemporaryJobRepository {

    Optional<JobPayload> consume(String jobId);

    void saveBatch(Map<String, JobPayload> jobs, Duration ttl);
}

底层可以根据服务端能力选择 Lua、事务或 Valkey 新命令。这样即使未来切换客户端或存储引擎,也不需要修改大量业务代码。

八、生产迁移优先使用复制,而不是直接复制文件

Valkey 官方提供了三类迁移方案:

迁移方式 停机时间 适用场景
复制 RDB 文件 较长 数据量不大、允许停机
Redis 到 Valkey 复制 较短 持续写入的生产系统
迁移指定 Key 可控 只迁移部分业务数据

对于 Redis OSS 7.2 及以前版本,生产环境通常更适合采用复制迁移:先把 Valkey 配置为 Redis 的副本,完成全量和增量同步,再切换应用连接并将 Valkey 提升为主节点。(Valkey)

flowchart LR
    A[盘点版本与依赖] --> B[搭建兼容性环境]
    B --> C[将 Valkey 配置为副本]
    C --> D[完成全量与增量同步]
    D --> E[校验键值与过期时间]
    E --> F[灰度切换应用连接]
    F --> G[提升 Valkey 为主节点]
    G --> H[观察后关闭旧实例]

迁移前必须完成的盘点

不能只记录 Redis 版本,还应检查:

  1. 单机、Sentinel 还是 Cluster;
  2. RDB、AOF 以及重写策略;
  3. 客户端类型和版本;
  4. 是否使用 Lua 脚本;
  5. 是否加载第三方模块;
  6. 是否依赖特殊命令;
  7. Key 数量、数据类型和 TTL 分布;
  8. 最大 Value、大型 Hash、大型 Set 和热 Key;
  9. 内存淘汰策略;
  10. 监控、备份和故障转移流程。

同步完成后,不能只比较 Key 数量

DBSIZEINFO KEYSPACE 相同,只能说明 Key 数量基本一致,不能证明数据完全正确。

至少还要验证:

  • 不同数据类型的抽样值;
  • TTL 是否保持;
  • Stream 消费组和待确认消息;
  • Lua 脚本是否能够执行;
  • 模块数据能否读取;
  • Cluster 槽位是否完整;
  • 热 Key 是否存在异常;
  • 复制延迟是否归零;
  • 应用错误率和超时率是否变化。

切换期间仍然可能丢失最后一小段写入

官方迁移说明也提醒,在 Redis 仍有写请求、而副本尚未完全追平时关闭原节点,仍然存在数据丢失风险。(Valkey)

因此,关键系统在最终切换前应安排一个短暂的写入冻结窗口:

  1. 暂停或排空写请求;
  2. 确认复制状态正常;
  3. 确认偏移量不再变化;
  4. 切换应用连接;
  5. 将 Valkey 提升为主节点;
  6. 恢复写入。

九、保留回滚能力,比快速使用新命令更重要

迁移完成后,很多团队会立即使用 Valkey 9.1 的新命令。

这会让回滚变得更加困难。

旧版 Redis 并不认识 HGETDELMSETEXCLUSTERSCAN 等 Valkey 新命令。更重要的是,一旦新系统承接了持续写入,原 Redis 实例中的数据就会逐渐落后。

因此,生产迁移建议分成两个阶段。

第一阶段:只替换服务端

  • 保留原有命令集;
  • 不修改数据模型;
  • 不立即使用 Valkey 特有能力;
  • 观察延迟、错误率、内存和持久化;
  • 保留旧实例和原始备份。

第二阶段:关闭回滚窗口

确认系统稳定后,再逐步启用:

  • 数据库级 ACL;
  • JSON 日志;
  • Valkey 专属监控指标;
  • HGETDEL
  • MSETEX
  • 原子槽位迁移;
  • 新的集群运维能力。

这种方式虽然比“一次性升级并改造所有代码”慢一些,但可以把服务端迁移风险与业务代码改造风险分开。

对于特别核心的系统,还可以采用更保守的两阶段版本路线:

Redis OSS 7.2 → Valkey 7.2 → Valkey 9.1

这样可以先完成项目和治理体系迁移,再单独验证跨大版本升级。Valkey 官方也建议谨慎处理大版本升级,并优先升级副本,再升级主节点。(Valkey)

十、不要用默认压测结果做容量规划

Valkey 自带 valkey-benchmark,但直接运行默认命令,得到的结果通常不能代表真实业务。

默认测试主要反复访问同一个 Key,Value 只有几个字节,而且没有启用 Pipeline。它回答的是“当前机器在某个简单模型下能跑多快”,而不是“订单系统迁移后能承受多少流量”。(Valkey)

一个更接近实际缓存负载的示例是:

valkey-benchmark \
  -t set,get \
  -r 500000 \
  -d 512 \
  -P 8 \
  -n 2000000 \
  -q

其中:

  • -r 500000 表示使用较大的随机 Key 空间;
  • -d 512 表示 Value 大小为 512 字节;
  • -P 8 表示 Pipeline 深度为 8;
  • -n 2000000 表示执行 200 万次请求。

压测时应保证 Redis 与 Valkey 使用:

  • 相同硬件;
  • 相同数据集;
  • 相同客户端数量;
  • 相同 Pipeline 深度;
  • 相同持久化策略;
  • 相同网络环境;
  • 相同预热时间;
  • 相同测试时长。

最终至少比较以下指标:

指标 关注原因
吞吐量 判断整体处理能力
P50 观察典型请求体验
P95、P99 发现排队和长尾延迟
最大延迟 发现持久化、扩容和重哈希抖动
CPU 判断主线程或 I/O 线程瓶颈
内存 评估真实容量变化
淘汰数量 判断缓存是否接近上限
错误率 发现连接和超时问题
复制延迟 判断高可用风险

需要特别注意,Pipeline 越深,吞吐量通常越高,但单条请求等待时间也可能增加。不能为了得到更漂亮的 QPS,使用远高于生产客户端的 Pipeline 深度。(Valkey)

十一、哪些项目值得迁移,哪些项目不必着急

比较适合评估 Valkey 的场景

  • 当前运行 Redis OSS 6.x 或7.2;
  • 大量存储短字符串和会话数据;
  • 排行榜使用大量 Sorted Set;
  • 对开源治理和许可证有明确要求;
  • 希望降低小对象内存开销;
  • 希望获得更细粒度 ACL;
  • 需要结构化 JSON 日志;
  • Cluster 扩缩容和槽位迁移频繁;
  • 愿意投入时间完成真实负载测试。

Valkey 9.0 已引入原子槽位迁移,9.1 又在命令行工具中增加相关支持,可以降低集群重新分片过程中部分 Key 已迁移、部分 Key 尚未迁移带来的复杂状态。(Valkey)

不建议立即迁移的场景

  • 当前系统长期稳定,负载和成本都没有压力;
  • 使用 Redis CE 7.4 及以后版本;
  • 严重依赖 Redis 新版本特有功能;
  • 使用大量商业模块或私有模块;
  • Lua 脚本和客户端行为缺少测试;
  • 没有预发布环境;
  • 没有监控、备份和回滚方案;
  • 迁移目的只是追求官方 QPS 数字。

数据库迁移本身不会直接产生业务价值。只有当它能够解决许可证、成本、性能、安全、扩容或运维问题时,迁移才值得承担风险。

十二、结语

Valkey 9.1 最值得关注的,并不是单实例每秒 210 万次请求这个数字。

真正有价值的是它背后的工程变化:

  • 小对象布局减少内存浪费;
  • I/O 线程模型提高多核利用率;
  • 主线程指标减少 CPU 误判;
  • JSON 日志降低采集成本;
  • 数据库级 ACL 收紧权限边界;
  • TLS 热更新降低证书轮换风险;
  • 新命令减少应用层多步骤操作;
  • 集群工具逐渐补齐复杂运维能力。

对于 Redis OSS 7.2 及以前版本的项目,Valkey 已经是一个值得认真评估的生产选项。但“协议兼容”并不代表“迁移零风险”,真正可靠的升级仍然需要版本盘点、数据验证、灰度切换、真实压测和明确的回滚窗口。

最稳妥的迁移,不是把 Redis 地址改成 Valkey 地址后直接上线。

而是先证明旧功能没有变化,再决定是否使用新能力。

0 条评论
如果你觉得文章对你有帮助,那就请作者喝杯咖啡吧☕
微信
支付宝
  0 条评论