Dragonfly 深入解析:兼容 Redis/Memcached 的高吞吐内存数据存储与核心设计
【免费下载链接】dragonflyA modern replacement for Redis and Memcached项目地址: https://gitcode.com/GitHub_Trending/dr/dragonfly
Dragonfly 是面向现代云应用工作负载的内存数据存储(in-memory datastore),API 层面与 Redis 和 Memcached 完全兼容,应用无需修改代码即可迁移接入。本文基于仓库 README.ko-KR.md(韩文版项目说明)并结合 src/server 下真实源码,系统讲解 Dragonfly 的基准测试表现、命令行配置参数与默认值、缓存/过期/快照等关键设计决策,以及 shared-nothing 架构与 Dash 哈希表等技术背景,帮助读者既能在生产环境直接上手配置部署,也能从源码层面理解其高性能的来源。
图片说明:下图为本仓库 docs/dashtable.md 中记录的 BGSAVE 内存占用对比实验曲线,用于佐证下文“内存效率”一节中快照阶段内存表现的描述;右侧为 Dash 哈希表结构示意图,对应“设计决策”一节中的核心数据结构。
一、项目定位:面向现代工作负载的内存数据存储
Dragonfly 是一个为现代应用工作负载设计的内存数据存储,核心卖点有三条:
- API 完全兼容 Redis 与 Memcached:使用现有客户端和现有命令即可接入,不需要修改应用代码;
- 高吞吐与低尾延迟:相比传统内存数据存储,项目方基准测试显示吞吐量提升显著,且拥有更低的尾部延迟(tail latency);
- 简单高效的内存利用:通过全新的数据结构与缓存算法,在同等工作负载下占用更少的内存资源。
在命令支持方面,项目说明(README.ko-KR.md)指出 Dragonfly 目前已支持约185 个 Redis 命令(大致相当于 Redis 5.0 API 的覆盖面)以及全部 Memcached 命令(含cas)。具体支持的命令清单可在项目的命令参考文档中查询;仓库内 fuzz/seeds 目录下的 resp / memcache 种子文件也侧面反映了命令覆盖范围之广(涵盖 string、hash、list、set、zset、stream、json、bloom filter、pub/sub、事务、脚本等类别)。
二、基准测试:吞吐量与内存效率
注意:以下数据均为项目方在 README 中公开的基准测试声明,测试环境为 AWS 特定实例类型,结果受硬件、网络与测试参数影响,仅作横向参考。
2.1 峰值吞吐量
在 AWS 网络能力最强的 c6gn.16xlarge 实例上,项目方基准测试显示 Dragonfly 相对 Redis 单进程吞吐量提升25 倍,突破380 万 QPS(3.8M QPS)。在流水线(pipeline)模式--pipeline=30下,SET 操作可达1000 万 QPS(10M QPS),GET 操作可达1500 万 QPS(15M QPS)。
Dragonfly 在峰值吞吐量下的 99 分位延迟指标(来自 README.ko-KR.md):
| 操作 | 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=...2.2 Dragonfly vs. Memcached
项目方在 AWS c6gn.16xlarge 实例上对比了 Dragonfly 与 Memcached。在相近延迟的前提下,Dragonfly 在读写两类负载中吞吐量均优于 Memcached;写负载下 Dragonfly 延迟更优,这与 Memcached 写路径 上的锁竞争有关。
SET 基准对比:
| Server | QPS(千 QPS) | latency 99% | 99.9% |
|---|---|---|---|
| Dragonfly | 3844 | 0.9ms | 2.4ms |
| Memcached | 806 | 1.6ms | 3.2ms |
GET 基准对比:
| Server | QPS(千 QPS) | latency 99% | 99.9% |
|---|---|---|---|
| Dragonfly | 3717 | 1ms | 2.4ms |
| Memcached | 2100 | 0.34ms | 0.6ms |
结论:Memcached 在读基准中延迟更低,但吞吐量也更低;Dragonfly 在写路径上优势尤其明显。
2.3 内存效率与 fork-less 快照
项目方内存效率实验流程如下:
- 使用
debug populate 5000000 key 1024命令向 Dragonfly 与 Redis 各填充约 5GB 数据; - 使用
memtier发送更新流量; - 使用
bgsave命令触发快照,并持续观测内存。
结果(见上文第一张图):空闲状态下 Dragonfly 比 Redis 内存效率高约 30%;快照阶段 Dragonfly 内存占用没有明显增长,而 Redis 峰值内存接近 Dragonfly 的 3 倍;Dragonfly 在几秒内即完成快照。
关于内存效率更深入的分析,可阅读 docs/dashtable.md——其中给出了 Dashtable 与 Redis 字典逐条记录的元数据开销对比(README 指出的关键结论:Dashtable 每记录仅约 20 bit 元数据开销,而 Redis dictEntry 需要 64 bit),以及单线程/多线程填充、BGSAVE、过期回收等实验数据。
三、配置指南:命令行参数与默认值
Dragonfly 支持适用的 Redis 参数,例如dragonfly --requirepass=foo --bind localhost。以下参数说明以 README.ko-KR.md 为准,并对照 src/server 源码中的ABSL_FLAG定义核对了默认值。
3.1 Redis 兼容参数
| 参数 | 说明 | 默认值 |
|---|---|---|
port | Redis 连接端口 | 6379(源码见 src/facade/ok_main.cc 与 src/server/main_service.cc,其中0表示禁用该端口,-1表示绑定随机可用端口) |
bind | 绑定地址:localhost仅允许本机连接;公网 IP 则允许外部连接到该 IP;0.0.0.0允许所有 IPv4 连接 | ""(源码见 src/server/dfly_main.cc) |
requirepass | AUTH 认证密码 | ""(源码见 src/server/server_family.cc,为空时也可通过环境变量DFLY_PASSWORD设置) |
maxmemory | 数据库最大内存限制(可读的字节单位,如512MB、2G、1.25GiB) | 0,表示程序根据运行环境自动确定最大内存(源码见 src/server/main_service.cc,同时要求每 proactor 线程至少 256MiB;启用 tiering 时该值仅约束 RAM 部分) |
dir | 快照文件目录:Docker 默认使用/data,CLI 默认"",可通过 Docker-v选项映射到宿主机目录 | ""(源码见 src/server/server_family.cc) |
dbfilename | 保存/加载的数据库文件名 | dump(源码见 src/server/server_family.cc,支持{timestamp}、{Y}、{m}、{d}宏) |
3.2 Dragonfly 特有参数
| 参数 | 说明 | 默认值 |
|---|---|---|
memcached_port | 启用 Memcached 兼容 API 的端口 | disabled(源码见 src/server/main_service.cc) |
keys_output_limit | keys命令返回的最大键数量 | 8192(源码见 src/server/generic_family.cc)。keys是危险命令,截断结果可避免拉取过多键时内存暴涨 |
dbnum | select命令支持的最大数据库数量 | 源码默认16(src/server/generic_family.cc) |
cache_mode | 缓存模式开关,见下文“新的缓存设计” | false(源码见 src/server/engine_shard_set.cc) |
hz | 键过期评估的基础频率 | 100(源码见 src/server/engine_shard.cc)。频率越低,空闲时 CPU 占用越低,但键驱逐更慢;源码注释提示生产环境不建议调低 |
primary_port_http_enabled | 为true时允许通过主 TCP 端口访问 HTTP 控制台 | true(源码见 src/facade/dragonfly_connection.cc) |
admin_port | 在指定端口启用管理员控制台,同时支持 HTTP 与 RESP 协议 | disabled(源码见 src/facade/dragonfly_connection.cc) |
admin_bind | 将管理员控制台 TCP 连接绑定到指定地址,支持 HTTP 与 RESP 协议 | any(源码见 src/facade/dragonfly_connection.cc) |
admin_nopass | 对指定端口开放无需认证令牌的管理员控制台访问,支持 HTTP 与 RESP 协议 | false(源码见 src/server/main_service.cc) |
cluster_mode | 集群模式,当前仅支持emulated | ""(源码见 src/server/cluster_support.cc) |
cluster_announce_ip | 集群命令向客户端通告的 IP 地址 | ""(源码见 src/server/cluster/cluster_family.cc) |
announce_port | 集群命令及复制主节点向客户端通告的端口 | 0(源码见 src/server/main_service.cc) |
snapshot_cron | 按 cron 表达式(分钟粒度)自动备份快照 | ""(源码见 src/server/server_family.cc,旧参数save_schedule已废弃) |
常用 cron 表达式示例(来自英文版 README,源码 src/server/server_family.cc 中的CronExprFlag支持标准 crontab 语法):
| Cron 表达式 | 含义 |
|---|---|
* * * * * | 每分钟 |
*/5 * * * * | 每 5 分钟 |
5 */2 * * * | 每 2 小时的第 5 分钟 |
0 0 * * * | 每天 00:00(午夜) |
0 6 * * 1-5 | 每周一至周五 06:00 |
3.3 常用选项启动脚本示例
./dragonfly-x86_64 --logtostderr --requirepass=youshallnotpass --cache_mode=true -dbnum 1 --bind localhost --port 6379 --maxmemory=12gb --keys_output_limit=12288 --dbfilename dump.rdb参数还可以通过以下两种方式提供:
--flagfile <filename>:文件中每行一个参数,键值参数用等号而非空格,参数值无需引号。该机制由 absl 的 flagfile 解析实现,相关处理逻辑见 src/server/server_family.cc(其中排除了flagfile等仅启动期生效的配置);- 环境变量:设置
DFLY_x,其中x为参数的精确名称(区分大小写)。
更多选项(如日志管理、TLS 支持、tiered storage、慢日志slowlog_log_slower_than、maxclients等)可通过运行dragonfly --help查看;例如replicaof(设置复制主节点)、tiered_prefix(启用 SSD 分层存储)等标志都定义在 src/server/server_family.cc 与 src/server/engine_shard.cc 中。
四、设计决策:缓存、过期与原生 HTTP 控制台
4.1 新的缓存设计(Novel Cache Design)
Dragonfly 提供一种单一、统一、自适应的缓存算法,简单且内存高效。通过--cache_mode=true启用缓存模式后,Dragonfly 只在接近maxmemory上限时才驱逐(evict)未来最不可能被再次命中的条目。
源码层面,缓存模式在 src/server/engine_shard_set.cc 定义,并在 src/server/db_slice.h 的DbSlice构造函数中传入;DbSlice::IsCacheMode()(src/server/db_slice.h)控制读取行为——缓存模式下仅在非加载(load)阶段生效。项目方在 docs/dashtable.md 中进一步指出,该驱逐算法以零内存开销实现了优于 LRU/LFU 等传统策略的命中率。
4.2 相对精确的过期时间(Expiration Deadlines)
- 过期时间范围被限制在约8 年以内;
- 毫秒精度的过期命令(如
PEXPIRE、PSETEX):当过期时间大于 2^28ms 时,会四舍五入到最近的秒,误差小于 0.001%,对大规模时间范围可以接受;若不符合你的使用场景,可以向项目方反馈或提交 issue 说明用例。
Dragonfly 与 Redis 在过期实现上的更多差异(如EXPIRE系列命令同时接受NX与GT/LT组合、Lua 使用 2022 年发布的 5.4.4 并支持 lua 整数等)见 docs/differences.md。
4.3 原生 HTTP 控制台与 Prometheus 兼容指标
默认情况下,Dragonfly 允许通过主 TCP 端口(6379)进行 HTTP 访问——你可以通过Redis 协议或HTTP 协议两种方式连接,服务器会在连接初始化阶段自动识别协议。直接用浏览器访问即可体验。当前 HTTP 页面信息不多,但未来会加入有用的调试与管理信息。
- 访问
:6379/metrics可查看Prometheus 兼容指标; - 导出的指标与 Grafana 仪表盘兼容,对应配置文件见 tools/local/monitoring/grafana/provisioning/dashboards/dragonfly.json(该目录下还包含配套的 Prometheus 采集配置,见 tools/local/monitoring)。
重要安全提示:HTTP 控制台设计为仅在安全网络内访问。如果对外暴露 Dragonfly 的 TCP 端口,建议使用--http_admin_console=false或--nohttp_admin_console禁用该控制台。
五、开发背景与架构选择
Dragonfly 始于 2022 年的一场实验:如果用 2022 年的视角重新设计一个内存数据存储,它会是什么样?基于团队作为内存存储用户与云公司工程师的经验,Dragonfly 需要保留两个关键特性:所有操作的原子性保证,以及极高吞吐下的亚毫秒级低延迟。
5.1 第一个挑战:充分利用云服务器资源——shared-nothing 架构
如何用当今公有云环境可用的服务器榨干 CPU、内存与 I/O 资源?答案是shared-nothing(无共享)架构:将内存存储的键空间(keyspace)在线程之间划分,每个线程独立管理自己的一块字典数据,这些分片被称为shard。支撑该架构的线程与 I/O 管理库即本仓库中的 helio 子项目(对应上游 romange/helio 开源库)。
5.2 原子性保证:VLL 锁管理器重设计
为给多键操作提供原子性保证,Dragonfly 的事务框架借鉴了学术界 VLL 论文(“VLL: a lock manager redesign for main memory database systems”)。shared-nothing 架构 + VLL的组合使 Dragonfly 无需使用互斥锁(mutex)或自旋锁(spinlock)即可组合出原子多键操作——这是 PoC 阶段的重要里程碑。
5.3 第二个挑战:更高效的数据结构——Dash 哈希表
为构建更高效的数据结构,Dragonfly 的核心哈希表基于论文 “Dash: Scalable Hashing on Persistent Memory”(arXiv:2003.07302)。该论文虽面向持久内存领域,但其设计恰好满足了 Dragonfly 的需求,保留了 Redis 字典的两个特性:
- 数据存储扩容时的**增量哈希(incremental hashing)**能力;
- 通过**无状态扫描(stateless scan)**在字典变化时遍历的能力。
除此之外,Dash 在 CPU 与内存使用上更高效。基于 Dash 的设计,Dragonfly 进一步实现了三项创新(详见 docs/dashtable.md 对分段结构、目录开销与分裂过程的分析):
- 高效的 TTL 记录过期处理:利用 Dash 分段(segment)满后分裂这一天然时机,仅扫描单个分段内的过期条目,扫描成本与分裂本身同阶(O(1)),配合后台渐进扫描实现低 CPU 开销的被动过期;
- 零内存开销的新缓存驱逐算法:命中率高于 LRU、LFU 等传统缓存策略;
- 全新的 fork-less(无 fork)快照算法:BGSAVE 与 SAVE 采用同一套全异步算法,维持点-时间(point-in-time)快照保证,无需 fork 子进程即可完成快照。
实验数据(来自 docs/dashtable.md,Dragonfly vs Redis 6 在 AMD Ryzen 5 3400G 上):
| 场景 | Dragonfly | Redis 6 |
|---|---|---|
| 单线程填充 2000 万键耗时 | 10.8s | 16.0s |
| 单线程填充内存占用 | 1GB | 1.73GB |
| 8 线程填充 2000 万键耗时 | 2.43s | 16.0s |
| 8 线程填充内存占用 | 896MB | 1.73GB |
Redis 的used_memory_overhead高达 1.0GB——对小数据场景,Redis 的元数据开销甚至超过数据本身,这正是 Dashtable 降低字典管理浪费的价值所在。
六、路线图与状态
按项目说明,Dragonfly 当前支持约 185 个 Redis 命令与全部 Memcached 命令(含cas),几乎与 Redis 5 API 对等。下一个里程碑是稳定核心功能并实现复制 API——为支持 Dragonfly 独有的复制功能,项目正在设计一种能提供数倍速度的分布式日志格式;复制功能落地后,将继续补齐 Redis 3-6 API 中缺失的命令。若缺少所需命令,可在项目 issues 中提出。
此外,Dragonfly 提供了丰富的周边资源便于上手:快速开始指南见 docs/quick-start/README.md,Docker 编排示例见 contrib/docker/docker-compose.yml,详细的 Dashtable 内存结构解析见 docs/dashtable.md,与 Redis 的行为差异清单见 docs/differences.md。
结语
从命令行参数到核心数据结构,Dragonfly 的设计处处体现“以现代硬件为目标”的思路:shared-nothing 架构让多核资源被充分压榨,VLL 事务框架免去了锁的代价,Dash 哈希表把字典元数据开销压到接近理论极限,而 fork-less 快照与零开销缓存驱逐算法则是这些底层优势向应用层能力的自然延伸。配合 Redis/Memcached 兼容 API 与开箱即用的 HTTP/Prometheus 监控能力,Dragonfly 为希望在保留既有客户端生态的同时获得更高吞吐与更低资源消耗的团队,提供了一个值得评估的现代内存数据存储选项。
【免费下载链接】dragonflyA modern replacement for Redis and Memcached项目地址: https://gitcode.com/GitHub_Trending/dr/dragonfly
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考