DragonflyDB: 高性能 Redis 替代方案¶
为什么要学 DragonflyDB¶
Redis 是缓存领域的事实标准,但它有一个根本性的架构限制:单线程。Redis 通过单线程事件循环实现了简单和一致性,但在多核服务器上无法充分利用硬件资源。
DragonflyDB 用 C++ 从零构建,保持 Redis/Memcached API 兼容的同时,使用多线程架构和无锁数据结构,实现了大幅性能提升。
| 维度 | Redis | DragonflyDB |
|---|---|---|
| 架构 | 单线程 | 多线程(shared-nothing) |
| 性能 | 10-30万 QPS | 数百万 QPS |
| 内存效率 | 一般 | 比 Redis 低 ~30% |
| API 兼容 | 原版 | 兼容 Redis + Memcached |
| 持久化 | RDB + AOF | 快照(更快) |
| 语言 | C | C++ |
| 集群 | 需要 Redis Cluster | 单实例利用全部核心 |
| 复杂度 | 需要分片/集群 | 单实例即可(大多数场景) |
核心优势:用一个 DragonflyDB 实例替代一整个 Redis 集群,降低运维复杂度。
核心概念¶
白话解释¶
DragonflyDB 的设计理念是:现代服务器有几十个 CPU 核心和大量内存,缓存系统应该充分利用这些资源。
它使用了几个关键技术: 1. Shared-Nothing 架构:每个线程有自己的数据分片,线程间不共享数据,避免锁竞争 2. 无锁数据结构:基于 Dash(一种新型哈希表)实现高并发读写 3. VLL(Very Lightweight Locking):极轻量的同步原语 4. Dashtable:比 Redis 的 dict 更内存高效的哈希表
核心概念表¶
| 概念 | 说明 | Redis 对比 |
|---|---|---|
| Shared-Nothing | 每个线程独立管理一个数据分片 | Redis 单线程处理所有 |
| Dashtable | 高效内存哈希表 | Redis dict |
| Fiber | 协程级并发 | io_uring 事件循环 |
| 快照 | 后台异步快照(不阻塞) | RDB save 期间有性能影响 |
| Multi-thread IO | 多线程网络 IO | Redis 6+ 的 io-threads(有限) |
| 内存去重 | 自动压缩重复字符串 | 需要手动优化 |
兼容性¶
DragonflyDB 兼容大部分 Redis 命令:
| 类别 | 支持的命令 |
|---|---|
| 字符串 | GET, SET, MGET, MSET, INCR, APPEND, STRLEN... |
| 哈希 | HGET, HSET, HMGET, HMSET, HGETALL, HDEL... |
| 列表 | LPUSH, RPUSH, LPOP, RPOP, LRANGE, LLEN... |
| 集合 | SADD, SREM, SMEMBERS, SINTER, SUNION... |
| 有序集合 | ZADD, ZREM, ZRANGE, ZSCORE, ZRANK... |
| 发布/订阅 | PUBLISH, SUBSCRIBE, PSUBSCRIBE... |
| 事务 | MULTI, EXEC, WATCH |
| Lua 脚本 | EVAL, EVALSHA |
| Stream | XADD, XREAD, XRANGE... |
安装配置¶
方式一:Docker(推荐)¶
# 快速启动
docker run -d --name dragonfly -p 6379:6379 --ulimit memlock=-1 \
docker.dragonflydb.io/dragonflydb/dragonfly
# 带持久化
docker run -d --name dragonfly \
-p 6379:6379 \
-v dragonfly-data:/data \
--ulimit memlock=-1 \
docker.dragonflydb.io/dragonflydb/dragonfly \
--dbfilename dump.rdb --dir /data
方式二:Docker Compose¶
# docker-compose.yml
version: '3'
services:
dragonfly:
image: docker.dragonflydb.io/dragonflydb/dragonfly
container_name: dragonfly
ports:
- '6379:6379'
volumes:
- dragonfly-data:/data
ulimits:
memlock: -1
command: >
--maxmemory 2g
--dbfilename dump.rdb
--dir /data
--requirepass ${DRAGONFLY_PASSWORD:-}
restart: unless-stopped
volumes:
dragonfly-data:
方式三:直接安装¶
# Ubuntu/Debian
sudo apt update && sudo apt install -y dragonfly
# macOS
brew install dragonflydb/tap/dragonfly
# 从二进制安装
wget https://github.com/dragonflydb/dragonfly/releases/latest/download/dragonfly-x86_64.tar.gz
tar xzf dragonfly-x86_64.tar.gz
sudo mv dragonfly /usr/local/bin/
# 启动
dragonfly --logtostderr
配置选项¶
# 常用启动参数
dragonfly \
--port 6379 \
--maxmemory 4g \ # 最大内存
--dbfilename dump.rdb \ # 快照文件名
--dir /var/lib/dragonfly \ # 数据目录
--requirepass your-password \ # 访问密码
--bind 0.0.0.0 \ # 监听地址
--proactor_threads 8 \ # IO 线程数(默认=CPU核心数)
--cache_mode true \ # 缓存模式(内存满时自动淘汰)
--snapshot_cron "0 */6 * * *" \ # 每6小时自动快照
--maxclients 65535 # 最大连接数
验证连接¶
# 使用 redis-cli(完全兼容)
redis-cli -p 6379 ping
# → PONG
redis-cli -p 6379 info server
# 显示 Dragonfly 版本和状态
快速上手¶
基本操作(redis-cli)¶
# 连接
redis-cli -p 6379
# 字符串操作
SET user:1:name "Alice"
GET user:1:name
# → "Alice"
SET counter 0
INCR counter
INCR counter
GET counter
# → "2"
# 设置过期时间
SET session:abc "data" EX 3600 # 1小时过期
TTL session:abc
# → 3599
# 哈希操作
HSET user:1 name "Alice" email "alice@example.com" age 30
HGET user:1 name
HGETALL user:1
# 列表操作
LPUSH queue:tasks "task1" "task2" "task3"
RPOP queue:tasks
# → "task1"
# 集合操作
SADD tags:post:1 "python" "tutorial" "beginner"
SMEMBERS tags:post:1
# 有序集合
ZADD leaderboard 100 "player1" 200 "player2" 150 "player3"
ZREVRANGE leaderboard 0 2 WITHSCORES
Python 使用¶
import redis
# 连接 DragonflyDB(和连接 Redis 完全一样)
r = redis.Redis(host='localhost', port=6379, decode_responses=True)
# 基本操作
r.set('greeting', 'Hello from DragonflyDB!')
print(r.get('greeting'))
# 缓存模式
def get_user(user_id: int) -> dict:
cache_key = f"user:{user_id}"
# 先查缓存
cached = r.get(cache_key)
if cached:
return json.loads(cached)
# 缓存未命中,查数据库
user = db.query_user(user_id)
# 写入缓存,1小时过期
r.setex(cache_key, 3600, json.dumps(user))
return user
# 哈希操作
r.hset('user:1', mapping={
'name': 'Alice',
'email': 'alice@example.com',
'login_count': 42
})
r.hincrby('user:1', 'login_count', 1)
user = r.hgetall('user:1')
print(user)
# → {'name': 'Alice', 'email': 'alice@example.com', 'login_count': '43'}
# 管道(批量操作)
pipe = r.pipeline()
for i in range(1000):
pipe.set(f'key:{i}', f'value:{i}')
pipe.execute()
Node.js 使用¶
import Redis from 'ioredis';
const redis = new Redis({
host: 'localhost',
port: 6379,
});
// 基本操作
await redis.set('key', 'value');
const value = await redis.get('key');
// 带过期时间
await redis.setex('session:123', 3600, JSON.stringify({ userId: 1 }));
// 管道
const pipeline = redis.pipeline();
pipeline.set('a', '1');
pipeline.set('b', '2');
pipeline.set('c', '3');
await pipeline.exec();
进阶用法¶
从 Redis 迁移¶
DragonflyDB 完全兼容 Redis 协议,迁移非常简单:
# 方式一:导入 RDB 文件
# 1. 从 Redis 导出 RDB
redis-cli -h redis-host BGSAVE
# 2. 复制 dump.rdb 到 DragonflyDB 数据目录
cp dump.rdb /var/lib/dragonfly/
# 3. 启动 DragonflyDB
dragonfly --dir /var/lib/dragonfly
# 方式二:在线复制
# DragonflyDB 可以作为 Redis 的副本
dragonfly --replicaof redis-host:6379
应用层迁移只需要改连接地址:
# 之前连接 Redis
r = redis.Redis(host='redis-server', port=6379)
# 改为连接 DragonflyDB(只改地址)
r = redis.Redis(host='dragonfly-server', port=6379)
# 代码不需要任何其他修改
缓存模式¶
# 启用缓存模式(内存满时自动淘汰旧数据)
dragonfly --cache_mode true --maxmemory 4g
# 淘汰策略和 Redis 一样
# 默认 noeviction(满了报错)
# 缓存模式下自动使用 allkeys-lru
发布/订阅¶
import redis
import threading
r = redis.Redis()
# 发布者
def publisher():
import time
for i in range(10):
r.publish('notifications', f'Message {i}')
time.sleep(1)
# 订阅者
def subscriber():
pubsub = r.pubsub()
pubsub.subscribe('notifications')
for message in pubsub.listen():
if message['type'] == 'message':
print(f"Received: {message['data']}")
# 启动
t1 = threading.Thread(target=subscriber, daemon=True)
t1.start()
publisher()
Lua 脚本¶
# 原子操作:检查并递减库存
lua_script = """
local stock = tonumber(redis.call('GET', KEYS[1]))
if stock and stock > 0 then
redis.call('DECR', KEYS[1])
return 1
end
return 0
"""
r = redis.Redis()
decrement = r.register_script(lua_script)
# 使用
r.set('stock:product:123', 100)
result = decrement(keys=['stock:product:123'])
# result = 1 (成功扣减)
监控和管理¶
# 查看信息
redis-cli INFO
# 查看内存使用
redis-cli INFO memory
# 查看客户端连接
redis-cli CLIENT LIST
# 实时监控命令
redis-cli MONITOR
# 慢查询日志
redis-cli SLOWLOG GET 10
# DragonflyDB 特有:查看线程信息
redis-cli INFO server | grep dragonfly
性能基准测试¶
# 使用 Redis 官方 benchmark 工具
redis-benchmark -p 6379 -c 50 -n 100000
# 常见结果(取决于硬件):
# DragonflyDB: 数百万 QPS(多线程)
# Redis: 10-30万 QPS(单线程)
# 使用 memtier_benchmark(更准确)
memtier_benchmark -s 127.0.0.1 -p 6379 \
--threads=4 --clients=50 --requests=100000 \
--data-size=256
常见问题¶
Q1: DragonflyDB 可以完全替代 Redis 吗?¶
大多数场景可以。需要注意: - 部分 Redis 模块(如 RediSearch、RedisJSON)有自己的实现方式 - Redis Cluster 协议支持有限(但单实例性能足够替代集群) - 一些非常边缘的 Redis 命令可能行为有差异
Q2: 多线程会影响数据一致性吗?¶
不会。DragonflyDB 使用 Shared-Nothing 架构,每个键被哈希到固定的线程,该键的所有操作由同一线程处理,保证了原子性和一致性。跨键事务通过 VLL 协调。
Q3: 什么时候该继续用 Redis?¶
- 已有稳定的 Redis 集群且没有性能问题
- 重度依赖 Redis 模块生态(RediSearch、RedisGraph 等)
- 需要 Redis Sentinel 的高可用方案
- 团队对 Redis 运维非常熟悉
Q4: 内存使用比 Redis 少 30%?真的吗?¶
是的,DragonflyDB 的 Dashtable 数据结构比 Redis 的 dict + listpack 更紧凑。在大量小对象场景下改善尤其明显。
Q5: 持久化策略是什么?¶
DragonflyDB 支持 RDB 快照,使用异步方式完成(不阻塞服务)。通过 --snapshot_cron 配置定时快照。暂不支持 Redis 的 AOF(append-only file),但快照恢复已足够大多数场景。
参考资源¶
| 资源 | 链接 |
|---|---|
| 官方网站 | https://www.dragonflydb.io |
| GitHub 仓库 | https://github.com/dragonflydb/dragonfly |
| 官方文档 | https://www.dragonflydb.io/docs |
| 基准测试 | https://www.dragonflydb.io/benchmarks |
| 兼容性列表 | https://www.dragonflydb.io/docs/command-reference |
| Redis 客户端库 | 任何 Redis 客户端库都兼容 |
总结:DragonflyDB 是 Redis 架构的现代化演进。如果你的 Redis 遇到了性能瓶颈需要搭建集群,先试试 DragonflyDB——一个实例可能就够了。它的 S3 API 兼容让迁移几乎无成本:只改连接地址,代码不用动。对于新项目,选择 DragonflyDB 作为缓存层是一个前瞻性的技术选型。