第 2 章

开源竞品全景:Memcached、Aerospike、Dragonfly、Valkey、KeyDB

第二章:开源竞品全景:Memcached、Aerospike、Dragonfly、Valkey、KeyDB

2.1 功能对比矩阵

在深入每个竞品之前,先用一张完整的对比表建立宏观认知:

维度 Redis 7.2 Memcached 1.6 Aerospike 6.x Dragonfly 1.x Valkey 7.2 KeyDB 6.x
数据结构 10+ 种 纯 KV KV+List+Map Redis 兼容 Redis 兼容 Redis 兼容
持久化 AOF/RDB 原生 SSD 有(快照) AOF/RDB AOF/RDB
集群 Cluster(哈希槽) 无(客户端分片) 原生集群 原生多主 Cluster 多主复制
线程模型 单线程命令+多线程I/O 多线程 多线程/多核 每核一线程 单线程+I/O多线程 多线程
协议 RESP2/RESP3 Memcached 文本/二进制 私有协议 RESP(兼容) RESP2/RESP3 RESP2
最大容量 内存上限 内存上限 SSD(TB级) 内存上限 内存上限 内存上限
许可证 RSALv2+SSPL BSD Apache 2.0 BSL(源码延迟开放) BSD BSD
生态成熟度 极高 中等 增长中
事务支持 MULTI/Lua Lua MULTI/Lua MULTI/Lua MULTI/Lua
Pub/Sub
Stream 是(兼容)

2.2 Memcached:简单即美德

2.2.1 架构设计

Memcached 是 Brad Fitzpatrick 2003 年为 LiveJournal 开发的,其设计哲学极其简单:一个多线程的内存哈希表,仅支持 GET/SET/DELETE/CAS 操作。

内部使用 slab allocator 管理内存,将内存划分为固定大小的 chunk 集合(slab class):

Slab Class  Chunk Size   Total Slabs   Per-Slab Items
1           96 bytes     1048576       10922
2           120 bytes    1048576       8738
3           152 bytes    1048576       6898
...
42          1 MB         1048576       1
43          Max(1.25MB)  1048576       1

每个 slab class 有独立的空闲列表,分配时从对应 class 的空闲列表取出,释放时归还。优点:无碎片。缺点:固定 chunk 大小导致内存浪费(存储 65 字节的值需要 120 字节的 chunk,浪费 55 字节)。

2.2.2 多线程模型

Memcached 使用 libevent 的多线程模型:

Main Thread (监听连接)
     │
     ├──→ Worker Thread 0 (处理连接 0, 4, 8...)
     ├──→ Worker Thread 1 (处理连接 1, 5, 9...)
     ├──→ Worker Thread 2 (处理连接 2, 6, 10...)
     └──→ Worker Thread 3 (处理连接 3, 7, 11...)

每个 Worker Thread 有自己的 libevent 实例,处理分配给它的连接。当多个线程同时访问哈希表时,需要全局锁(一把大锁保护整个 hash table)或分段锁(每个 hash bucket 一把锁)。

slab allocator 的全局锁是 Memcached 多线程模型的主要瓶颈。在 32-core 机器上,吞吐量提升是亚线性的,大约只有 8-10x(而非 32x)。

2.2.3 适用场景与局限

适用:简单 KV 缓存,数据无需持久化,追求最低延迟,不需要复杂数据结构。

局限

在 Redis 出现后,Memcached 的新项目占有率持续下滑。但对于纯缓存场景(数据可重建、不需要持久化),Memcached 依然是有效的选择,其多线程模型在超高并发下可能比 Redis 单线程更有优势。

2.3 Aerospike:突破内存边界

2.3.1 混合内存架构(HMA)

Aerospike 解决了 Redis 最根本的问题:内存容量限制。其核心创新是混合内存架构:

┌─────────────────────────────────────────────────────────┐
│                     Aerospike Node                      │
│                                                         │
│  ┌─────────────────┐     ┌──────────────────────────┐  │
│  │   DRAM Index    │     │     SSD (Device Layer)   │  │
│  │                 │     │                          │  │
│  │ Key Hash → Ptr  │────→│ Actual Record Data       │  │
│  │ 64 bytes/record │     │ Direct Block I/O         │  │
│  │                 │     │ (Bypass OS Page Cache)   │  │
│  └─────────────────┘     └──────────────────────────┘  │
│                                                         │
│  内存开销:64B × 记录数    数据容量:SSD 总容量           │
└─────────────────────────────────────────────────────────┘

关键技术细节

每条记录在 DRAM 中只存 64 字节的索引:

