深入解读 Dragonfly:Redis 与 Memcached 的现代内存数据存储替代方案
【免费下载链接】dragonflyA modern replacement for Redis and Memcached项目地址: https://gitcode.com/GitHub_Trending/dr/dragonfly
Dragonfly 是一款为现代应用负载而设计的内存数据存储(in-memory data store),它完全兼容 Redis 与 Memcached 的 API,迁移时无需修改任何业务代码。本文基于当前仓库的 README.md 主文档,结合 docs/quick-start/README.md、docs/differences.md、docs/dashtable.md 与src/server下的源码实现,系统讲解 Dragonfly 的快速上手、配置参数、设计决策与底层架构,读完即可理解它的运行方式并掌握完整的启动与调优方案。
一、Dragonfly 是什么
Dragonfly 是一个专为现代应用负载构建的内存数据存储。它的核心定位是:
- API 级兼容:完全兼容 Redis 与 Memcached 的 API,采用时无需改动代码即可平滑替换;
- 垂直扩展能力强:相比传统单线程内存存储,其吞吐量随实例规格增长而持续提升,同时在同等负载下占用更少的计算与内存资源;
- 低尾延迟:在保持亚毫秒级延迟的同时支撑极高的吞吐。
从当前仓库源码看,Dragonfly 已实现约 185 条 Redis 命令(大致对应 Redis 5.0 的 API 面)与 13 条 Memcached 命令,命令族实现分布在 src/server 下(如 string_family.cc、generic_family.cc、list_family.cc 等)。也就是说,绝大多数基于 Redis/Memcached 的现有客户端与业务代码可以直接切换过来。
二、三分钟快速开始
Docker 是体验 Dragonfly 最快捷的方式。以下是不同平台下的启动命令(详见 docs/quick-start/README.md)。
Linux 环境(使用 host 网络直通,性能表现最佳):
docker run --network=host --ulimit memlock=-1 docker.dragonflydb.io/dragonflydb/dragonflymacOS 环境(network=host在 macOS 上存在兼容问题,改用端口映射):
docker run -p 6379:6379 --ulimit memlock=-1 docker.dragonflydb.io/dragonflydb/dragonflyWindows 环境(通过 WSL 容器运行):
wslc run -p 6379:6379 --ulimit memlock=-1 docker.dragonflydb.io/dragonflydb/dragonfly启动后,Dragonfly 会同时响应http与redis两种协议请求。你可以用redis-cli连接localhost:6379,也可以直接在浏览器中访问http://localhost:6379。部分机器上如果出现初始化错误,可以尝试追加--privileged标志运行。
用 redis 客户端验证读写:
redis-cli 127.0.0.1:6379> set hello world OK 127.0.0.1:6379> keys * 1) "hello" 127.0.0.1:6379> get hello "world"除了 Docker,仓库还提供了多种部署方式:Docker Compose 配置见 contrib/docker,Kubernetes Helm Chart 见 contrib/charts/dragonfly。
三、基准测试表现
3.1 与 Redis 对比
官方基准使用memtier_benchmark在 AWS 上执行,压测程序运行在同一可用区(AZ)的另一台负载测试实例(c5n)上,命令为:
memtier_benchmark -c 20 --test-time 100 -t 4 -d 256 --distinct-client-seed在常见的单线程 Redis 运行实例m5.large上,Dragonfly 表现与 Redis 相当:
| 场景 | Redis | Dragonfly |
|---|---|---|
SET(--ratio 1:0) | QPS 159K,P99.9 1.16ms,P99 0.82ms | QPS 173K,P99.9 1.26ms,P99 0.9ms |
GET(--ratio 0:1) | QPS 194K,P99.9 0.8ms,P99 0.65ms | QPS 191K,P99.9 0.95ms,P99 0.8ms |
这说明 Dragonfly 内部为垂直扩展而设计的算法层在单线程运行时并不会带来明显开销。
换用更强的实例m5.xlarge(memtier_benchmark -c 20 --test-time 100 -t 6 -d 256 --distinct-client-seed)后,两者差距开始拉大:
| 场景 | Redis | Dragonfly |
|---|---|---|
SET(--ratio 1:0) | QPS 190K,P99.9 2.45ms,P99 0.97ms | QPS 279K,P99.9 1.95ms,P99 1.48ms |
GET(--ratio 0:1) | QPS 220K,P99.9 0.98ms,P99 0.8ms | QPS 305K,P99.9 1.03ms,P99 0.87ms |
核心结论是:Dragonfly 的吞吐能力随实例规格持续增长,而单线程的 Redis 受 CPU 瓶颈限制会达到性能上限。
在 AWS 网络能力最强的实例c6gn.16xlarge上,官方基准测得 Dragonfly 相比单进程 Redis 吞吐量提升约 25 倍,QPS 突破 380 万。在峰值吞吐下,Dragonfly 的 99 分位延迟如下:
| 操作 | r6g | c6gn | c7g |
|---|---|---|---|
| set | 0.8ms | 1ms | 1ms |
| get | 0.9ms | 0.9ms | 0.8ms |
| setex | 0.9ms | 1.1ms | 1.3ms |
注:所有基准均使用memtier_benchmark执行,线程数按服务器与实例类型调优,memtier运行在独立的 c6gn.16xlarge 机器上;SETEX 基准将过期时间设为 500 秒以确保在测试结束前不失效。基准命令模板:
memtier_benchmark --ratio ... -t <threads> -c 30 -n 200000 --distinct-client-seed -d 256 \ --expiry-range=...在 pipeline 模式(--pipeline=30)下,Dragonfly 的 SET 可达到1000 万 QPS、GET 可达到1500 万 QPS。
3.2 与 Memcached 对比
在 AWS c6gn.16xlarge 实例上,官方对比了 Dragonfly 与 Memcached:
SET 基准:
| 服务器 | QPS(千/秒) | 延迟 99% | 99.9% |
|---|---|---|---|
| Dragonfly | 3844 | 0.9ms | 2.4ms |
| Memcached | 806 | 1.6ms | 3.2ms |
GET 基准:
| 服务器 | QPS(千/秒) | 延迟 99% | 99.9% |
|---|---|---|---|
| Dragonfly | 3717 | 1ms | 2.4ms |
| Memcached | 2100 | 0.34ms | 0.6ms |
结论:在相当延迟水平下,Dragonfly 的读写吞吐均超过 Memcached;写路径上延迟优势更明显,这与 Memcached 写路径的锁竞争有关,详细分析见 docs/memcached_benchmark.md。读基准中 Memcached 延迟更低,但吞吐也更低。
3.3 内存效率
官方测试流程:先用debug populate 5000000 key 1024向两个服务器填充约 5GB 数据,再用memtier发送更新流量,最后执行bgsave触发快照并观测内存。
测试结果表明:空闲状态下 Dragonfly 比 Redis 内存效率高约 30%,且快照阶段内存几乎无可见增长;Redis 在峰值时内存使用量约为 Dragonfly 的 3 倍。Dragonfly 的 snapshot 在数秒内即可完成。更深入的内存效率原理可参考 docs/dashtable.md。
四、配置参数详解
Dragonfly 支持通用的 Redis 参数,例如可以直接运行:
dragonfly --requirepass=foo --bind localhost4.1 Redis 兼容参数
| 参数 | 说明 | 默认值 |
|---|---|---|
port | Redis 连接端口 | 6379 |
bind | localhost仅允许本机连接;公网 IP 允许来自外部的连接;0.0.0.0允许所有 IPv4 | — |
requirepass | AUTH 认证密码 | "" |
maxmemory | 数据库最大内存上限(人类可读字节数) | 0(自动决定) |
dir | 快照保存目录:Docker 默认/data,CLI 默认"",可用 Docker-v映射到宿主机目录 | "" |
dbfilename | 数据库保存/加载文件名 | dump |
其中maxmemory与dir/dbfilename有更细的源码行为:
- 从 src/server/dfly_main.cc 可以看到,当
maxmemory未显式指定时,Dragonfly 会自行探测可用内存并按约 80% 的比例自动设置上限,并输出日志说明; - 从 src/server/server_family.cc 的定义看,
dbfilename默认值为dump-{timestamp},且除{timestamp}外还支持{Y}、{m}、{d}等宏做文件名时间展开,实际运行时快照文件名会包含时间戳; - 从 src/server/dfly_main.cc 可以确认,当显式指定的
maxmemory过小(小于线程数 × 256MB)时会有相关校验逻辑。
4.2 Dragonfly 特有参数
| 参数 | 说明 | 默认值 |
|---|---|---|
memcached_port | 启用 Memcached 兼容 API 的端口 | 禁用 |
keys_output_limit | keys命令最多返回的 key 数量 | 8192 |
dbnum | select支持的最大数据库数量 | 16 |
cache_mode | 启用缓存模式,见下文"新型缓存设计" | false |
hz | key 过期评估频率 | 100 |
snapshot_cron | 自动备份快照的 cron 调度表达式(分钟粒度,标准 cron 语法) | "" |
primary_port_http_enabled | 是否允许在主 TCP 端口上访问 HTTP 控制台 | true |
admin_port | 管理控制台端口(同时支持 HTTP 与 RESP 协议) | 禁用 |
admin_bind | 管理控制台 TCP 绑定地址(同时支持 HTTP 与 RESP 协议) | any |
admin_nopass | 管理端口免认证开放访问 | false |
cluster_mode | 集群模式 | ""(目前仅支持emulated) |
cluster_announce_ip | 集群命令向客户端通告的 IP | — |
announce_port | 集群命令向客户端及复制主节点通告的端口 | — |
snapshot_cron支持的标准 cron 表达式示例:
| Cron 表达式 | 含义 |
|---|---|
* * * * * | 每分钟执行 |
*/5 * * * * | 每 5 分钟执行 |
5 */2 * * * | 每 2 小时的第 5 分钟执行 |
0 0 * * * | 每天 00:00(午夜)执行 |
0 6 * * 1-5 | 周一至周五 06:00 执行 |
keys是危险命令,keys_output_limit的作用是截断结果以避免一次拉取过多 key 导致内存暴涨(定义见 src/server/generic_family.cc)。hz值越低,空闲时 CPU 占用越低,但淘汰/过期速度越慢(定义见 src/server/engine_shard.cc)。cluster_mode、cluster_announce_ip等集群参数的源码定义分别位于 src/server/cluster_support.cc 与 src/server/cluster/cluster_family.cc。
4.3 常用参数启动示例
./dragonfly-x86_64 --logtostderr --requirepass=youshallnotpass --cache_mode=true -dbnum 1 --bind localhost --port 6379 --maxmemory=12gb --keys_output_limit=12288 --dbfilename dump.rdb4.4 参数的三种提供方式
除了命令行 flag,参数还可以通过以下两种方式传入:
--flagfile <filename>:文件中每行列一个 flag,键值对 flag 用等号而非空格分隔,flag 值无需引号;- 环境变量:设置
DFLY_x,其中x为 flag 的准确名称(区分大小写)。
更完整的选项(如日志管理、TLS 支持等)可运行dragonfly --help查看。
五、设计决策
5.1 新型缓存设计(Novel cache design)
Dragonfly 内置一套统一的自适应缓存算法,简单且内存高效。通过--cache_mode=true启用后,只有当内存接近maxmemory上限时,Dragonfly 才会淘汰"未来最不可能被访问"的条目。其 flag 定义见 src/server/engine_shard_set.cc:
ABSL_FLAG(bool, cache_mode, false, "If true, the backend behaves like a cache, " "by evicting entries when getting close to maxmemory limit");从 src/server/db_slice.cc 的构造函数可以看到,cache_mode会作为参数传入DbSlice,直接影响数据库切片层的驱逐行为。配合 Dash 表结构,该淘汰算法实现了零内存开销的缓存策略(详见下文 Dashtable 章节)。
5.2 过期时间与精度权衡
- 过期时间范围被限制在约8 年以内;
- 对于毫秒精度的过期命令(
PEXPIRE、PSETEX等),当期限大于2^28ms时,会被就近取整到秒,误差小于0.001%,对于大范围过期场景可以接受。
若该精度取舍不适合你的场景,可以通过 issue 反馈。Dragonfly 与 Redis 过期语义的详细差异见 docs/differences.md。
5.3 原生 HTTP 控制台与 Prometheus 指标
默认情况下,Dragonfly 在主 TCP 端口(6379)上同时提供 HTTP 访问能力——服务器会在连接建立阶段自动识别 Redis 协议或 HTTP 协议。也就是说,用浏览器访问http://localhost:6379即可打开控制台。
- 访问
:6379/metrics可查看 Prometheus 兼容指标; - 导出的指标与 Grafana 仪表盘兼容,预置配置见 tools/local/monitoring;
- 重要安全提醒:HTTP 控制台仅应在可信网络内访问。如果 Dragonfly 的 TCP 端口对外暴露,建议用
--http_admin_console=false或--nohttp_admin_console禁用它。
六、架构背景与底层原理
Dragonfly 的诞生源于一个 2022 年的实验:如果从零设计一个内存数据存储会是什么样?基于作者们作为云厂商工程师使用内存存储的经验,他们为 Dragonfly 确立了两个关键特性:所有操作的原子性保证,以及在高吞吐下依然保持亚毫秒级低延迟。
6.1 共享无状态架构(Shared-Nothing)
第一个挑战是如何充分利用现代公有云服务器的 CPU、内存与 I/O 资源。Dragonfly 采用shared-nothing 架构:将键空间(keyspace)在线程之间分区,每个线程管理自己独立的字典数据切片,这些切片被称为shard。支撑该架构的线程与 I/O 管理库 Helio 位于本仓库的 helio 目录。
6.2 基于 VLL 的事务框架
为了给多 key 操作提供原子性保证,Dragonfly 借鉴了学术界的最新成果——论文"VLL: a lock manager redesign for main memory database systems",并以此构建事务框架。shared-nothing 架构与 VLL 的结合,使得 Dragonfly 可以在不使用 mutex 或 spinlock的情况下组合出原子的多 key 操作,这是其 PoC 阶段的关键里程碑。
6.3 Dashtable:核心哈希表结构
Dragonfly 的核心哈希表基于论文"Dash: Scalable Hashing on Persistent Memory"(arXiv:2003.07302)设计。虽然该论文聚焦持久内存领域,但其设计思想恰好适配内存存储的需求。Dash 设计让 Dragonfly 保留了 Redis 字典的两大特性:
- 数据增长期间的增量哈希能力;
- 在变更下使用无状态 scan 操作遍历字典的能力。
与此同时,Dash 在 CPU 与内存使用上更高效。基于 Dash,Dragonfly 进一步实现了三个独特能力(详细原理见 docs/dashtable.md):
- TTL 记录的高效过期:利用 Dashtable 的"segment 分段"结构,在 segment 因插入满而需要分裂时顺带扫描并清理过期条目,扫描成本为 O(1),甚至可能因此避免扩容;
- 零内存开销的新型缓存淘汰算法:在字典元数据上直接承载驱逐所需信息,无需额外 LRU/LFU 结构;
- 无 fork 的快照算法:完全异步、无需 fork 子进程,即可维持 point-in-time 快照保证。
从内存账本来看:经典 Redis 字典每个条目平均需要 16-32 字节开销,而 Dashtable 每个条目仅需 6-16 字节开销。由于每个 segment 独立增长,表的内存使用平滑,消除了扩容时的内存尖峰与延迟尖峰。
6.4 与 Redis 的若干差异
迁移时还需注意以下细节(详见 docs/differences.md):
- 字符串长度与索引:字符串大小限制为 256MB;
GETRANGE、SETRANGE等命令的索引应为 [-2147483647, 2147483648] 范围内的有符号 32 位整数;SORT不区分 locale; - 过期 flag 组合:
EXPIRE、PEXPIRE、EXPIREAT、PEXPIREAT允许NX与GT/LT组合(键不存在时按 NX 处理,否则按 GT/LT 单独判定),Redis 会拒绝这些组合; - Lua 版本:使用 2022 年发布的 Lua 5.4.4,因此也支持 Lua 整数。
七、总结
Dragonfly 以"共享无状态 + VLL 事务 + Dash 哈希表"三块基石构建了现代内存数据存储的骨架:垂直扩展的吞吐能力、亚毫秒级低延迟、更优的内存效率,以及无需 fork 的快照与零开销缓存淘汰等创新特性。API 层面完全兼容 Redis 与 Memcached,配合 docs/quick-start、docs/differences.md、docs/dashtable.md 等文档,开发者可以快速评估并迁移现有业务。源码层面的命令族实现(src/server)、Helio 线程库(helio)与测试用例(tests/dragonfly)为深入理解其实现提供了完整素材。
【免费下载链接】dragonflyA modern replacement for Redis and Memcached项目地址: https://gitcode.com/GitHub_Trending/dr/dragonfly
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考