Redis 是一项基础技术,因此我们偶尔会看到人们考虑采用替代架构。几年前,KeyDB 提出了这一点,最近一个新项目 Dragonfly 声称是速度最快的 Redis 兼容内存数据存储。我们相信这些项目带来了许多有趣的技术和想法,值得讨论和辩论。在 Redis,我们喜欢这种挑战,因为它要求我们重申 Redis 最初设计的架构原则(致敬 Salvatore Sanfilippo,也就是 antirez)。
虽然我们一直在寻找机会来创新和提升 Redis 的性能和能力,但我们希望分享我们的观点和一些思考,说明为什么 Redis 的架构仍然是内存中实时数据存储(缓存、数据库及其间的一切)的最佳选择。
因此,在接下来的部分中,我们将重点介绍我们对速度和架构差异的看法,以及与正在进行的比较相关的内容。在本帖末尾,我们还提供了与 Dragonfly 项目相关的基准测试和性能比较的详细信息,我们将在下面讨论这些内容,并邀请您亲自查看和复现。
速度
Dragonfly 的基准测试将独立的单进程 Redis 实例(只能利用单个核心)与多线程的 Dragonfly 实例(可以利用 VM/服务器上的所有可用核心)进行比较。不幸的是,这种比较并不能代表 Redis 在实际世界中的运行方式。作为技术构建者,我们努力准确了解我们的技术与他人技术的比较情况,因此我们进行了我们认为是公平的比较,使用 Dragonfly 团队在其基准测试中使用的最大实例类型 AWS c6gn.16xlarge 进行了一系列性能测试,将 40 分片 Redis 7.0 集群(可以利用大多数实例核心)与 Dragonfly 进行了比较。在我们的测试中,我们看到 Redis 的吞吐量比 Dragonfly 高 18% – 40%,即使仅使用了 64 个虚拟核心中的 40 个。
架构差异
一些背景知识
我们相信这些多线程项目的创建者所做的许多架构决策受到了他们在先前工作中遇到的痛点的影响。我们同意,在多核机器上运行单个 Redis 进程(有时具有数十个核心和数百 GB 内存)并不能充分利用显而易见的可用资源。但这并非 Redis 的设计初衷;这只是许多 Redis 提供商选择运行其服务的方式。
Redis 通过运行多进程(使用 Redis 集群)来实现水平扩展,即使在单个云实例的上下文中也是如此。在 Redis(公司),我们进一步发展了这一概念,构建了 Redis Enterprise,它提供了一个管理层,允许我们的用户大规模运行 Redis,默认启用高可用性、即时故障转移、数据持久化和备份。
我们决定分享一些我们在幕后使用的原则,以帮助人们理解我们认为在生产环境中运行 Redis 的良好工程实践是什么。
架构原则
每个 VM 运行多个 Redis 实例
每个 VM 运行多个 Redis 实例使我们能够
- 使用完全无共享的架构进行垂直和水平线性扩展。与仅进行垂直扩展的多线程架构相比,这将始终提供更大的灵活性。
- 提高复制速度,因为复制是跨多个进程并行进行的。
- 由于新 VM 的 Redis 实例将同时从多个外部 Redis 实例填充数据,从而实现从 VM 故障中快速恢复。
将每个 Redis 进程限制在合理的大小
我们不允许单个 Redis 进程的大小超过 25 GB(运行 Redis on Flash 时为 50 GB)。这使我们能够
- 享受写时复制的好处,而无需在为复制、快照和仅追加文件 (AOF) 重写而 fork Redis 时支付巨大的内存开销。而且,是的,如果您不这样做,您(或您的用户)将付出高昂的代价,如此处所示。
- 由于每个 Redis 实例都保持较小,因此可以快速轻松地管理我们的集群、迁移分片、重新分片、扩展和重新平衡。
水平扩展至关重要
通过水平扩展运行您的内存数据存储的灵活性非常重要。以下是其中几个原因
- 更好的弹性 – 集群中使用的节点越多,集群就越健壮。例如,如果您在 3 节点集群上运行数据集,并且一个节点降级,则有 1/3 的集群性能受损;但如果您在 9 节点集群上运行数据集,并且一个节点降级,则只有 1/9 的集群性能受损。
- 更易于扩展 – 向集群添加额外节点并将数据集的一部分迁移到其中要容易得多,而垂直扩展则需要引入更大的节点并复制整个数据集(并且考虑在这个可能漫长的过程中可能发生的所有糟糕的事情……)
- 逐步扩展更具成本效益 – 垂直扩展,尤其是在云端,成本高昂。在许多情况下,即使您只需要向数据集中添加几个 GB,也需要将实例大小加倍。
- 高吞吐量 – 在 Redis,我们看到许多客户在小数据集上运行高吞吐量工作负载,对网络带宽和/或每秒包数 (PPS) 有很高的要求。考虑一个 1GB 数据集,其用例为 1M+ ops/sec。将其运行在单节点 c6gn.16xlarge 集群(128GB,64 个 CPU,100gbps,每小时 2.7684 美元)上,而不是运行在 3 节点 c6gn.xlarge 集群(每个 8GB,4 个 CPU,最高 25Gbps,每小时 0.1786 美元)上,且成本不到 20%,并且更加健壮?在提高吞吐量的同时保持成本效益并提高弹性,这似乎是这个问题的简单答案。
- NUMA 的现实情况 – 垂直扩展也意味着运行具有多个核心和大容量 DRAM 的双插槽服务器;这种基于 NUMA 的架构非常适合 Redis 这样的多进程架构,因为它更像是小型节点的网络。但 NUMA 对多线程架构更具挑战性,根据我们与其他多线程项目的经验,NUMA 可以将内存数据存储的性能降低高达 80%。
- 存储吞吐量限制 – AWS EBS 等外部磁盘的扩展速度不如内存和 CPU 快。事实上,云服务提供商会根据所使用的机器类别施加存储吞吐量限制。因此,有效扩展集群以避免上述问题并满足高数据持久性要求的唯一方法是使用水平扩展,即增加更多节点和更多网络附加磁盘。
- 临时磁盘 – 临时磁盘是在 SSD 上运行 Redis 的绝佳方式(其中 SSD 用作 DRAM 的替代品,而非持久存储),同时享受基于磁盘的数据库成本并保持 Redis 速度(请参阅我们如何通过 Redis on Flash 实现)。同样,当临时磁盘达到其限制时,扩展集群的最佳方式,在许多情况下也是唯一方式,是添加更多节点和更多临时磁盘。
- 商品硬件 – 最后,我们有许多本地客户在本地数据中心、私有云甚至小型边缘数据中心运行;在这些环境中,很难找到内存超过 64 GB 和 8 个 CPU 的机器,再次强调,唯一的扩展方式是水平扩展。
总结
我们欣赏社区中新一波多线程项目带来的新鲜、有趣的想法和技术。甚至一些概念将来也可能融入 Redis(例如我们已经开始研究的 io_uring、更现代的字典、更策略性的线程使用等)。但在可预见的未来,我们不会放弃 Redis 提供的无共享、多进程架构的基本原则。这种设计提供了最佳的性能、扩展性和弹性,同时还支持内存中实时数据平台所需的各种部署架构。
附录 Redis 7.0 vs. Dragonfly 基准测试详情
基准测试总结
版本
目标
- 验证 Dragonfly 的结果是否可复现,并确定获取这些结果的完整条件(考虑到 memtier_benchmark、操作系统版本等缺少一些配置……),更多详情请参阅此处
- 确定在 AWS c6gn.16xlarge 实例上可实现的最佳 OSS Redis 7.0.0 集群性能,以匹配 Dragonfly 的基准测试
客户端配置
- OSS Redis 7.0 解决方案需要与 Redis 集群建立大量开放连接,因为每个 memtier_benchmark 线程都连接到所有分片
- OSS Redis 7.0 解决方案在使用两个 memtier_benchmark 进程(但在同一个客户端 VM 上)运行基准测试时提供了最佳结果,以匹配 Dragonfly 的基准测试)
资源利用率和最优配置
- OSS Redis 集群在 40 个主分片时取得了最佳结果,这意味着 VM 上有 24 个备用 vCPU。尽管机器未完全利用,但我们发现增加分片数量并没有帮助,反而降低了整体性能。我们仍在调查此行为。
- 另一方面,Dragonfly 解决方案完全利用了 VM 的全部 64 个 vCPU,达到 100% 利用率。
- 对于这两种解决方案,我们改变了客户端配置以获得最佳结果。如下所示,我们成功复制了大多数 Dragonfly 的数据,甚至对于 pipeline 等于 30 的情况,超过了其最佳结果。
- 这意味着我们有可能进一步提高 Redis 所取得的成果。
最后,我们还发现 Redis 和 Dragonfly 都没有受到网络 PPS 或带宽的限制,因为我们已经证实,在使用的两个 VM(客户端和服务器,均使用 c6gn.16xlarge)之间,对于 TCP,payload 约为 300B 时,我们可以达到 > 10M PPS 和 > 30 Gbps。
结果分析
- GET pipeline 1 亚毫秒:
- OSS Redis: 4.43M ops/sec,平均延迟和 p50 延迟均达到亚毫秒。平均客户端延迟为 0.383 毫秒
- Dragonfly 声称达到 4M ops/sec
- 我们成功复现了 3.8M ops/sec,平均客户端延迟为 0.390 毫秒
- Redis vs Dragonfly – Redis 的吞吐量比 Dragonfly 声称的结果高 10%,比我们成功复现的 Dragonfly 结果高 18%。
- GET pipeline 30
- OSS Redis: 22.9M ops/sec,平均客户端延迟为 2.239 毫秒
- Dragonfly 声称达到 15M ops/sec
- 我们成功复现了 15.9M ops/sec,平均客户端延迟为 3.99 毫秒
- Redis vs Dragonfly – Redis 比 Dragonfly 复现的结果好 43%,比 Dragonfly 声称的结果好 52%
- SET pipeline 1 亚毫秒:
- OSS Redis: 4.74M ops/sec,平均延迟和 p50 延迟均达到亚毫秒。平均客户端延迟为 0.391 毫秒
- Dragonfly 声称达到 4M ops/sec
- 我们成功复现了 4M ops/sec,平均客户端延迟为 0.500 毫秒
- Redis vs Dragonfly – Redis 好 19%(我们复现了 Dragonfly 声称达到的相同结果)
- SET pipeline 30
- OSS Redis: 19.85M ops/sec,平均客户端延迟为 2.879 毫秒
- Dragonfly 声称达到 10M ops/sec
- 我们成功复现了 14M ops/sec,平均客户端延迟为 4.203 毫秒)
- Redis vs Dragonfly – Redis 比 Dragonfly 复现的结果好 42%,比 Dragonfly 声称的结果好 99%
每种变体使用的 memtier_benchmark 命令
- GET pipeline 1 亚毫秒
- Redis
- 2X: memtier_benchmark –ratio 0:1 -t 24 -c 1 –test-time 180 –distinct-client-seed -d 256 –cluster-mode -s 10.3.1.88 –port 30001 –key-maximum 1000000 –hide-histogram
- Dragonfly
- memtier_benchmark –ratio 0:1 -t 55 -c 30 -n 200000 –distinct-client-seed -d 256 -s 10.3.1.6 –key-maximum 1000000 –hide-histogram
- GET pipeline 30
- Redis
- 2X: memtier_benchmark –ratio 0:1 -t 24 -c 1 –test-time 180 –distinct-client-seed -d 256 –cluster-mode -s 10.3.1.88 –port 30001 –key-maximum 1000000 –hide-histogram –pipeline 30
- Dragonfly
- memtier_benchmark –ratio 0:1 -t 55 -c 30 -n 200000 –distinct-client-seed -d 256 -s 10.3.1.6 –key-maximum 1000000 –hide-histogram –pipeline 30
- SET pipeline 1 亚毫秒
- Redis
- 2X: memtier_benchmark –ratio 1:0 -t 24 -c 1 –test-time 180 –distinct-client-seed -d 256 –cluster-mode -s 10.3.1.88 –port 30001 –key-maximum 1000000 –hide-histogram
- Dragonfly
- memtier_benchmark –ratio 1:0 -t 55 -c 30 -n 200000 –distinct-client-seed -d 256 -s 10.3.1.6 –key-maximum 1000000 –hide-histogram
- SET pipeline 30
- Redis
- 2X: memtier_benchmark –ratio 1:0 -t 24 -c 1 –test-time 180 –distinct-client-seed -d 256 –cluster-mode -s 10.3.1.88 –port 30001 –key-maximum 1000000 –hide-histogram –pipeline 30
- Dragonfly
- memtier_benchmark –ratio 1:0 -t 55 -c 30 -n 200000 –distinct-client-seed -d 256 -s 10.3.1.6 –key-maximum 1000000 –hide-histogram –pipeline 30
基础设施详情
我们客户端(运行 memtier_benchmark)和服务器(运行 Redis 和 Dragonfly)使用了相同的 VM 类型,以下是规格
- VM:
- AWS c6gn.16xlarge
- aarch64
- ARM Neoverse-N1
- 每个插槽的核心数: 64
- 每个核心的线程数: 1
- NUMA 节点数: 1
- 内核: Arm64 Kernel 5.10
- 安装内存: 126GB