/* 简化的 Aerospike 主索引结构 */
struct as_index {
    uint8_t  key_hash[20];   // RIPEMD-160 哈希
    uint8_t  set_id[2];      // set 编号
    uint64_t rblock_id;      // SSD 上的 record block ID
    uint16_t n_rblocks;      // 占用的 block 数量
    uint32_t generation;     // 版本号(CAS 使用)
    uint32_t void_time;      // TTL 过期时间
    uint8_t  replication_state; // 副本状态
    // ... 共 64 字节(经过 bit packing)
};

绕过文件系统:Aerospike 直接对 SSD 设备做原始块读写(O_DIRECT),跳过操作系统页面缓存。原因:页面缓存按 4KB 页管理数据,对随机小记录(< 1KB)效率低;O_DIRECT 允许 512-byte 对齐的直接 I/O,更适合 SSD 的物理访问模式。

实测数据:在 NVMe SSD 上,Aerospike 的随机读延迟约 0.5-1ms,接近 DRAM 访问延迟的 10-20x,但远低于旋转磁盘的 5-10ms。

2.3.2 Smart Client

Aerospike 的客户端库(Smart Client)包含集群拓扑感知能力:

传统代理架构:
客户端 → Proxy → 数据节点

Aerospike Smart Client:
客户端 → 直接路由 → 正确的数据节点(无代理层)

客户端在启动时获取集群分区映射,将记录哈希值直接映射到对应节点。这消除了代理层的单点故障和延迟开销(通常省去 0.1-0.5ms)。

2.3.3 强一致性 CP 模式

Aerospike 支持两种一致性模式:

Availability 模式(AP):类似 Redis,异步复制,高可用,可能读到旧数据。

Strong Consistency 模式(CP):基于写 quorum(类 Paxos):

写操作流程:
1. 客户端发送写请求到主节点
2. 主节点将记录写入本地 SSD
3. 主节点并发发送复制请求到所有副本节点
4. 等待 (RF/2 + 1) 个节点确认(RF=Replication Factor)
5. 主节点返回 ACK 给客户端

RF=3 时,需要 2 个节点确认(容忍 1 节点故障)。写延迟高于 AP 模式,但保证不丢数据。

2.3.4 适用场景与 Redis 对比

场景 选 Aerospike 选 Redis
数据量 >> 内存
广告竞价(RTB)用户画像
简单 KV,数据量在内存内
复杂数据结构(List/Stream)
需要 Pub/Sub / 消息队列
毫秒以下延迟 ✗(SSD 读 0.5ms+) ✓(< 0.1ms)

典型案例:某广告平台存储 20 亿条用户画像,每条记录约 500 字节,总数据量约 1TB。用 Redis 需要 1TB 内存(约 $20,000/月云费),用 Aerospike 只需 128GB 内存 + 2TB NVMe SSD(约 $3,000/月),成本差 6-7 倍。

2.4 Dragonfly:重新设计 Redis

2.4.1 shared-nothing 架构

Dragonfly 于 2022 年公开,声称性能是 Redis 的 25 倍。其核心架构是 shared-nothing

CPU Core 0    CPU Core 1    CPU Core 2    CPU Core 3
     │              │              │              │
  Thread 0       Thread 1       Thread 2       Thread 3
     │              │              │              │
  Shard 0       Shard 1       Shard 2       Shard 3
(key space      (key space    (key space    (key space
partition 0)    partition 1)  partition 2)  partition 3)

每个线程独占一部分 key 空间(通过 key hash 确定归属 shard)。线程间无需竞争锁,因为每个 key 只属于一个 shard。跨 shard 操作(如 MGET 涉及多个 shard 的 key)通过内部消息传递协调。

这与 Redis 的单线程命令处理形成鲜明对比:Dragonfly 的吞吐量随 CPU 核心数线性扩展。

2.4.2 dashtable:无锁哈希表

Dragonfly 使用自研的 dashtable(dash = dynamic array of segments with hash)替代传统哈希表:

传统哈希表的问题:resize 时需要全量 rehash,期间需要锁定整个表。Redis 通过渐进式 rehash(每次操作时搬移少量桶)绕过了这个问题,但仍需要在旧表和新表之间维护状态。

dashtable 的设计:

2.4.3 性能数据与局限

官方 benchmark(AWS c6gn.12xlarge,48 vCPU,192GB RAM):

Redis 7.0(单实例):   ~800,000 SET/s
Dragonfly 1.0(单实例):~20,000,000 SET/s(25x)

注意:这个对比有前提条件——Redis 是单核利用率,Dragonfly 利用了全部 48 vCPU。与 Redis Cluster(48 节点)相比,差距缩小到 2-3x。

