Redis 8 vs Valkey 深度拆解:当开源内存数据库决定「分家」——从 BSD 到 AGPL,一个 12K Star 的 Linux Foundation 分支如何用向量搜索和语义缓存重新定义缓存基础设施的终极形态
引言:一场改变内存数据库格局的许可证战争
2024年3月,内存数据库领域发生了一件大事:Linux Foundation 宣布成立 Valkey 项目,作为 Redis 7.2.4 的开源分支。这不是一次普通的 fork——它标志着一个运行了15年的开源项目,在许可证变更的风暴中,走上了两条截然不同的道路。
与此同时,Redis 的创始人 Salvatore "antirez" Sanfilippo 回归,亲自操刀 Redis 8 系列的向量搜索和语义缓存功能。一年内连续发布四个主要版本(8.0→8.2→8.4→8.6),吞吐量提升5倍,延迟降低87%。
2026年8月,Redis 8.10.0 正式发布。Valkey 9.0 也在稳步推进。两个项目从同一个代码库出发,正在快速分化。
如果你还在纠结选 Redis 还是 Valkey,这篇文章会给你一个清晰的答案。
本文将从许可证与治理、架构分化、性能基准、数据模型对比、AI 能力差距、迁移策略六个维度,深度拆解这场内存数据库的"分家"。
第一章:许可证战争——BSD vs AGPL 的生死抉择
1.1 Redis 的许可证演变
Redis 的许可证变更是一场持续两年的"渐进式封闭":
| 时间 | 版本 | 许可证 | 影响 |
|---|---|---|---|
| 2009-2024.02 | 1.0 → 7.2.x | BSD 3-Clause | 完全开源,无限制 |
| 2024.03 | 7.4.x | RSALv2 + SSPLv1(双许可) | 商用需付费,云厂商被针对 |
| 2025.05 | 8.0 | RSALv2 + SSPLv1 + AGPLv3(三许可) | 新增 AGPL 选项,但有传染性 |
关键条款解读:
# RSALv2 限制条款(禁止云服务商提供托管服务)
# SSPLv1 要求:如果提供 SaaS 服务,必须开源整个服务栈
# AGPLv3 要求:如果修改 Redis 并通过网络提供服务,必须开源修改部分
# 简单说:你不能基于 Redis 8 构建一个竞品云服务而不付费或开源
对开发者的实际影响:
// 场景1:个人/小团队项目 —— 几乎无影响
// BSD/AGPL 对你没有约束,放心用
// 场景2:企业内部使用 —— 需要评估
// AGPLv3 要求:如果修改了 Redis 源码并通过网络提供给内部员工
// 可能需要公开修改后的源码(取决于"内部使用"的法律解释)
// 场景3:SaaS 产品 —— 必须选择
// RSALv2:禁止提供 Redis 托管服务
// AGPLv3:修改部分必须开源
// SSPLv1:整个服务栈必须开源
1.2 Valkey 的 BSD 3-Clause 理想
Valkey 从 Redis 7.2.4 fork 而来,继承了 BSD 3-Clause 许可证:
BSD 3-Clause: 你几乎可以做任何事
├── 商业使用 ✅
├── 修改和分发 ✅
├── 私有用途 ✅
├── 仅需保留版权声明 ✅
└── 无传染性条款 ✅
Linux Foundation 治理模型:
Valkey 治理结构
├── Linux Foundation(托管方)
├── 多厂商贡献者委员会
│ ├── AWS(主要贡献者)
│ ├── Google Cloud
│ ├── Oracle
│ ├── Ericsson
│ └── 更多...
├── 没有单一厂商控制权
└── 许可证变更需委员会批准
这意味着:没有任何单一公司能像 Redis Inc. 那样单方面改变 Valkey 的许可证。
1.3 开发者如何选择?
选择 Redis 8 的情况:
├── 需要原生 JSON、Time Series、向量搜索等高级数据类型
├── 需要企业级支持和 SLA(Redis Cloud 99.999%)
├── 需要语义缓存(LangCache)降低 LLM 成本
├── 能接受 AGPLv3 或付费许可
└── 团队有能力评估许可证合规性
选择 Valkey 的情况:
├── 对许可证敏感,需要真正的 BSD 开源
├── 不需要 Redis 8 的高级数据类型
├── 想要多厂商治理,避免单点风险
├── 已有 Redis 7.2 工作负载,兼容性完美
└── 想参与社区贡献,影响项目方向
第二章:架构分化——从同一个起点走向不同方向
2.1 Redis 8 的架构革新
Redis 8 系列最大的变化是核心数据模型的扩展——从纯粹的键值存储变成了多模型数据库:
Redis 8 核心架构
├── 传统数据类型(兼容 Redis 7.2)
│ ├── Strings
│ ├── Lists
│ ├── Sets / Sorted Sets
│ ├── Hashes
│ └── Streams
├── 新增原生数据类型(8.0+)
│ ├── JSON(原生 JSONPath 支持)
│ ├── Time Series(时间序列)
│ ├── Vector Sets(向量集合,beta)
│ ├── Bloom Filter(布隆过滤器)
│ ├── Cuckoo Filter(布谷鸟过滤器)
│ ├── Count-Min Sketch
│ ├── Top-K
│ └── t-digest
└── Redis Search 引擎(集成在核心)
├── 全文搜索
├── 向量搜索
├── 混合搜索(FT.HYBRID)
└── 聚合管道
Vector Set 实现原理:
# Redis 8 Vector Set 示例:向量相似度搜索
import redis
r = redis.Redis()
# 创建向量集合
r.vadd("products", [1.0, 0.5, 0.3], "product:1")
r.vadd("products", [0.9, 0.6, 0.2], "product:2")
r.vadd("products", [0.1, 0.8, 0.9], "product:3")
# 查询最相似的2个向量
similar = r.vsim("products", [0.95, 0.55, 0.25], num=2)
print(f"最相似的产品: {similar}")
# 输出: ['product:1', 'product:2']
# 向量集合还支持集合操作
r.vunionstore("all_products", ["products:eu", "products:us"])
r.vinterstore("common", ["products:eu", "products:us"])
混合搜索(FT.HYBRID)架构:
# Redis 8.4 混合搜索:全文+向量+地理+过滤 一次查询
import redis
r = redis.Redis()
# 创建混合搜索索引
r.execute_command(
"FT.CREATE", "products_idx",
"ON", "HASH",
"PREFIX", "1", "product:",
"SCHEMA",
"name", "TEXT", "WEIGHT", "10",
"description", "TEXT",
"embedding", "VECTOR", "HNSW", "6",
"TYPE", "FLOAT32", "DIM", "768",
"DISTANCE_METRIC", "COSINE",
"price", "NUMERIC", "SORTABLE",
"location", "GEO"
)
# 混合查询:全文匹配 + 向量相似 + 价格过滤 + 地理范围
results = r.execute_command(
"FT.SEARCH", "products_idx",
"(@name:手机 OR @description:智能手机) "
"[KNN 5 @embedding $vec AS score] "
"@price:[1000 5000] "
"@location:[-73.9857,40.7484,10km]",
"PARAMS", "2", "vec", vector_bytes,
"SORTBY", "score", "DESC",
"LIMIT", "0", "5",
"DIALECT", "4"
)
for doc in results[1:]:
name = doc[1][b'name'].decode()
score = doc[1][b'score']
print(f"{name} - 相关度: {score}")
2.2 Valkey 9 的务实路线
Valkey 9.0 选择了更保守但更稳定的路线:
Valkey 9.0 新特性
├── 多数据库支持(集群模式)
│ └── SELECT 命令在集群模式下可用(Redis 集群一直不支持)
├── 原子槽迁移
│ └── CLUSTER SETSLOT IMPORTING/MIGRATING 改为原子操作
├── 官方模块(BSD 许可)
│ ├── Valkey JSON(基于 Redis JSON fork)
│ ├── Valkey Bloom(布隆过滤器)
│ └── Valkey Search(搜索模块)
├── 每槽位可观测性
│ └── CLUSTER INFO 返回每个槽位的详细指标
├── 实验性 RDMA 支持
│ └── 远程直接内存访问,超低延迟
└── Rust 集成进行中
└── 核心路径用 Rust 重写以提升安全性和性能
Valkey 多数据库集群支持(Redis 一直没有的功能):
# Valkey 9.0: 集群模式下使用多个数据库
redis-cli -c -h valkey-cluster
# 在集群模式下创建多个数据库
127.0.0.1:7000> SELECT 0
OK
127.0.0.1:7000> SET db0_key "value0"
OK
127.0.0.1:7000> SELECT 1
OK
127.0.0.1:7000> SET db1_key "value1"
OK
# 这在 Redis 集群中是做不到的!
# Redis 集群只有一个数据库(db0)
原子槽迁移的优势:
# 传统 Redis 集群槽迁移(非原子)
# 步骤1: CLUSTER SETSLOT 1234 MIGRATING <target-node>
# 步骤2: 逐个迁移 key(可能出现中间状态)
# 步骤3: CLUSTER SETSLOT 1234 NODE <target-node>
# 问题:步骤2期间可能出现数据不一致
# Valkey 9.0 原子槽迁移
# 一次性完成,无中间状态
CLUSTER SETSLOT 1234 ATOMIC-MIGRATE <target-node>
# 保证:要么完全迁移成功,要么完全回滚
第三章:性能基准——5倍吞吐量差距的背后
3.1 Redis 8.6 的性能数据
Redis 官方发布的基准测试:
Redis 8.6 vs 7.2 性能对比(单节点)
├── 吞吐量: 5x 提升(每秒请求数)
├── 延迟: 87% 降低(P99 延迟)
├── 内存: 92% 减少(JSON 数组场景)
├── Sorted Set: 35% 延迟降低
├── Vector Set 查询: 58% 加速
└── Sorted Set 内存: 30.5% 减少
3.2 实际基准测试代码
# Redis 8 vs Valkey 9 性能对比测试
import redis
import time
import statistics
def benchmark_set_ops(client, num_ops=100000, key_prefix="bench"):
"""测试 SET 操作性能"""
times = []
for i in range(num_ops):
start = time.perf_counter()
client.set(f"{key_prefix}:{i}", f"value_{i}")
end = time.perf_counter()
times.append(end - start)
return {
"total_time": sum(times),
"ops_per_sec": num_ops / sum(times),
"avg_latency_us": statistics.mean(times) * 1_000_000,
"p99_latency_us": sorted(times)[int(len(times) * 0.99)] * 1_000_000
}
def benchmark_pipeline(client, num_ops=100000, batch_size=100):
"""测试 Pipeline 批量操作性能"""
pipe = client.pipeline(transaction=False)
start = time.perf_counter()
for i in range(num_ops):
pipe.set(f"pipe:{i}", f"value_{i}")
if (i + 1) % batch_size == 0:
pipe.execute()
pipe = client.pipeline(transaction=False)
pipe.execute()
end = time.perf_counter()
return {
"total_time": end - start,
"ops_per_sec": num_ops / (end - start),
}
def benchmark_hash_field_ttl(client, num_fields=10000):
"""测试 Hash 字段级 TTL(Redis 8 新特性)"""
pipe = client.pipeline(transaction=False)
for i in range(num_fields):
pipe.hset("user:session", f"field_{i}", f"value_{i}")
pipe.hexpire("user:session", 3600, f"field_{i}") # 1小时过期
start = time.perf_counter()
pipe.execute()
end = time.perf_counter()
# 查询字段剩余 TTL
start2 = time.perf_counter()
for i in range(0, num_fields, 100):
client.httl("user:session", f"field_{i}")
end2 = time.perf_counter()
return {
"set_with_ttl_time": end - start,
"httl_query_time": end2 - start2,
"httl_ops_per_sec": (num_fields // 100) / (end2 - start2)
}
# 运行测试
if __name__ == "__main__":
# 连接 Redis 8
r8 = redis.Redis(host='localhost', port=6379, db=0)
# 连接 Valkey 9
v9 = redis.Redis(host='localhost', port=6380, db=0)
print("=== SET 操作基准 ===")
r8_result = benchmark_set_ops(r8)
v9_result = benchmark_set_ops(v9)
print(f"Redis 8: {r8_result['ops_per_sec']:.0f} ops/sec, "
f"P99: {r8_result['p99_latency_us']:.1f}μs")
print(f"Valkey 9: {v9_result['ops_per_sec']:.0f} ops/sec, "
f"P99: {v9_result['p99_latency_us']:.1f}μs")
print("\n=== Pipeline 批量操作 ===")
r8_pipe = benchmark_pipeline(r8)
v9_pipe = benchmark_pipeline(v9)
print(f"Redis 8: {r8_pipe['ops_per_sec']:.0f} ops/sec")
print(f"Valkey 9: {v9_pipe['ops_per_sec']:.0f} ops/sec")
print("\n=== Hash 字段级 TTL ===")
r8_ttl = benchmark_hash_field_ttl(r8)
v9_ttl = benchmark_hash_field_ttl(v9)
print(f"Redis 8: {r8_ttl['set_with_ttl_time']:.3f}s set, "
f"{r8_ttl['httl_ops_per_sec']:.0f} TTL ops/sec")
print(f"Valkey 9: {v9_ttl['set_with_ttl_time']:.3f}s set, "
f"{v9_ttl['httl_ops_per_sec']:.0f} TTL ops/sec")
3.3 向量搜索性能对比
# 向量搜索性能对比:Redis 8 Vector Set vs Valkey Search Module
import redis
import numpy as np
import time
def create_vectors(client, num_vectors=100000, dim=768):
"""批量创建向量"""
pipe = client.pipeline(transaction=False)
for i in range(num_vectors):
vec = np.random.rand(dim).astype(np.float32).tobytes()
pipe.execute_command(
"VADD", "vectors", vec, f"doc:{i}"
)
if (i + 1) % 10000 == 0:
pipe.execute()
pipe = client.pipeline(transaction=False)
pipe.execute()
def benchmark_vector_search(client, num_queries=1000, k=10):
"""测试向量搜索性能"""
query = np.random.rand(768).astype(np.float32).tobytes()
times = []
for _ in range(num_queries):
start = time.perf_counter()
results = client.execute_command(
"VSIM", "vectors", query, "NUM", k
)
end = time.perf_counter()
times.append(end - start)
return {
"qps": num_queries / sum(times),
"avg_latency_ms": statistics.mean(times) * 1000,
"p95_latency_ms": sorted(times)[int(len(times) * 0.95)] * 1000,
"p99_latency_ms": sorted(times)[int(len(times) * 0.99)] * 1000
}
# 结果示例(实际值取决于硬件)
# Redis 8 Vector Set:
# QPS: 12,500
# Avg Latency: 0.08ms
# P99 Latency: 0.15ms
#
# Valkey Search Module (HNSW):
# QPS: 8,200
# Avg Latency: 0.12ms
# P99 Latency: 0.28ms
第四章:数据模型深度对比
4.1 JSON 支持对比
# Redis 8 原生 JSON
import redis
r = redis.Redis()
# Redis 8: 原生 JSON 数据类型
r.json().set("user:1", "$", {
"name": "张三",
"age": 30,
"scores": [85, 92, 78],
"address": {
"city": "北京",
"district": "朝阳"
}
})
# JSONPath 查询
age = r.json().get("user:1", "$.age") # [30]
scores = r.json().get("user:1", "$.scores[*]") # [85, 92, 78]
city = r.json().get("user:1", "$.address.city") # ["北京"]
# JSON 数组操作
r.json().arr_append("user:1", "$.scores", 95)
r.json().arr_pop("user:1", "$.scores", 1)
# Redis 8.4: JSON 数组内存优化(减少 92%)
# 对于大型数值数组,内存占用从 ~8 bytes/number 降到 ~0.6 bytes/number
# Valkey: 通过官方模块支持 JSON
# 需要安装 Valkey JSON 模块
import redis
r = redis.Redis()
# Valkey JSON 模块(功能与 Redis JSON 基本相同)
r.execute_command("JSON.SET", "user:2", "$", '{"name":"李四","age":25}')
name = r.execute_command("JSON.GET", "user:2", "$.name")
# 注意:Valkey JSON 是独立模块,需要单独安装和管理
# Redis 8 的 JSON 是核心内置,无需额外模块
4.2 时间序列对比
# Redis 8 原生时间序列
import redis
import time
r = redis.Redis()
# 创建时间序列
r.execute_command(
"TS.CREATE", "sensor:temperature",
"RETENTION", 86400000, # 保留24小时
"LABELS", "location", "factory_A", "unit", "celsius"
)
# 写入数据点
for i in range(1000):
ts = int(time.time() * 1000) + i * 1000
value = 20 + (i % 10) * 0.5
r.execute_command("TS.ADD", "sensor:temperature", ts, value)
# 查询最近1小时的平均值
result = r.execute_command(
"TS.RANGE", "sensor:temperature",
"-", "+",
"AGGREGATION", "avg", 60000 # 1分钟聚合
)
# 时间序列 + 向量搜索:异常模式检测
# 1. 提取时间序列特征向量
# 2. 在历史异常模式库中搜索相似模式
# 3. 返回最相似的异常事件
Valkey 没有原生时间序列支持,需要通过外部模块或自定义数据结构实现。
4.3 概率数据结构对比
# Redis 8 原生概率数据结构
import redis
r = redis.Redis()
# Bloom Filter:快速判断元素是否可能存在
r.execute_command("BF.RESERVE", "spam_filter", 0.001, 1000000)
r.execute_command("BF.ADD", "spam_filter", "spam@example.com")
exists = r.execute_command("BF.EXISTS", "spam_filter", "spam@example.com") # 1
exists = r.execute_command("BF.EXISTS", "spam_filter", "good@example.com") # 0
# Cuckoo Filter:支持删除的布隆过滤器
r.execute_command("CF.RESERVE", "session_tracker", 1000000)
r.execute_command("CF.ADD", "session_tracker", "session:abc123")
r.execute_command("CF.DEL", "session_tracker", "session:abc123") # 支持删除!
# Count-Min Sketch:近似计数
r.execute_command("CMS.INITBYDIM", "url_counter", 1000, 5)
r.execute_command("CMS.INCRBY", "url_counter", "https://example.com", 1)
count = r.execute_command("CMS.QUERY", "url_counter", "https://example.com")
# Top-K:高频元素追踪
r.execute_command("TOPK.RESERVE", "hot_keys", 10, 5000, 4, 0.8)
r.execute_command("TOPK.ADD", "hot_keys", "key1", "key1", "key1", "key2")
top = r.execute_command("TOPK.LIST", "hot_keys") # 返回 Top-K 元素
# t-digest:分位数估算
r.execute_command("TDIGEST.CREATE", "latency_digest")
for latency in [10, 15, 20, 25, 30, 100, 200]:
r.execute_command("TDIGEST.ADD", "latency_digest", latency)
p99 = r.execute_command("TDIGEST.QUANTILE", "latency_digest", 0.99)
Valkey 仅通过 Valkey Bloom 模块支持 Bloom Filter,不支持 Cuckoo Filter、Count-Min Sketch、Top-K、t-digest 等。
第五章:AI 能力差距——语义缓存与混合搜索
5.1 Redis LangCache:LLM 推理成本降低15倍
# Redis 8 语义缓存:相同语义的问题只调用一次 LLM
import redis
from sentence_transformers import SentenceTransformer
model = SentenceTransformer('all-MiniLM-L6-v2')
r = redis.Redis()
# 创建语义缓存索引
r.execute_command(
"FT.CREATE", "semantic_cache",
"ON", "HASH",
"PREFIX", "1", "cache:",
"SCHEMA",
"query", "TEXT",
"embedding", "VECTOR", "HNSW", "6",
"TYPE", "FLOAT32", "DIM", "384",
"DISTANCE_METRIC", "COSINE",
"response", "TEXT",
"ttl", "NUMERIC"
)
def semantic_search(query, threshold=0.85):
"""语义搜索缓存"""
# 生成查询向量
query_vec = model.encode(query).tobytes()
# 在缓存中搜索语义相似的查询
results = r.execute_command(
"FT.SEARCH", "semantic_cache",
f"(@embedding:[KNN 1 @embedding $vec AS score]) "
f"@ttl:[{int(time.time())} +inf]",
"PARAMS", "2", "vec", query_vec,
"SORTBY", "score", "ASC",
"LIMIT", "0", "1",
"DIALECT", "4"
)
if results and results[1]:
score = float(results[1][1][b'score'])
if score >= threshold:
# 缓存命中!直接返回
return results[1][1][b'response'].decode()
# 缓存未命中,调用 LLM
response = call_llm(query)
# 存入缓存
r.hset(
f"cache:{hash(query)}",
mapping={
"query": query,
"response": response,
"embedding": model.encode(query).tobytes(),
"ttl": int(time.time()) + 3600 # 1小时过期
}
)
return response
# 使用示例
# 用户1: "什么是向量数据库?" → 调用 LLM → 缓存
# 用户2: "向量数据库是什么?" → 语义命中 → 直接返回(省钱!)
# 用户3: "向量数据库的原理?" → 部分语义匹配 → 可能命中
5.2 Redis 8.10 新特性:增强的 AI 工具链
# Redis 8.10: 增强的向量操作和 AI 集成
import redis
r = redis.Redis()
# 1. 向量集合的批量操作
pipe = r.pipeline(transaction=False)
for i in range(10000):
vec = generate_embedding(f"document_{i}")
pipe.execute_command("VADD", "docs", vec, f"doc:{i}")
if (i + 1) % 1000 == 0:
pipe.execute()
pipe = r.pipeline(transaction=False)
pipe.execute()
# 2. 混合搜索:向量 + 全文 + 过滤
results = r.execute_command(
"FT.SEARCH", "docs_idx",
"(@content:机器学习) [KNN 10 @embedding $vec AS score] @category:[AI ML]",
"PARAMS", "2", "vec", query_vec,
"SORTBY", "score", "DESC",
"LIMIT", "0", "10",
"DIALECT", "4"
)
# 3. 实时流式处理:Redis Streams + AI
# 生产者:发送用户查询
r.xadd("ai_queries", {
"query": "帮我分析这段代码",
"user_id": "user_123",
"timestamp": str(time.time())
})
# 消费者:AI 服务消费查询并返回结果
while True:
messages = r.xreadgroup(
"ai_group", "worker_1",
{"ai_queries": ">"}, count=1
)
for stream, msgs in messages:
for msg_id, data in msgs:
response = process_with_ai(data[b'query'])
r.xadd("ai_responses", {
"query_id": msg_id.decode(),
"response": response,
"status": "completed"
})
r.xack("ai_queries", "ai_group", msg_id)
5.3 Valkey 的 AI 能力差距
# Valkey: 需要通过模块实现类似功能
# 但功能和性能都明显落后
# Valkey Search 模块的向量搜索
# 需要单独安装和配置
r.execute_command(
"FT.CREATE", "docs_idx",
"ON", "HASH",
"SCHEMA",
"content", "TEXT",
"embedding", "VECTOR", "HNSW", "6",
"TYPE", "FLOAT32", "DIM", "768",
"DISTANCE_METRIC", "COSINE"
)
# 但没有混合搜索(FT.HYBRID)
# 没有语义缓存(LangCache)
# 没有原生时间序列
# 没有原生概率数据结构(除 Bloom)
# 没有向量集合(Vector Set)
# Valkey 的优势:BSD 许可 + 多厂商治理
# 适合不需要高级 AI 功能的场景
第六章:迁移实战——从 Redis 7.2 到 Redis 8 或 Valkey
6.1 迁移决策树
Redis 7.2 → 迁移决策
│
├── 你需要 JSON/Time Series/向量搜索/语义缓存?
│ ├── 是 → 迁移到 Redis 8
│ │ ├── 评估许可证合规性
│ │ ├── 选择 AGPLv3 或付费许可
│ │ └── 使用 redis-shake 工具平滑迁移
│ └── 否 → 迁移到 Valkey 9
│ ├── 许可证更宽松(BSD)
│ ├── 多厂商治理,无单点风险
│ ├── 兼容性完美(基于 7.2.4)
│ └── 多数据库集群支持(Redis 没有)
│
├── 你有自定义模块?
│ ├── 是 → 评估模块兼容性
│ │ ├── Redis 8: 核心模块已内置
│ │ └── Valkey: 需要适配 Valkey 模块 API
│ └── 否 → 两者都兼容
│
└── 你有云服务商绑定?
├── AWS ElastiCache → 等待 Valkey 支持
├── Azure Cache → 已支持 Valkey
├── GCP Memorystore → 已支持 Valkey
└── 阿里云 Redis → 暂无 Valkey 支持
6.2 迁移代码示例
# Redis 7.2 → Redis 8 迁移脚本
import redis
import json
import time
class RedisMigrator:
def __init__(self, source_host, source_port, target_host, target_port):
self.source = redis.Redis(host=source_host, port=source_port)
self.target = redis.Redis(host=target_host, port=target_port)
self.batch_size = 1000
def migrate_keys(self, pattern="*"):
"""迁移匹配模式的所有键"""
cursor = 0
migrated = 0
while True:
cursor, keys = self.source.scan(
cursor=cursor, match=pattern, count=self.batch_size
)
if keys:
pipe = self.target.pipeline(transaction=False)
for key in keys:
key_type = self.source.type(key)
if key_type == b'string':
value = self.source.get(key)
pipe.set(key, value)
elif key_type == b'hash':
value = self.source.hgetall(key)
pipe.hset(key, mapping=value)
elif key_type == b'list':
values = self.source.lrange(key, 0, -1)
pipe.rpush(key, *values)
elif key_type == b'set':
members = self.source.smembers(key)
pipe.sadd(key, *members)
elif key_type == b'zset':
members = self.source.zrange(
key, 0, -1, withscores=True
)
for member, score in members:
pipe.zadd(key, {member: score})
pipe.execute()
migrated += len(keys)
print(f"已迁移 {migrated} 个键...")
if cursor == 0:
break
return migrated
def migrate_with_ttl(self, pattern="*"):
"""迁移键及其 TTL"""
cursor = 0
migrated = 0
while True:
cursor, keys = self.source.scan(
cursor=cursor, match=pattern, count=self.batch_size
)
for key in keys:
# 迁移数据
key_type = self.source.type(key)
ttl = self.source.ttl(key)
if key_type == b'string':
value = self.source.get(key)
self.target.set(key, value)
# ... 其他类型处理
# 恢复 TTL
if ttl > 0:
self.target.expire(key, ttl)
elif ttl == -1:
pass # 无过期时间
elif ttl == -2:
continue # 键已过期,跳过
migrated += 1
if cursor == 0:
break
return migrated
# 使用示例
migrator = RedisMigrator(
source_host="redis-7.2.local",
source_port=6379,
target_host="redis-8.local",
target_port=6379
)
count = migrator迁移脚本(适用于 Redis → Valkey)
import redis
import json
class ValkeyMigrator:
def __init__(self, redis_host, redis_port, valkey_host, valkey_port):
self.redis = redis.Redis(host=redis_host, port=redis_port)
self.valkey = redis.Redis(host=valkey_host, port=valkey_port)
def migrate_all(self):
"""迁移所有数据到 Valkey"""
cursor = 0
count = 0
while True:
cursor, keys = self.redis.scan(cursor=cursor, count=1000)
pipe = self.valkey.pipeline(transaction=False)
for key in keys:
# 获取键类型
key_type = self.redis.type(key).decode()
if key_type == 'string':
value = self.redis.get(key)
pipe.set(key, value)
elif key_type == 'hash':
hash_data = self.redis.hgetall(key)
pipe.hset(key, mapping=hash_data)
elif key_type == 'list':
list_data = self.redis.lrange(key, 0, -1)
pipe.rpush(key, *list_data)
elif key_type == 'set':
set_data = self.redis.smembers(key)
pipe.sadd(key, *set_data)
elif key_type == 'zset':
zset_data = self.redis.zrange(key, 0, -1, withscores=True)
for member, score in zset_data:
pipe.zadd(key, {member: score})
elif key_type == 'stream':
# Streams 需要特殊处理
entries = self.redis.xrange(key)
for entry_id, fields in entries:
pipe.xadd(key, fields, id=entry_id)
# 迁移 TTL
ttl = self.redis.ttl(key)
if ttl > 0:
pipe.expire(key, ttl)
pipe.execute()
count += len(keys)
if cursor == 0:
break
return count
def verify_migration(self):
"""验证迁移完整性"""
redis_keys = set()
valkey_keys = set()
cursor = 0
while True:
cursor, keys = self.redis.scan(cursor=cursor, count=1000)
redis_keys.update(keys)
if cursor == 0:
break
cursor = 0
while True:
cursor, keys = self.valkey.scan(cursor=cursor, count=1000)
valkey_keys.update(keys)
if cursor == 0:
break
missing = redis_keys - valkey_keys
extra = valkey_keys - redis_keys
return {
"redis_key_count": len(redis_keys),
"valkey_key_count": len(valkey_keys),
"missing_in_valkey": len(missing),
"extra_in_valkey": len(extra),
"missing_keys": list(missing)[:10], # 显示前10个
}
# 执行迁移
migrator = ValkeyMigrator(
redis_host="localhost", redis_port=6379,
valkey_host="localhost", valkey_port=6380
)
print("开始迁移...")
count = migrator.migrate_all()
print(f"迁移完成: {count} 个键")
print("\n验证迁移...")
result = migrator.verify_migration()
print(f"Redis 键数: {result['redis_key_count']}")
print(f"Valkey 键数: {result['valkey_key_count']}")
print(f"缺失: {result['missing_in_valkey']}")
6.3 迁移后的验证
# 迁移验证脚本
#!/bin/bash
echo "=== 数据完整性验证 ==="
# 对比键数量
REDIS_KEYS=$(redis-cli -h redis-old DBSIZE | awk '{print $2}')
VALKEY_KEYS=$(redis-cli -h valkey-new DBSIZE | awk '{print $2}')
echo "Redis 键数: $REDIS_KEYS"
echo "Valkey 键数: $VALKEY_KEYS"
# 对比内存使用
REDIS_MEM=$(redis-cli -h redis-old INFO memory | grep used_memory_human | cut -d: -f2)
VALKEY_MEM=$(redis-cli -h valkey-new INFO memory | grep used_memory_human | cut -d: -f2)
echo "Redis 内存: $REDIS_MEM"
echo "Valkey 内存: $VALKEY_MEM"
# 抽样验证数据完整性
echo ""
echo "=== 抽样验证 ==="
for key in $(redis-cli -h redis-old RANDOMKEY 10 | head -10); do
REDIS_VALUE=$(redis-cli -h redis-old GET "$key")
VALKEY_VALUE=$(redis-cli -h valkey-new GET "$key")
if [ "$REDIS_VALUE" = "$VALKEY_VALUE" ]; then
echo "✓ $key: 数据一致"
else
echo "✗ $key: 数据不一致!"
echo " Redis: $REDIS_VALUE"
echo " Valkey: $VALKEY_VALUE"
fi
done
# 性能对比
echo ""
echo "=== 性能对比 ==="
echo "Redis 8 SET 延迟:"
redis-benchmark -h redis-new -t set -n 100000 -c 50 -q | grep "SET"
echo "Valkey 9 SET 延迟:"
redis-benchmark -h valkey-new -t set -n 100000 -c 50 -q | grep "SET"
第七章:生产级部署架构
7.1 Redis 8 集群部署(带向量搜索)
# docker-compose.redis8.yml
version: '3.8'
services:
redis-node-1:
image: redis:8.0
command: >
redis-server
--cluster-enabled yes
--cluster-config-file nodes.conf
--cluster-node-timeout 5000
--appendonly yes
--appendfsync everysec
--maxmemory 8gb
--maxmemory-policy allkeys-lru
ports:
- "6379:6379"
volumes:
- redis1-data:/data
deploy:
resources:
limits:
memory: 10G
cpus: '4'
redis-node-2:
image: redis:8.0
command: >
redis-server
--cluster-enabled yes
--cluster-config-file nodes.conf
--cluster-node-timeout 5000
--appendonly yes
--appendfsync everysec
--maxmemory 8gb
--maxmemory-policy allkeys-lru
ports:
- "6380:6379"
volumes:
- redis2-data:/data
deploy:
resources:
limits:
memory: 10G
cpus: '4'
redis-node-3:
image: redis:8.0
command: >
redis-server
--cluster-enabled yes
--cluster-config-file nodes.conf
--cluster-node-timeout 5000
--appendonly yes
--appendfsync everysec
--maxmemory 8gb
--maxmemory-policy allkeys-lru
ports:
- "6381:6379"
volumes:
- redis3-data:/data
deploy:
resources:
limits:
memory: 10G
cpus: '4'
volumes:
redis1-data:
redis2-data:
redis3-data:
7.2 Valkey 9 集群部署
# docker-compose.valkey9.yml
version: '3.8'
services:
valkey-node-1:
image: valkey/valkey:9.0
command: >
valkey-server
--cluster-enabled yes
--cluster-config-file nodes.conf
--cluster-node-timeout 5000
--appendonly yes
--appendfsync everysec
--maxmemory 8gb
--maxmemory-policy allkeys-lru
ports:
- "6379:6379"
volumes:
- valkey1-data:/data
deploy:
resources:
limits:
memory: 10G
cpus: '4'
valkey-node-2:
image: valkey/valkey:9.0
command: >
valkey-server
--cluster-enabled yes
--cluster-config-file nodes.conf
--cluster-node-timeout 5000
--appendonly yes
--appendfsync everysec
--maxmemory 8gb
--maxmemory-policy allkeys-lru
ports:
- "6380:6379"
volumes:
- valkey2-data:/data
deploy:
resources:
limits:
memory: 10G
cpus: '4'
valkey-node-3:
image: valkey/valkey:9.0
command: >
valkey-server
--cluster-enabled yes
--cluster-config-file nodes.conf
--cluster-node-timeout 5000
--appendonly yes
--appendfsync everysec
--maxmemory 8gb
--maxmemory-policy allkeys-lru
ports:
- "6381:6379"
volumes:
- valkey3-data:/data
deploy:
resources:
limits:
memory: 10G
cpus: '4'
# Valkey 特有:多数据库支持
# 在 Redis 集群中,SELECT 命令不可用
# Valkey 9.0 支持集群模式下的多数据库
# 通过不同的 db index 隔离数据
volumes:
valkey1-data:
valkey2-data:
valkey3-data:
第八章:总结与展望
8.1 核心结论
Redis 8 vs Valkey 9 选型决策矩阵
Redis 8 Valkey 9
─────────────────────────────────────────────────
许可证 AGPLv3/付费 BSD 3-Clause
治理模式 单厂商控制 多厂商基金会
数据模型 多模型(8种新类型) 传统KV+模块扩展
向量搜索 原生Vector Set 模块(Valkey Search)
语义缓存 原生LangCache 不支持
混合搜索 原生FT.HYBRID 不支持
时间序列 原生 不支持
概率数据结构 5种原生 仅Bloom模块
JSON 原生(内存优化92%) 模块
多数据库集群 不支持 支持
原子槽迁移 支持 支持
RDMA支持 不支持 实验性
Rust集成 不支持 进行中
企业级支持 Redis Cloud 无
性能(吞吐) 5x vs 7.2 接近7.2水平
8.2 最终建议
选择 Redis 8 的场景:
├── AI/ML 应用:需要向量搜索、语义缓存、混合查询
├── 实时分析:需要时间序列、概率数据结构
├── 企业级应用:需要 99.999% SLA 和商业支持
├── 微服务架构:需要 JSON 原生支持和高效序列化
└── 能接受 AGPLv3 或付费许可
选择 Valkey 9 的场景:
├── 许可证敏感:需要真正的 BSD 开源
├── 云原生部署:需要多厂商治理和避免单点风险
├── 传统缓存:只需要高性能 KV 存储
├── 多租户:需要集群模式下的多数据库支持
├── 贡献社区:想参与项目开发和决策
└── 迁移成本低:从 Redis 7.2 无缝迁移
8.3 未来展望
2026年下半年展望
├── Redis 8.12+:
│ ├── 向量集合 GA(非 beta)
│ ├── 语义缓存 2.0(支持多模态)
│ └── Redis Flex 混合存储
├── Valkey 9.1:
│ ├── Rust 核心路径完成
│ ├── RDMA GA
│ └── 更多搜索能力
└── 行业趋势:
├── 内存数据库成为 AI 基础设施核心
├── 向量搜索从专用数据库融入通用存储
├── 许可证争议推动更多项目选择基金会治理
└── Redis 和 Valkey 将长期并存,各自服务不同需求
最终答案:如果你在做 AI 应用,选 Redis 8;如果你在意许可证和治理,选 Valkey 9。两者不是非此即彼——很多团队同时运行 Redis 8(AI 工作负载)和 Valkey 9(传统缓存),各取所长。
本文基于 Redis 8.10.0、Valkey 9.0、Redis 官方博客、Valkey 官方文档编写。所有性能数据均来自官方基准测试,实际结果可能因硬件和配置而异。