dragonflydb/dragonfly
GitHub: dragonflydb/dragonfly
一款高性能内存数据存储,作为 Redis 和 Memcached 的现代替代方案,提供 25 倍吞吐量提升和更高的内存效率。
Stars: 30092 | Forks: 1152
[](https://github.com/dragonflydb/dragonfly/actions/workflows/ci.yml) [](https://twitter.com/dragonflydbio)
其他语言: [简体中文](README.zh-CN.md) [日本語](README.ja-JP.md) [한국어](README.ko-KR.md) [Português](README.pt-BR.md)
[官网](https://www.dragonflydb.io/) • [文档](https://dragonflydb.io/docs) • [快速开始](https://www.dragonflydb.io/docs/getting-started) • [社区 Discord](https://discord.gg/HsPjXGVH85) • [Dragonfly 论坛](https://dragonfly.discourse.group/) • [加入 Dragonfly 社区](https://www.dragonflydb.io/community)
[GitHub 讨论区](https://github.com/dragonflydb/dragonfly/discussions) • [GitHub Issues](https://github.com/dragonflydb/dragonfly/issues) • [贡献指南](https://github.com/dragonflydb/dragonfly/blob/main/CONTRIBUTING.md) • [AI 代理指南](AGENTS.md) • [Dragonfly Cloud](https://www.dragonflydb.io/cloud)
## 世界上最高效的内存数据存储
Dragonfly 是一款为现代应用工作负载而构建的内存数据存储。
Dragonfly 完全兼容 Redis 和 Memcached API,采用时无需修改任何代码。与传统的内存数据存储相比,Dragonfly 的吞吐量提升了 25 倍,在更低的尾部延迟下实现了更高的缓存命中率,并且在相同规模的工作负载下,资源消耗可减少 80%。
## 目录
- [基准测试](#benchmarks)
- [快速开始](https://github.com/dragonflydb/dragonfly/tree/main/docs/quick-start)
- [配置](#configuration)
- [路线图与状态](#roadmap-status)
- [设计决策](#design-decisions)
- [背景](#background)
- [从源码构建](./docs/build-from-source.md)
## 基准测试
我们首先在通常用于运行 Redis 的 `m5.large` 实例上比较 Dragonfly 和 Redis,
这是由于 Redis 的单线程架构。基准测试程序从同一可用区 (AZ) 的另一个负载测试实例 (c5n) 上运行,使用命令 `memtier_benchmark -c 20 --test-time 100 -t 4 -d 256 --distinct-client-seed`
Dragonfly 展示了与之相当的性能:
1. SETs (`--ratio 1:0`):
| Redis | DF |
| -----------------------------------------|----------------------------------------|
| QPS: 159K, P99.9: 1.16ms, P99: 0.82ms | QPS:173K, P99.9: 1.26ms, P99: 0.9ms |
| | |
2. GETs (`--ratio 0:1`):
| Redis | DF |
| ----------------------------------------|----------------------------------------|
| QPS: 194K, P99.9: 0.8ms, P99: 0.65ms | QPS: 191K, P99.9: 0.95ms, P99: 0.8ms |
上面的基准测试表明,DF 内部允许垂直扩展的算法层在单线程运行时并没有带来太大的损耗。
然而,如果我们使用稍强一点的实例 (m5.xlarge),DF 和 Redis 之间的差距开始拉大。
(`memtier_benchmark -c 20 --test-time 100 -t 6 -d 256 --distinct-client-seed`):
1. SETs (`--ratio 1:0`):
| Redis | DF |
| ----------------------------------------|----------------------------------------|
| QPS: 190K, P99.9: 2.45ms, P99: 0.97ms | QPS: 279K , P99.9: 1.95ms, P99: 1.48ms|
2. GETs (`--ratio 0:1`):
| Redis | DF |
| ----------------------------------------|----------------------------------------|
| QPS: 220K, P99.9: 0.98ms , P99: 0.8ms | QPS: 305K, P99.9: 1.03ms, P99: 0.87ms |
随着实例大小的增加,Dragonfly 的吞吐量持续增长,
而单线程的 Redis 受限于 CPU 瓶颈,其性能已达到局部峰值。
如果我们在网络性能最强的实例 c6gn.16xlarge 上比较 Dragonfly 和 Redis,
与 Redis 单进程相比,Dragonfly 的吞吐量增长了 25 倍,突破了 3.8M QPS。
Dragonfly 在峰值吞吐量下的第 99 百分位延迟指标:
| op | 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 -c 30 -n 200000 --distinct-client-seed -d 256 \
--expiry-range=...
```
在 Pipeline 模式 `--pipeline=30` 下,Dragonfly 的 SET 操作达到 **10M QPS**,GET 操作达到 **15M QPS**。
### Dragonfly vs. Memcached
我们在 AWS 的 c6gn.16xlarge 实例上比较了 Dragonfly 和 Memcached。
在延迟相当的情况下,Dragonfly 的吞吐量在写入和读取工作负载上均优于 Memcached。由于 [Memcached 写入路径上的争用](docs/memcached_benchmark.md),Dragonfly 在写入工作负载中表现出更好的延迟。
#### 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 在读取基准测试中表现出较低的延迟,但吞吐量也较低。
### 内存效率
为了测试内存效率,我们使用 `debug populate 5000000 key 1024` 命令向 Dragonfly 和 Redis 填充了约 5GB 的数据,使用 `memtier` 发送更新流量,并通过 `bgsave` 命令启动快照。
下图展示了每个服务器在内存效率方面的表现。
Dragonfly 在空闲状态下的内存效率比 Redis 高出 30%,并且在快照阶段没有显示出任何可见的内存使用增加。在峰值时,Redis 的内存使用量几乎是 Dragonfly 的 3 倍。
Dragonfly 在几秒钟内更快地完成了快照。
有关 Dragonfly 内存效率的更多信息,请参阅我们的 [Dashtable 文档](/docs/dashtable.md)。
## 配置
Dragonfly 在适用的地方支持常见的 Redis 参数。例如,你可以运行:`dragonfly --requirepass=foo --bind localhost`。
Dragonfly 目前支持以下 Redis 特定参数:
* `port`:Redis 连接端口(`默认值:6379`)。
* `bind`:使用 `localhost` 仅允许本地连接,或使用公网 IP 地址允许连接**到该 IP** 地址(即也可以从外部连接)。使用 `0.0.0.0` 允许所有 IPv4 连接。
* `requirepass`:AUTH 认证的密码(`默认值:""`)。
* `maxmemory`:数据库使用的最大内存限制(人类可读的字节格式)(`默认值:0`)。`maxmemory` 值为 `0` 意味着程序将自动确定其最大内存使用量。
* `dir`:Dragonfly Docker 默认使用 `/data` 文件夹进行快照,CLI 默认使用 `""`。你可以使用 `-v` Docker 选项将其映射到你的主机文件夹。
* `dbfilename`:保存和加载数据库的文件名(`默认值:dump`)。
还有一些 Dragonfly 特有的参数:
* `memcached_port`:启用 Memcached 兼容 API 的端口(`默认值:disabled`)。
* `keys_output_limit`:`keys` 命令返回的最大键数(`默认值:8192`)。请注意,`keys` 是一个危险的命令。我们截断其结果是为了避免在获取过多键时导致内存激增。
* `dbnum`:`select` 支持的最大数据库数量。
* `cache_mode`:见下文的[新颖缓存设计](#novel-cache-design)部分。
* `hz`:键过期评估频率(`默认值:100`)。较低的频率在空闲时使用较少的 CPU,但代价是驱逐率较慢。
* `snapshot_cron`:使用具有分钟粒度的标准 cron 语法进行自动备份快照的 Cron 调度表达式(`默认值:""`)。
下面是一些 cron 调度表达式的示例,欢迎在我们的[文档](https://www.dragonflydb.io/docs/managing-dragonfly/backups#the-snapshot_cron-flag)中阅读有关此参数的更多信息。
| Cron Schedule Expression | Description |
|--------------------------|--------------------------------------------|
| `* * * * *` | 每分钟 |
| `*/5 * * * *` | 每第 5 分钟 |
| `5 */2 * * *` | 每隔 2 小时的第 5 分钟 |
| `0 0 * * *` | 每天 00:00(午夜) |
| `0 6 * * 1-5` | 周一至周五 06:00(黎明) |
* `primary_port_http_enabled`:如果为 `true`,允许在主 TCP 端口上访问 HTTP 控制台(`默认值:true`)。
* `admin_port`:在分配的端口上启用控制台的管理员访问权限(`默认值:disabled`)。支持 HTTP 和 RESP 协议。
* `admin_bind`:将管理控制台 TCP 连接绑定到给定地址(`默认值:any`)。支持 HTTP 和 RESP 协议。
* `admin_nopass`:在分配的端口上启用对控制台的开放管理员访问,无需认证令牌(`默认值:false`)。支持 HTTP 和 RESP 协议。
* `cluster_mode`:支持的集群模式(`默认值:""`)。目前仅支持 `emulated`。
* `cluster_announce_ip`:集群命令向客户端宣告的 IP。
* `announce_port`:集群命令向客户端和复制主节点宣告的端口。
### 包含常用选项的启动脚本示例:
```
./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 `:该文件应每行列出一个标志,键值标志使用等号代替空格。标志值不需要引号。
* 设置环境变量。设置 `DFL_y`,其中 `y` 是标志的确切名称,区分大小写。
有关日志管理或 TLS 支持等更多选项,请运行 `dragonfly --help`。
## 路线图与状态
Dragonfly 目前支持约 185 个 Redis 命令和除 `cas` 之外的所有 Memcached 命令。Dragonfly 的 API 几乎与 Redis 5 持平,下一个里程碑是稳定基本功能并实现复制 API。如果你需要的功能尚未实现,请提交 issue。
对于 Dragonfly 原生复制,我们正在设计一种分布式日志格式,将支持更高数量级的速度。
在复制功能之后,我们将继续为 Redis 3-6 版本 API 添加缺失的命令。
请参阅我们的[命令参考](https://dragonflydb.io/docs/category/command-reference)以获取 Dragonfly 当前支持的命令。
## 设计决策
### 新颖的缓存设计
Dragonfly 拥有一个单一、统一、自适应的缓存算法,既简单又节省内存。
你可以通过传递 `--cache_mode=true` 标志来启用缓存模式。一旦开启此模式,Dragonfly 将驱逐那些在未来最不可能被访问的条目,但仅当它接近 `maxmemory` 限制时才会这样做。
### 具有相对精度的过期截止时间
过期范围限制在约 8 年以内。
具有毫秒精度的过期截止时间(PEXPIRE, PSETEX 等)对于**大于 2^28ms 的截止时间**,将四舍五入到最接近秒数,其误差小于 0.001%,对于大范围来说应该是可以接受的。如果这不适合你的用例,请联系我们或提交一个 issue 解释你的情况。
有关 Dragonfly 过期截止时间与 Redis 实现之间的更详细差异,[请参阅此处](docs/differences.md)。
### 原生 HTTP 控制台和 Prometheus 兼容指标
默认情况下,Dragonfly 允许通过其主 TCP 端口 (6379) 进行 HTTP 访问。没错,你可以通过 Redis 协议和 HTTP 协议连接到 Dragonfly——服务器在连接初始化期间自动识别协议。请用你的浏览器尝试一下。目前的 HTTP 访问信息不多,但将来会包含有用的调试和管理信息。
访问 URL `:6379/metrics` 以查看 Prometheus 兼容的指标。
Prometheus 导出的指标与 Grafana 仪表板兼容,[见此处](tools/local/monitoring/grafana/provisioning/dashboards/dashboard.json)。
重要提示!HTTP 控制台旨在安全网络内访问。如果你将 Dragonfly 的 TCP 端口对外暴露,我们建议你使用 `--http_admin_console=false` 或 `--nohttp_admin_console` 禁用该控制台。
## 背景
Dragonfly 最初是一个实验,旨在探索如果是在 2022 年设计内存数据存储,它会是什么样子。基于我们作为内存存储用户的经验以及在云公司工作的工程师的经验,我们知道我们需要为 Dragonfly 保留两个关键特性:所有操作的原子性保证,以及在极高吞吐量下保持亚毫秒级的低延迟。
我们的第一个挑战是如何利用当今公有云中可用的服务器来充分利用 CPU、内存和 I/O 资源。为了解决这个问题,我们使用了 [shared-nothing architecture](https://en.wikipedia.org/wiki/Shared-nothing_architecture),这允许我们在线程之间划分内存存储的键空间,以便每个线程都可以管理自己的字典数据切片。我们称这些切片为“shards”。为 shared-nothing architecture 提供线程和 I/O 管理支持的库已在[此处](https://github.com/romange/helio)开源。
为了为多键操作提供原子性保证,我们利用了近期学术研究的进展。我们选择了论文 [“VLL: a lock manager redesign for main memory database systems”](https://www.cs.umd.edu/~abadi/papers/vldbj-vll.pdf) 来开发 Dragonfly 的事务框架。选择 shared-nothing architecture 和 VLL 使我们能够组合原子多键操作,而无需使用 mutexes 或 spinlocks。这是我们概念验证 的一个重要里程碑,其性能在其他商业和开源解决方案中脱颖而出。
我们的第二个挑战是为新存储设计更高效的数据结构。为了实现这一目标,我们的核心哈希表结构基于论文 ["Dash: Scalable Hashing on Persistent Memory"](https://arxiv.org/pdf/2003.07302.pdf)。该论文本身围绕持久内存域,并不直接与内存存储相关,但它最适合我们的问题。论文中建议的哈希表设计使我们能够保持 Redis 字典中存在的两个特殊属性:数据存储增长期间的增量哈希能力,以及使用无状态扫描操作在更改下遍历字典的能力。除了这两个属性外,Dash 在 CPU 和内存使用方面效率更高。通过利用 Dash 的设计,我们能够通过以下功能进一步创新:
* TTL 记录的高效记录过期。
* 一种新颖的缓存驱逐算法,比 LRU 和 LFU 等其他缓存策略实现更高的命中率,且**零内存开销**。
* 一种新颖的 **fork-less** 快照算法。
一旦我们建立了 Dragonfly 的基础并且[对其性能感到满意](#benchmarks),我们就开始实现 Redis 和 Memcached 功能。到目前为止,我们已经实现了约 185 个 Redis 命令(大致相当于 Redis 5.0 API)和 13 个 Memcached 命令。
最后,
我们的使命是为云工作负载构建一个设计良好、超快、具有成本效益的内存数据存储,充分利用最新的硬件进步。我们旨在解决当前解决方案的痛点,同时保留其产品 API 和主张。
我们的使命是为云工作负载构建一个设计良好、超快、具有成本效益的内存数据存储,充分利用最新的硬件进步。我们旨在解决当前解决方案的痛点,同时保留其产品 API 和主张。
标签:API兼容, C++, DNS解析, Dragonfly, HTTP工具, In-Memory Database, Memcached替代品, Redis替代品, 中间件, 低延迟, 内存数据库, 内存数据结构, 后端开发, 开源项目, 数据擦除, 缓存命中率, 缓存系统, 自定义请求头, 键值存储, 高并发