局限

  1. 源码闭源商业化:Dragonfly 使用 BSL(Business Source License)。源码公开,但商业使用需付费或等待 4 年后转 Apache 2.0。
  2. 生态不成熟:社区规模远小于 Redis,生产故障案例少,踩坑经验缺乏。
  3. 部分兼容性问题:声称 Redis 协议完全兼容,但边缘 case(如 Lua 脚本复杂操作、某些 RESP3 特性)存在已知不兼容。
  4. Cluster 支持有限:Dragonfly 单实例吞吐量高,但分布式模式下的行为与 Redis Cluster 有差异。

2.5 Valkey:开源社区的分叉选择

2.5.1 许可证风波

2024 年 3 月 20 日,Redis Inc. 宣布将 Redis 的开源许可证从 BSD-3 改为双许可证:

2024 年 3 月 21 日,Linux Foundation 宣布接管 Redis 7.2.4 的 BSD fork,命名为 Valkey

2.5.2 Valkey 的发展现状

2024-03-21  Linux Foundation 宣布接管
2024-03-27  AWS、Google、Oracle、Alibaba、Ericsson 宣布加入
2024-04-16  Valkey 7.2.5 发布(首个正式版本)
2024-06-11  Valkey 8.0.0-rc1 发布,开始引入多线程优化
2024-10-01  AWS ElastiCache 默认引擎切换为 Valkey

Valkey 8.0 的重点改进:

2.5.3 Redis vs Valkey 现在该如何选

对于新项目:

对于存量 Redis 项目:Valkey 与 Redis 7.2 协议完全兼容,迁移零改动。

2.6 KeyDB:多主多写架构

KeyDB 是 Snap(Snapchat 母公司)2019 年开源的 Redis fork(2022 年被 Snap 收购后改为 EX Inc.)。

2.6.1 核心差异:per-thread event loop

Redis(以及 Valkey)的网络 I/O 线程只负责读写,命令执行仍在主线程。KeyDB 将整个事件循环(包括命令解析、执行)都多线程化:

KeyDB 线程模型:
Thread 0: Event Loop 0 → 处理连接 [0..N/K] 的完整生命周期
Thread 1: Event Loop 1 → 处理连接 [N/K..2N/K]
Thread 2: Event Loop 2 → 处理连接 [2N/K..3N/K]
...

线程间通过 MVCC(多版本并发控制)机制协调对共享数据结构的访问。

2.6.2 多主多写(Active-Active Replication)

KeyDB 支持多个主节点同时接受写入,节点间互相同步,解决了 Redis 单主写入的瓶颈:

              写入                写入
Client ──────→ Node A ←──────── Client
                 ↕(双向同步)
               Node B

写冲突通过 last-write-wins(LWW)或向量时钟解决。适合写多读多、需要多机房双写的场景。

KeyDB 的实际处境:KeyDB 的商业化前景不明朗,社区活跃度低于 Valkey。对于新项目,Valkey 是更稳妥的选择。

2.7 选型决策树

需要键值存储?
├── 数据量 >> 可用内存(> 70%)?
│   ├── 是 → Aerospike(HMA 混合存储)
│   └── 否 → 继续
│
├── 需要复杂数据结构(List/Stream/ZSet)?
│   ├── 是 → Redis/Valkey(生态最丰富)
│   └── 否 → 继续
│
├── 纯缓存,无需持久化,高并发?
│   ├── 是 → Memcached(多线程,简单高效)
│   └── 否 → 继续
│
├── 要求最高吞吐量,单节点?
│   ├── 是,可接受 BSL 许可 → Dragonfly
│   └── 否 → 继续
│
├── 需要多主多写?
│   ├── 是 → KeyDB 或 Redis Enterprise(CRDT)
│   └── 否 → 继续
│
├── 云托管优先?
│   ├── AWS → ElastiCache for Valkey / MemoryDB for Valkey
│   ├── 阿里云 → Tair(增强版,特殊场景)
│   └── 其他 → 各云厂商的 Redis/Valkey 托管服务
│
└── 默认选择:Valkey(最佳开源选择,BSD 许可证,社区活跃)

2.8 小结

Redis 的竞品并非在全面超越 Redis,而是在特定维度上做出不同权衡:

许可证变更事件深刻影响了整个行业:云厂商正在将默认引擎切换到 Valkey,未来 2-3 年 Valkey 可能成为新项目的首选。对于存量 Redis 项目,迁移成本几乎为零。

下一章将深入云厂商的托管服务:ElastiCache、MemoryDB 与阿里云 Tair 的技术细节与选型决策。

本章评分
4.6  / 5  (108 评分)

💬 留言讨论