语义缓存一直跑在 Redis Stack 上,Redis 8 说要合并进来
我们的语义缓存从 2024 年 9 月开始就跑在 Redis Stack 上(就是那个带 RediSearch 模块的发行版)。这套方案最大的别扭之处是:它跟官方主线版本是两套东西,运维要单独装,升级要跟 Stack 的节奏,云厂商的托管服务支持也不一致。
2025 年 1 月初,Redis 官方陆续放出了 Redis 8 的信息,其中最重要的一条是把 RediSearch、向量检索这些原 Stack 的能力并入 Redis 主线。我第一时间拉了预览版测了一轮,这篇记录。下面涉及 Redis 8 的内容基于年初的公开信息和预览构建,正式版发布时细节可能会有变化,先按早期实践看。
先说我们现在的处境
我们线上跑的是 Redis Stack 7.4,存了这些东西:
$ redis-cli info keyspace
db0:keys=1,284,715,expires=0,avg_ttl=0
$ redis-cli FT._LIST
1) "idx:qvec" # 语义缓存的向量索引
2) "idx:sku" # 商品名模糊搜索
3) "idx:doc" # 文档全文索引
$ redis-cli info memory | grep -E "used_memory_human|peak"
used_memory_human:14.23G
used_memory_peak_human:16.81G
内存 14 GB,其中向量索引占了大头。机器是 32 GB,已经用到一半了。
向量集合:一个更轻的选择
Redis 8 引入了一个新的数据类型,叫向量集合(vector set)。跟我们之前用 FT.CREATE 建的向量索引相比,它的定位不一样:
| 维度 | FT.CREATE 向量索引 | 向量集合 |
|---|---|---|
| 依附于什么 | Hash 或 JSON 文档的一个字段 | 独立的 key,就是个集合 |
| 元数据检索 | 支持,走 RediSearch 的表达式过滤 | 只支持简单的属性过滤 |
| 使用复杂度 | 要先建 schema,字段类型要匹配 | 不用建 schema,直接加元素 |
| 适合场景 | 向量 + 复杂结构化过滤 | 纯向量相似度检索 |
用起来确实简单。加元素:
# VADD key [REDUCE dim] VALUES num fp32_vector element [SETATTR json]
redis-cli> VADD qvec VALUES 4 0.12 -0.34 0.56 0.78 "怎么申请退款"
(integer) 1
redis-cli> VADD qvec VALUES 4 0.11 -0.33 0.55 0.79 "退款流程是怎样的"
(integer) 1
查相似:
# VSIM key ELE|FP32|WITHSCORES [SCORE min] [COUNT n] <query>
redis-cli> VSIM qvec FP32 0.13 -0.35 0.57 0.75 WITHSCORES COUNT 5
1) "怎么申请退款"
2) "0.9987"
3) "退款流程是怎样的"
4) "0.9942"
我用它替换了语义缓存里的向量部分,代码量确实少了。之前建索引要写 schema、要考虑维度匹配、要处理 FT.CREATE 的字段类型,现在就是 VADD / VSIM 两个命令。
public List<CacheHit> lookup(float[] queryVec, int topK, double minScore) {
List<String> args = new ArrayList<>(List.of("qvec", "FP32"));
for (float v : queryVec) args.add(Float.toString(v));
args.addAll(List.of("WITHSCORES", "COUNT", String.valueOf(topK),
"SCORE", String.valueOf(minScore)));
List<Object> raw = redis.execute("VSIM", args.toArray());
// 返回是 [member, score, member, score, ...]
...
}
我们没全量切过去,原因是过滤能力
语义缓存有个需求:按业务线隔离。同样是"怎么退款",A 业务线和 B 业务线的答案不一样。用 FT.CREATE 可以写 @biz_line:{A} 的过滤表达式,向量集合只能靠给元素加属性然后后过滤。
实测下来,加了 60 个业务线、每个业务线 2 万条向量的场景下:
| 方式 | P99 延迟 | 召回是否完整 |
|---|---|---|
| FT.CREATE + 过滤表达式 | 4.2 ms | 完整 |
| 向量集合 + 后过滤 | 11.7 ms | COUNT=50 时经常只返回 3-5 条 |
后过滤的问题跟其他向量库一样:先召回再筛,筛完可能不够数。所以向量集合适合"过滤条件简单或者没有"的场景,比如纯语义去重、相似图片检索,我们把它用在了客服问题去重上,这个场景不需要过滤。
内置搜索引擎:不用再装 Stack 了
这一条对我们运维的价值最大。以前:
# 要用搜索引擎,必须装 Stack 或者手工加载 module
$ redis-server --loadmodule /usr/lib/redis/modules/redisearch.so
Redis 8 里这些模块直接内置,启动就是可用的。我实测了下原来的 Stack 索引能不能直接迁移:
$ redis-server --version
Redis server v=8.0.0-pre ...
$ redis-cli FT.CREATE idx:doc ON HASH PREFIX 1 doc: SCHEMA title TEXT content TEXT
OK
$ redis-cli FT.SEARCH idx:doc "退款" LIMIT 0 5
1) (integer) 1843
2) "doc:10086"
...
命令语法跟 Stack 7.4 完全一致,我们现有的 RediSearch 代码一行都不用改。这一点很重要,意味着迁移成本主要在运维侧而不是开发侧。
顺带测了下聚合和 JSON 索引,也都正常:
$ redis-cli FT.AGGREGATE idx:doc "*" \
GROUPBY 1 @category \
REDUCE COUNT 0 AS cnt \
SORTBY 2 @cnt DESC LIMIT 0 5
性能:官方说提升,我自己测了一遍
官方宣称 Redis 8 在吞吐上有明显提升。我用 redis-benchmark 和我们的真实数据集各测了一遍,机器 8C16G,单实例。
| 测试项 | Redis Stack 7.4 | Redis 8 预览 | 变化 |
|---|---|---|---|
| GET(redis-benchmark, 50 并发) | 142,800 QPS | 181,400 QPS | +27% |
| SET(同上) | 131,200 QPS | 164,900 QPS | +26% |
| FT.SEARCH 全文(1843 命中) | 3,140 QPS | 3,980 QPS | +27% |
| 向量检索 kNN(128 万条, topK=10) | 2,180 QPS | 2,610 QPS | +20% |
| 向量检索 P99 延迟 | 8.4 ms | 6.9 ms | -18% |
测试环境不严谨(预览版、单实例、没有其他负载),但趋势是对的:普通命令提升 25% 左右,查询类提升 20% 到 27%。官方说的是多线程 IO 的改进带来的,这个在连接数多的时候收益更明显。
我们生产环境的连接数在 600 左右(8 个业务实例,每个 75 个连接),属于能吃到的范围。等正式版出来我会再测一次。
几个要注意的点
内存占用没降,反而略涨
我原本期待内置之后内存管理会更好,实测下来向量索引的内存占用基本持平(14.23 GB vs 14.51 GB,多了 2%)。向量检索的内存大头是向量本身和索引结构,这个省不掉。真想降内存得靠量化,我们后来把向量从 float32 转成了 int8 量化存储,内存降到 4.6 GB,召回率从 0.981 掉到 0.962,可以接受。
# 量化:把 [-1,1] 的 float 映射到 [-127,127]
redis-cli> VADD qvec INT8 VALUES 4 15 -43 71 99 "怎么申请退款"
预览版别上生产
这个不用多说,但我还是提一句:预览构建里有两处我们触发了崩溃(一次是 VSIM 传空向量,一次是 VADD 传了维度不一致的向量)。虽然都是参数错误,但正确行为应该是报错而不是进程退出。等 GA 再看。
许可协议要看清楚
Redis 在 2024 年改过一次许可(从 BSD 改成 RSALv2 + SSPLv1 的双许可)。如果你是把 Redis 作为服务对外提供(比如做缓存托管产品),要特别注意 SSPL 的约束。我们自己内部使用不受影响,但法务那边还是过了一遍。
我们现在的计划
- 观察,不急着升。等正式版发布并且稳定两三个月。我们上一个 Redis 版本是 2024 年 3 月升的,节奏没必要这么快。
- 新场景优先试向量集合。客服问题去重这个新需求我们已经在预览版上跑了,等 GA 直接上。
- 存量索引不动。RediSearch 的索引文件在 Stack 和 8 之间兼容性还没完全确认,我们打算等 GA 后用主从同步的方式做灰度:8 作为从库挂载,对比查询结果,确认一致再切。
写在后面
现在回头看,《Redis 8 新特性与 AI 能力集成》本身不算多难,难的是线上真出问题那十分钟里的判断。经验都是这么来的。