亚马逊AWS官方博客
推陈出新 – 内存 key-value 数据库 Valkey 介绍和剖析
![]() |
2024 年 3 月 20 日,Redis Labs 宣布从 Redis 7.4 开始,将 BSD-3-clause 源码使用协议修改为 RSAv2 和 SSPLv1 协议。该变化意味着 Redis 在 OSI(开放源代码促进会)定义下不再是严格的开源产品。实际上,Redis 项目的贡献除了 Redis Labs 的成员外,还有多位来自于其他公司的成员,贡献比例逐年上升,今年甚至达到了 50% 左右。这些核心成员对 Redis Labs 更改 Redis 协议的变动并不认可,所以由 Linux 基金会组织成立了 Valkey 开源项目。Valkey 采用 BSD 源码使用协议,在 2024 年 4 月 16 日推出了 7.2.5 版本,该版本从 Redis 7.2.4 fork 而来,可以帮助用户无缝完成切换。
自成立至今,Valkey 项目蓬勃发展,包括亚马逊云科技在内的 40 多家公司持续投入,并贡献了很多性能和功能的改进。2024 年 10 月 2 日,Valkey 8.0.1 版本正式发布。与此同时,亚马逊云科技也增加了 Valkey 的托管数据库版本,于 2024 年 10 月 8 日推出了 Amazon ElastiCache/MemoryDB for Valkey 7.2 的托管数据库版本,并降低了价格;2024 年 11 月 21 日,推出了 Amazon ElastiCache for Valkey 8.0 的托管数据库版本。
无论是对托管数据库成本上的降低,还是开源 Valkey 性能上的改进和功能上的创新,Valkey 都给您提供了更加丰富的选项来升级迭代您的内存数据库或缓存系统。因此,本篇博客聚焦于介绍 Amazon ElastiCache/MemoryDB for Valkey 的变化,深入剖析 Valkey 8.0 的性能和功能增强,并会介绍亚马逊云科技推出的 Valkey/Redis 开源客户端 Valkey Glide,以及分享如何从 Redis 切换到 Valkey。
Amazon ElastiCache/MemoryDB for Valkey
Amazon ElastiCache 是亚马逊云科技推出的缓存数据库,能够提供微秒级读写延迟,单集群可以扩展到 500 个节点。目前,Amazon ElastiCache 已经在所有区域支持了 Valkey。与其它引擎相比,Amazon ElastiCache for Valkey 的节点版价格降低了 20%,Serverless 版价格降低了 33%。
Amazon MemoryDB 是亚马逊云科技推出的内存型数据库,相较于 ElastiCache 提供了持久化支持,写入内存的每条数据都会持久化到跨 3 个可用区的存储系统中。它能够提供微秒级读延迟以及个位数毫秒级别的写延迟,同样可以扩展到 500 个节点。用户可以直接将 MemoryDB 作为持久化的数据库单独使用,游戏行业的排行榜、金融用户的 K 线图存储等均可以考虑 MemoryDB。目前,Amazon MemoryDB 也已经在所有区域支持了 Valkey。与其它引擎相比,Amazon MemoryDB for Valkey 的价格比 Amazon MemoryDB for Valkey OSS 降低了 30%。此外,每月写入到 Amazon MemoryDB for Valkey 的前 10TB 不再收费,超过 10TB 的数据写入量价格相比 Amazon MemoryDB for Redis OSS 也降低了 80%。因为 Valkey 7.2 是从 Redis7.2 fork 而来,直接切换到 Valkey 引擎可以带来成本的节约。
Amazon ElastiCache 于 2024 年 10 月推出了灵活 RI,客户不用再受限于单个固定型号的机型(比如 r7g.xlarge),相反,可以在同一个实例类型下通用。而且,ElastiCache for Redis 的灵活性 RI 可以无缝切换到 ElastiCache for Valkey。因为 ElastiCache for Valkey 的单价低于 ElastiCache for Redis,已经购买 RI 的客户可以使用相同的 RI 应用到更多的 Valkey 节点。
2024 年 11 月 21 日,ElastiCache 支持了 Valkey 8.0 的托管引擎。主要有两个提升:1. 提升了 ElastiCache Serverless for Valkey 的扩展速度,较 ElastiCache for Valkey 7.2 扩展速度提升了 5 倍,可以实现 13 分钟内完成从 0 到 5 百万 RPS 的扩展; 2. 利用了 Valkey 8.0 内存节约的部分,能够在存储相同数据量时节约近 20% 的内存容量。
开源 Valkey 8.0 特性介绍
Valkey 7.2 直接从 Redis7.2.4 fork 出来, Valkey 8.0 里添加了很多改进,包括提升性能、提升内存利用率、加快复制效率、增强 resharding 过程中高可用性,以及提升集群的观测性等。本部分进行逐一展开。
性能提升
Redis 的核心是单线程处理客户端的 SET/GET 请求,但现在大多数机器都是多核的。为了利用到多核,减少单线程成为瓶颈,亚马逊云科技一直在 ElastiCache 上进行性能提升改进,包括:1)2019 年,ElastiCache for Redis 5.0.3 推出 IO 线程,将主线程部分工作释放到 IO 线成完成;2)2021 年,ElastiCache for Redis6.2 优化了 IO 线程,将开启 TLS 的集群加解密和连接处理操作从主线程移到 IO 线程;3)2023 年,ElastiCache for Redis 7.0 提出了 IO 线程多路复用,同一个 IO 线程处理的多个客户端的连接请求以类似于 pipeline 的机制同主线程交互;4)2023 年,ElastiCache for Redis 7.1 进行了 IO 线程的进一步优化,将输入请求和输出结果的表示层信息推送到 IO 线程进行处理,同时提出了内存访问摊销的机制,加速 GET 的处理。
2024 年 9 月,亚马逊云科技将以上 IO 线程相关的优化贡献到开源 Valkey 8.0 中,使单点 Valkey 8.0 达到了 1.2M 的 RPS 的处理能力。主要包含两个部分的改进:IO 多路复用和内存访问摊销。
Valkey 允许用户设置 io-threads 数目来指明所有的线程数目,下图对应线程数目为 3 的情况,即 1 个主线程和 2 个 IO 线程。主线程会处理核心的 SET/GET 工作(Process Commands),同时在执行 GET 操作时会利用预取机制去尽可能读取更多的 key(Prefetch Commands Keys)。主线程负责给 IO 线程分配任务,比如和网络交互进行读取(Read)、写入(Write)、释放相应对象(Free Objects)等。对于调用 socket 的系统调用,比如 epoll_wait,主线程如果正在忙于处理读写请求时,也会将 EPOLL 任务分发给一个 IO 线程进行处理。
![]() |
Valkey 7.2/Redis 在内存中的数据结构实际是一个全局的数据词典,词典的每个 hash bucket 存在一串 item,即具体的数据值。主线程在处理 GET 请求时,需要从 Redis 的词典表中找到相应的 key(lookupKey),然而 Redis 的链表组织结构使得主线程很难根据上一次的访问来预测出下一个要访问的 key,因此主线程对内存的访问通常需要顺序来执行,耗时比例较高。对此,Valkey 8.0 做了相应的优化,会通过 dictPrefetch 机制,将多个 IO 线程的读取请求合并到一起并行处理,并把结果放到主线程对应 CPU 核的 L1/L2 缓存中,减少了 Redis 主线程针对每个命令都要进行内存调用的消耗,提高了性能。
![]() |
内存访问空间优化
对内存数据库而言,内存的占用量是用户进行数据库机型选型的重要衡量指标之一。Valkey 8.0 针对内存的使用进行细致的指针级别的优化,在某些情况下可以达到 20% 左右的内存节约。它对内存访问空间的节约主要体现在两个方面:Valkey 每个 item 中存储的指向 key 实际内容的指针,以及指向同一 slot 的前、后 item 的指针。
Valkey 7.2/Redis 在进行每条 item 处理的时候,会有三个指针,key 指针指向实际的 key 存放区域,value 指针指向实际的 value 存放区域,next 指针指向下一条 item。实际上,key 自身内容和指针实际生命周期是一致的,而且 key 通常比较短,所以 Valkey 8.0 将 key 的指针移除,将 key 的内容直接存放在 item 的部分,省掉了 8 个字节的存储空间。
![]() |
Valkey 7.2/Redis 集群模式有 16384 个 slot,为方便进行 slot 的操作,slot 中的每个 item 都会包含两个指针,分别指向该 slot 中前一个和后一个 item。Valkey 8.0 在 slot 上做了相应的优化,不再维护一个整体的全局词典,相反为每一个 slot 维护单独的词典。这样,在 slot 中的每个 item 就不需要再额外存储指向前后 item 的指针,即节约了 16 个字节。当然,从 1 个词典转变成 16384 个词典也会增加一些额外的元信息存储,但相对于每个 key 上存放的 16 字节的减少,对于数据量较多的情况,能达到比较可观的内存占用量节约。
![]() |
![]() |
Valkey 7.2 | Valkey 8.0 |
复制优化
在 Valkey/Redis 进行主、从节点同步时,有时需要进行全量同步操作。比如有新的从节点加入,或者从节点和主节点复制延迟过大而主节点记录变更 buffer 没有全部更新数据时,都会触发新的全量同步操作。Valkey 8.0 通过双通道传输的机制,优化了全量同步,在某些情况下能降低 50% 全量同步的时间,并减少 60% 的复制数据内存。
全量同步时,主节点会生成 RDB 文件,同时会将生成 RDB 过程中的增量更改信息存储在专门的 client output buffer 中。RDB 生成完毕后,主节点会将 RDB 文件传输给从节点,从节点拿到 RDB 文件并 apply 后,会继续从主节点读取 client output buffer 中的增量日志。Valkey 7.2/Redis 在生成 RDB 文件过程中采用的 Copy-on-Write 的机制,即先 fork 一个进程,指向相同的内存空间。在生成 RDB 文件过程中,如果前端应用修改了部分数据,Valkey/Redis 会对修改的数据页进行一个拷贝,并在拷贝上进行修改。因此,全量同步的时间会影响 Redis 主节点的性能。
Valkey 8.0 提出了双通道复制机制来分别复制 RDB 和增量日志,同时将 client output buffer 从主节点放到从节点去存放。这样的改进使得主节点在进行 RDB 文件生成的过程中,就可以将增量日志发送给从节点,将顺序传输的方式改成了并行传输,加快了同步速度。此外,从节点存放 client output buffer,能够释放写节点的资源,避免 buffer 过载,并使主节点更好地应对前端应用的负载。此外,同步速度加快,也能减少 buffer 自身的大小,同时减少主节点 COW 的时间,降低对前端应用的影响。
提升 resharding 重分片过程中集群可用性
Valkey 的集群模式将数据分成多个分片,随着数据量或者数据访问模式的变化,用户有时需要进行 re-sharding,即重分片。在 re-sharding 过程中,需要将 slot 的所有权从源分片转移到目标分片,其中一个核心的命令是 CLUSTER SETSLOT。Re-sharding 对于一个 slot 的操作大致过程如下:
- 设置 slot 的目标分片节点为 importing 状态。调用命令 CLUSTER SETSLOT <slot> IMPORTING <source-node-id>
- 设置 slot 的源分片节点为 migrating 状态。调用命令 CLUSTER SETSLOT <slot> MIGRATING <destination-node-id>
- 从源分片节点依次读取 slot 中 key 的信息。调用命令 CLUSTER GETKEYSINSLOT ,拿到 slot 中的 keys,而后调用MIGRATE 命令将其发送到目标分片节点
- 通知目标分片节点更改 slot 所有权。调用命令 CLUSTER SETSLOT <slot> NODE <destination-node-id>
- 通知源分片节点更改 slot 所有权。调用命令 CLUSTER SETSLOT <slot> NODE <destination-node-id>
- (可选)通知集群其他主节点更改 slot 所有权。调用命令 CLUSTER SETSLOT <slot> NODE <destination-node-id>
在 Valkey 7.2/Redis 中,所有的 CLUSTER SETSLOT 命令是和处理其它命令一样,只发送给分片的主节点,而后异步复制到从节点。但是如果在元信息同步的过程中,主节点发生故障而此时从节点还没有收到对应的元信息,就有可能出现slot所有权信息丢失的情况,带来 slot 无法访问的问题。比如在上述第 4 步和第 5 步结束之后,目标分片主节点发生了故障,而目标分片从节点还没有收到信息,源分片也认为转移所有权已经成功,就有可能触发此问题。
Valkey 8.0 对此进行了优化,会将 SETSLOT 的命令在主节点执行之前先发送给从节点,即完成了强同步操作,避免了信息的丢失。此外,在发生 failover 时,Valkey 8.0 会自动在源和目标节点上更新 slot 迁移的信息,进而保证了应用请求能够持续发送到目标分片中,来提升集群的完整性和可用性。Valkey 8.0 还针对新的空的分片做了相应优化。
提高可观测性 – Slot 级别指标
数据库良好的可观测性可以为用户提供对数据库更好的洞察,比如消耗的 CPU、网络资源,比如慢查询、比如频繁访问的数据量等。对于 Valkey/Redis 的用户而言,除了观测到每个节点的负载外,如果能够有更细粒度的监控信息,能够更高效地进行性能调优,进行针对性的优化。
Valkey 8.0 提供了 slot 级别的指标信息,能够提供 slot 级别 key 的数目,slot 级别 CPU 的使用量、网络的吞吐量等。这样,在用户发现较热的节点后,可以对访问量比较密集的 slot 进行针对性的调优,比如拆分 slot,重新设置 key 的定义规则(比如通过 hashtag 的方式)来进行 key 的重新分布等。添加更多的监测信息也会一定程度上影响 Valkey 的性能。经评估, slot 级别的监测信息增加大致对 RPS 带来了 0.7% 的性能影响,还在一般可以接受的范围。Slot 级别的监测信息可以通过如下命令进行查看:
其余更新
除了上面提到的性能、内存节约、复制效率提升、可用性、观测性等更新外,Valkey 8.0 还做了一些其它的更新,比如
- 支持 Dual IPv4 and IPv6 Stack:支持 IP 混合环境,提升兼容性和灵活性。
- 提升 Pub/Sub 效率:集群内消息以 streamline 方式传递,降低了 overhead,提升 Pub/Sub 操作效率。
- Valkey Over RDMA(Experimental):客户端和 Valkey 服务器间采用 RDMA 技术,吞吐量提升 275%。
- 其他小的性能/稳定性提升
值得注意的一点是,Valkey 8.0 对单点能力的提升,可以带来额外的好处:1)可以增加整个集群的容量,来支持更多的请求高峰,降低 P99 延迟等;2)对应用程序负载不容易通过 sharding 来进行水平扩展的场景,比如热 key 和大的 collection等操作,适配能力更强;3)单点存放更多的数据,可以更好支撑在同一节点内部的 MGET 等跨多个 key 操作。
Valkey – Glide: Valkey 客户端
Valkey 7.2 是从 Redis 7.2.4 fork 而来,在配置环境、API 和命令、RESP 协议、客户端支持方面与 Redis7.2.4 相比均没有变化,只是额外增加了对 Valkey 相关命令的支持,比如 valkey-benchmark、valkey-cli 等等,所以已有的 Redis 客户端均可以连接到 Valkey。经实测,Jedis、redis-benchmark、go-redis、redis-py、K6 等连接 Valkey 均没有问题。
然而对于 Redis 的客户端,现在有支持 50 多种不同语言的 200 多个功能参差不齐的客户端,用户在使用客户端连接到 Valkey/Redis 时也经常遇到问题。大致有以下几类:
- 集群更改时,比如 failover、升级、重分片后,客户端不能及时刷新,不能优雅重连。有时用户必须重启应用程序来手动重连。但应用程序通常部署在多个 Pod 上,经常需要管理整体的重启机制。
- 同一公司的多个业务团队因熟悉的编程语言不同而选择不同的 Redis 客户端,然而不同客户端支持的功能集可能不同,造成业务团队对 Redis 能力的理解不同,对运维团队的诉求不同。
- 希望更好的利用到主从分片的从节点,支持读写分离,同时减少对主节点的压力。
亚马逊云科技投入了很多精力贡献到开源的客户端中,也在官方文档上列出了使用一些客户端连接 ElastiCache 的最佳实践,但仍然发现 ElastiCache 的用户有很多客户端使用的痛点。针对此,亚马逊云科技研发并开源了 Valkey Glide 客户端,来解决这些痛点。Valkey Glide 支持连接到 Valkey 7.2 和 8.0 版本,也支持连接到 Redis 6.2、7.0 和 7.2 版本,目前支持三种语言:Java、Python 和 Node.js。
Valkey Glide 提供主要功能有:
- 感知拓扑变化。Valkey Glide 能及时发现 Valkey/Redis 集群拓扑变化,通过重连机制等提高应用程序的可用性。此外,Valkey Glide 通过大多数规则算法,查询几个节点来决定集群拓扑信息,避免 CLUSTER 命令风暴。
- 读写分离。 支持 PRIMARY(一致从写节点读)和 PREFER_REPICA (读从节点,从节点不可用情况下,读写节点)。
- 自动 pub/sub 重新订阅。拓扑变化引发断联时,比如集群升级时,会自动将连接重定向到新节点,减轻应用程序负担。
- Valkey Glide 1.2 版本支持了一些额外功能,比如:1)自动将流量发送到和客户端在同一个可用区的 Valkey 节点,减少跨可用区沟通消耗并降低访问延迟;2)支持 JSON 类型数据处理;3)支持 MemoryDB 的向量存储。
此外,Valkey Glide 实现的机制是采用 Rust 作为核心,外层针对不同语言进行不同的封装。Rust 语言本身能够提供高效的处理能力。基于同一个内核,能够在不同语言的封装上提供一致的功能,解决同一公司内不同业务团队使用不同编程语言的诉求。
![]() |
迁移到 Valkey
涉及到数据库的迁移通常包括两个部分:1)数据的迁移;2)应用程序的适配性。如前所述,已有的应用程序无需更改可以直接连接到 Valkey 集群。本节聚焦在数据的迁移部分。
如果是托管数据库的数据迁移,您可以在 ElastiCache 控制台上支持直接修改 ElastiCache for Redis 的引擎为 Valkey。后台处理的 Workflow 流程与更改 Redis 版本或者更改实例类型相同。如果您使用能够自动重连的客户端比如 Valkey Glide,对应用程序的影响几乎可以忽略不计。
![]() |
如果是在自建 Redis 或者其他提供 Redis 接口的数据到 Valkey 的迁移,您可以复用 Redis 的工具进行迁移。全量迁移,可以采用 RDB 文件导出再导入的方式;增量迁移,可以采用一些开源的软件,比如 RedisShake、RIOT 都可以顺利完成到 Valkey 的迁移。
结语
本篇文章介绍了 Valkey 项目的来历,Amazon ElastiCache/MemoryDB 对 Valkey 的托管支持,Valkey 8.0 新的特性,包括性能、可用性、可观测性等,支持 Valkey 的客户端 Valkey Glide,以及迁移到 Valkey 的方法等,希望能帮助您更好地了解 Valkey。
Valkey 项目是 Linux 基金会支持,支持开放的 BSD 协议,目前已经有 40 多家公司加入,也纳入了多个操作系统的安装途径。无论是开源项目的开放性、技术上的先进性,还是托管数据库的便利性、相对 Redis 引擎的成本优势等,Valkey 都是值得考虑的一个选择。后续我们也会继续推出对 Valkey 以及 Valkey Glide 评测的系列文章。