跳转至

DragonflyDB: 高性能 Redis 替代方案

为什么要学 DragonflyDB

Redis 是缓存领域的事实标准,但它有一个根本性的架构限制:单线程。Redis 通过单线程事件循环实现了简单和一致性,但在多核服务器上无法充分利用硬件资源。

DragonflyDB 用 C++ 从零构建,保持 Redis/Memcached API 兼容的同时,使用多线程架构和无锁数据结构,实现了大幅性能提升。

维度RedisDragonflyDB
架构单线程多线程(shared-nothing)
性能10-30万 QPS数百万 QPS
内存效率一般比 Redis 低 ~30%
API 兼容原版兼容 Redis + Memcached
持久化RDB + AOF快照(更快)
语言CC++
集群需要 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多线程网络 IORedis 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
StreamXADD, 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 使用

pip install redis
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 使用

npm install ioredis
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 作为缓存层是一个前瞻性的技术选型。