Redis 长期被定位为「缓存」,但自 RediSearch 模块并入 Redis Stack 后,它同时具备了倒排索引、全文检索、聚合分析的能力。相比 Elasticsearch,RediSearch 的优势在于延迟极低(亚毫秒级)、部署极简(一个模块)、与既有 Redis 数据天然共处——你可以在同一份 Hash 上既做 HGET 又做全文搜索。
代价是能力边界更窄:没有复杂的相关性调优、没有分布式聚合、内存成本高。因此选型的核心问题不是「谁更强」,而是「你的检索需求是否落在 RediSearch 的能力圈内」。
本文从建索引讲起,覆盖字段类型、分词、查询语法、聚合、高亮与调优。
一、RediSearch 的定位与部署
1.1 能力边界
RediSearch 索引实时可见、查询亚毫秒级、分词器内置可扩展,但聚合只支持基础分组统计、分布式需客户端合并结果、相关性调优有限、索引常驻内存;Elasticsearch 则是近实时(默认 1 秒 refresh)、十毫秒级查询、分词器极丰富、聚合强大、原生分布式,但内存可落盘、运维更重。
1.2 部署方式
# Docker 启动 Redis Stack(含 RediSearch、RedisJSON、RedisTimeSeries)
docker run -d --name redis-stack -p 6379:6379 \
-v /data/redis-stack:/data \
redis/redis-stack-server:7.4.0-v0
# 验证模块已加载
redis-cli MODULE LIST
# 1) 1) "name" 2) "search" 3) "ver" 4) (integer) 20812
若使用自行编译的模块,在
redis.conf中通过loadmodule /opt/redis-stack/lib/redisearch.so加载,并用search-maxmemory 0控制索引内存上限(0 表示不限制)。RediSearch 的索引全部驻留内存,内存占用通常是原始数据的 1.2~3 倍(取决于字段数与是否 SORTABLE)。规划容量时务必预留 3 倍余量。
1.3 与既有 Redis 数据的关系
RediSearch 不复制数据,只在索引中保存倒排结构与必要的字段副本,原始数据仍在 Hash 或 JSON 中;删除索引不会删数据,删除数据则索引会残留(需要 FT.DEL 或依赖 FT.CREATE 的过滤条件清理)。
二、FT.CREATE 建索引
2.1 从 Hash 建索引
redis-cli HSET product:1001 name "iPhone 15 Pro" brand "Apple" price 8999 \
category "phone" stock 120 tags "5g,apple,flagship" created_at 1696000000
redis-cli HSET product:1002 name "小米 14 Ultra" brand "Xiaomi" price 6499 \
category "phone" stock 80 tags "5g,xiaomi,flagship" created_at 1696100000
redis-cli FT.CREATE idx:product ON HASH PREFIX 1 product: SCHEMA \
name TEXT WEIGHT 5.0 SORTABLE \
brand TEXT SORTABLE \
category TAG \
tags TAG SEPARATOR "," \
price NUMERIC SORTABLE \
stock NUMERIC \
location GEO \
created_at NUMERIC SORTABLE
2.2 从 JSON 建索引
redis-cli FT.CREATE idx:article ON JSON PREFIX 1 article: SCHEMA \
'$.title' AS title TEXT WEIGHT 3.0 \
'$.body' AS body TEXT \
'$.author' AS author TAG \
'$.views' AS views NUMERIC SORTABLE
ON JSON时字段用 JSONPath 指定,并用AS起别名(别名去掉$与点号,便于查询)。HASH 模式下字段名直接就是 Hash 的 field 名。
2.3 字段类型总览
TEXT 用于全文检索(分词后建倒排,关键选项 WEIGHT、NOSTEM、WITHSUFFIXTRIE);TAG 用于精确匹配的标签与枚举(SEPARATOR、CASESENSITIVE);NUMERIC 支持数值范围与排序;GEO 支持经纬度距离过滤;VECTOR 支持向量相似检索(TYPE、DIM、DISTANCE_METRIC);GEOSHAPE 支持多边形与点集合。
2.4 SORTABLE 的代价
SORTABLE 会把字段值额外存一份到索引(sortable value),使 SORTBY 无需回表,代价是内存翻倍:不设时以读取原文档为代价,设置后排序极快但多一份字段值,SORTABLE UNF 可更省内存但不做小写归一化。只对真正用于排序的字段加 SORTABLE,给所有字段都加会让内存膨胀 2 倍以上。
2.5 索引管理命令
FT.INFO idx 查看元数据与内存占用,FT._LIST 列出所有索引,FT.ALTER idx SCHEMA ADD f TEXT 动态加字段,FT.DROPINDEX idx [DD] 删索引(带 DD 同时删文档),FT.CREATE ... TEMPORARY 3600 创建自动过期的临时索引,FT.DICTADD 与 FT.DICTDEL 维护自定义词典。
redis-cli FT.INFO idx:product | grep -A2 -E 'indexing|num_docs|inverted_sz_mb'
# num_docs 100000
# inverted_sz_mb 42.5
# indexing 0
三、分词:中英文的差异处理
3.1 默认分词行为
RediSearch 默认按空白与标点切分,并做小写化与词干化。英文 running 与 run 可互相命中;中文则整段被当成一个 token——小米手机很好用 无法被 手机 命中。用 FT.EXPLAIN 可以查看一段文本被切分成了哪些 token,是排查分词问题的利器。
3.2 中文分词的三种方案
单字分词把每个汉字切成一个 token,无需词典、召回高但精度低且索引大;双字分词(bigram)用相邻两字组合,精度与召回平衡但索引膨胀;词典分词(jieba 等)精度最高,但需自定义构建词典。
3.3 客户端预分词
最通用的做法是在写入前由应用侧分词,把结果用空格连接后存入专用字段:
import jieba
title = "小米 14 Ultra 旗舰手机评测"
tokens = " ".join(jieba.cut_for_search(title))
# "小米 14 Ultra 旗舰 手机 评测"
r.hset("product:1002", mapping={"name": title, "name_tokens": tokens})
redis-cli FT.CREATE idx:product ON HASH PREFIX 1 product: SCHEMA \
name TEXT NOSTEM \
name_tokens TEXT NOSTEM \
brand TAG
NOSTEM关闭词干化,避免中文被误处理。查询时把用户输入也分词后拼接,再传给FT.SEARCH。
3.4 自定义词典与编译分词模块
用 redis-cli FT.DICTADD cn_dict 红米 骁龙 天玑 折叠屏 可把业务专有名词加入词典,影响分词与查询扩展。生产级方案是使用带 jieba 的 RediSearch 分支(如 redisearch-cn),在 SCHEMA 中指定 LANGUAGE chinese。
自编译模块会增加运维复杂度,升级 Redis 版本时需重新编译。除非检索质量要求极高,否则客户端预分词 + bigram 兜底已能满足大多数电商、内容场景。
3.5 TAG 与 TEXT 的选择
TEXT 会分词、大小写不敏感、支持前缀与模糊匹配,查询用 @f:word;TAG 不分词、整体匹配、支持多值(SEPARATOR 分隔),查询用 @f:{value},适合分类、品牌、标签。
常见错误:把「品牌」「分类」建为 TEXT,导致
Apple被词干化后匹配到apples,且无法用{...}精确语法。枚举型字段一律用 TAG。
四、FT.SEARCH 查询语法
4.1 基本形式
FT.SEARCH index query [NOCONTENT] [LIMIT offset num] [SORTBY field ASC|DESC]
[RETURN n field ...] [FILTER field min max] [INKEYS n key ...]
[HIGHLIGHT ...] [SUMMARIZE ...] [DIALECT 2]
# 全量搜索(危险,务必加 LIMIT)
redis-cli FT.SEARCH idx:product "*" LIMIT 0 10
# 品牌为 apple 且价格 5000~10000 的手机
redis-cli FT.SEARCH idx:product "@brand:apple @price:[5000 10000]" LIMIT 0 10
4.2 查询语法元素
@field:word 限定字段(如 @brand:apple);@f:(a b) 表示同字段内多词与关系;a b 是全局与;a OR b 是或;-a 是排除;"a b" 是短语(需同序相邻);a* 是前缀;%word% 是编辑距离 1 的模糊匹配、%%word%% 是距离 2;"pro max"~2 表示词间最多隔 2 个词的近似匹配。
4.3 TAG、NUMERIC 与 GEO 过滤
# 单标签与多标签(或)
redis-cli FT.SEARCH idx:product "@category:{phone}"
redis-cli FT.SEARCH idx:product "@tags:{apple OR xiaomi}"
# 数值区间(含边界,inf 表示无穷;( 表示开区间)
redis-cli FT.SEARCH idx:product "@price:[5000 10000]" LIMIT 0 5
redis-cli FT.SEARCH idx:product "@price:[(5000 (10000]" LIMIT 0 5
# 地理位置:以某点为中心 5km 内
redis-cli FT.SEARCH idx:product "@location:[-122.4194 37.7749 5 km]" LIMIT 0 5
4.4 排序、分页与返回字段
# 按价格升序,只返回名称与价格
redis-cli FT.SEARCH idx:product "@category:{phone}" \
SORTBY price ASC LIMIT 0 10 RETURN 2 name price
# 不要内容,只要主键
redis-cli FT.SEARCH idx:product "@brand:apple" NOCONTENT LIMIT 0 100
# 过滤与排序混用
redis-cli FT.SEARCH idx:product "*" FILTER price 3000 8000 \
SORTBY stock DESC LIMIT 0 10
LIMIT offset num 分页,offset 越大越慢;SORTBY field 排序要求字段是 SORTABLE;RETURN n f... 精简返回减少传输;NOCONTENT 只返回 key 最快;FILTER 做数值与地理的后置过滤,能写进 query 表达式就优先写进去;TIMEOUT ms 设置查询超时防慢查询拖垮实例。
深分页是常见性能杀手:
LIMIT 10000 10需跳过 1 万条结果。若业务需要,改用游标式设计(按created_at或 ID 区间翻页)而非 offset。
4.5 查询解析与防注入
# FT.EXPLAIN 展示查询如何被解析为倒排索引的求交并
redis-cli FT.EXPLAIN idx:product "@brand:apple @price:[5000 10000]"
# INTERSECT { UNION { apple } NUMERIC { 5000 10000 } }
DIALECT 2 还支持参数化写法:FT.SEARCH idx "@name:$q" PARAMS 2 q "iphone" DIALECT 2。
用
DIALECT 2可避免用户输入中的特殊字符被当作语法解析,是防注入的正确姿势。用户输入一律走PARAMS传参,绝不字符串拼接进 query。
五、FT.AGGREGATE 聚合分析
5.1 基本形式与分组统计
FT.AGGREGATE index query
[GROUPBY n field [REDUCE func nargs arg ... [AS name]] ...]
[SORTBY n field [ASC|DESC] ...]
[APPLY expr AS name]
[FILTER expr]
[LIMIT offset num]
# 按品牌分组,统计商品数与均价
redis-cli FT.AGGREGATE idx:product "*" \
GROUPBY 1 @brand \
REDUCE COUNT 0 AS cnt \
REDUCE AVG 1 @price AS avg_price \
REDUCE MAX 1 @price AS max_price \
SORTBY 2 @cnt DESC \
LIMIT 0 10
5.2 可用 REDUCE 函数
COUNT 组内计数,COUNT_DISTINCT 去重计数,SUM 与 AVG 求和与均值,MIN 与 MAX 取极值,STDDEV 算标准差,QUANTILE 取分位数(第二个参数为 q,如 0.95),TOLIST 收集为列表,FIRST_VALUE 取组内首个值,RANDOM_SAMPLE 随机抽样。
# 价格分位数(p50 / p95 / p99)
redis-cli FT.AGGREGATE idx:product "@category:{phone}" \
REDUCE QUANTILE 2 @price 0.5 AS p50 \
REDUCE QUANTILE 2 @price 0.95 AS p95 \
REDUCE QUANTILE 2 @price 0.99 AS p99
5.3 APPLY 表达式
# 计算折扣后价格并四舍五入
redis-cli FT.AGGREGATE idx:product "@category:{phone}" \
APPLY "@price * 0.8" AS final_price \
APPLY "round(@final_price)" AS final_round \
SORTBY 2 @final_price ASC \
LIMIT 0 5
支持的表达式函数包括 sqrt、log、abs、ceil、floor、round、upper、lower、substr、format、split、exists、case 等,语法类似 SQL 表达式。
5.4 聚合的执行限制
聚合的结果集大小默认受 MAXAGGREGATERESULTS 限制(超出被截断),不能跨索引 JOIN,不支持窗口函数 OVER 语义,分组数过大时内存消耗显著。
redis-cli FT.CONFIG SET MAXAGGREGATERESULTS 10000000
redis-cli FT.CONFIG GET '*'
六、高亮与摘要
6.1 HIGHLIGHT
redis-cli FT.SEARCH idx:product "iphone" \
HIGHLIGHT FIELDS 1 name TAGS "<b>" "</b>" \
LIMIT 0 5
返回结果中命中的词会被包裹成 <b>iphone</b>,前端可直接渲染。
6.2 SUMMARIZE
# 对长正文生成摘要片段,最多 3 段,每段 30 词
redis-cli FT.SEARCH idx:article "redis" \
SUMMARIZE FIELDS 1 body FRAGS 3 LEN 30 SEPARATOR " ... " \
RETURN 3 title body published \
LIMIT 0 5
FRAGS 控制摘要片段数(默认 3),LEN 控制每片段词数(默认 20),SEPARATOR 是片段间分隔符(默认 ...)。
HIGHLIGHT与SUMMARIZE都在服务端做字符串处理,对长文本有明显开销。列表页建议只对摘要字段启用,详情页再单独取全文。
七、索引管理与性能调优
7.1 内存构成
FT.INFO 会列出 inverted_sz_mb(倒排索引)、total_indexing_time(首次建索引耗时)、doc_table_size_mb(文档表)、sortable_values_size_mb(SORTABLE 副本)与 records_per_doc_avg。倒排索引占比受字段数与分词后 token 数影响;文档表随文档数与主键长度增长;SORTABLE 值等于所有可排序字段的副本大小;偏移向量支撑短语查询。
7.2 调优手段
- 精简索引字段:只索引真正用于检索/排序的字段
- TEXT 换 TAG:枚举型字段用 TAG,索引体积可降一个数量级
- 谨慎 SORTABLE:每个 SORTABLE 字段都存一份副本
- 控制分词粒度:中文单字分词索引最大,bigram 次之
- 及时清理:过期数据用
FT.DEL或设 TTL,避免索引与数据不一致 - 分片部署:Redis Cluster 下索引分散到各分片,
FT.SEARCH需客户端合并结果
7.3 查询侧优化
深分页(LIMIT 10000 10)应改为游标或区间翻页;通配前缀(a*)会扫描大量词条,需加 WITHSUFFIXTRIE 或收紧前缀;模糊查询(%word%)CPU 开销高,只在短词上使用;高基数 GROUPBY 应预聚合或限制分组数;查询必须强制带 LIMIT,并用 TIMEOUT 防止慢查询阻塞实例。
redis-cli FT.SEARCH idx:product "@name:*x*" TIMEOUT 50
7.4 索引重建与切换
修改 SCHEMA 的字段类型(如 TEXT 改 TAG)必须重建索引。生产环境用双索引切换:先 FT.CREATE idx:product_v2 指向同一批数据,用 FT.INFO 观察 indexing 归零后灰度切流量,验证无误再 FT.DROPINDEX idx:product 删除旧索引。
八、与向量检索的组合
纯关键词检索无法理解语义(搜「续航长」匹配不到「大电池」),纯向量检索又缺乏精确过滤能力。混合检索用 TAG/NUMERIC 先做硬过滤,再用向量做语义排序,是当前 RAG 与推荐系统的主流方案。
8.1 建带向量的索引
redis-cli FT.CREATE idx:doc ON HASH PREFIX 1 doc: SCHEMA \
title TEXT \
category TAG \
price NUMERIC \
embedding VECTOR HNSW 6 \
TYPE FLOAT32 DIM 768 DISTANCE_METRIC COSINE \
M 16 EF_CONSTRUCTION 200
TYPE 指定向量元素类型(FLOAT32 最常用),DIM 必须与 embedding 模型维度一致,DISTANCE_METRIC 可选 COSINE、L2 或 IP,M 是每节点连接数(1664,越大越准越占内存),400,越大越准越慢)。EF_CONSTRUCTION 是建索引时的搜索宽度(100
8.2 混合查询
# 先用 TAG 过滤,再按向量距离排序(KNN 10 个近邻)
redis-cli FT.SEARCH idx:doc "(@category:{phone})=>[KNN 10 @embedding \$vec AS score]" \
PARAMS 2 vec "<binary-vector>" \
SORTBY score ASC \
RETURN 3 title score \
DIALECT 2
向量必须以二进制形式传入
PARAMS,且字节序为 little-endian float32。客户端 SDK 通常提供封装。关于向量索引的深入细节,可参考本专题的 Redis 向量检索 一文。
九、生产实践与选型建议
9.1 数据一致性
RediSearch 索引与原始数据是两套存储,任何绕过 Redis 的数据变更(如直接从 RDB 恢复、外部导入)都需要重建索引。写入路径必须是「先写 Hash/JSON,再写索引」的原子组合。可用 FT.INFO 的 num_docs 与 DBSIZE 对比校验一致性。
9.2 高可用与容量规划
索引随 RDB 保存、随写命令复制到副本,因此重启后无需重建;集群下索引定义需在每个分片创建;模块版本必须与 Redis 版本匹配;备份只需备份 RDB,但恢复后需用 FT.INFO 验证。容量上,10 万文档 5 字段约 3080MB(2GB 实例即可),100 万文档约 300800MB(4GB),1000 万文档约 3~8GB(16GB 加集群),1 亿文档则需 30GB 以上并必须集群分片。
9.4 选型决策
需要全文检索时,若数据量在 1000 万以内、延迟要求毫秒级、且已有 Redis,RediSearch 是运维成本最低的选择;若需要复杂相关性调优、多维聚合或多语言分词,应选 Elasticsearch;若需要语义检索,则用 RediSearch 加向量,或选用专用向量库(Milvus、Qdrant)。
9.5 上线检查清单
- 枚举字段已用 TAG 而非 TEXT,SORTABLE 只加在真正排序的字段上
- 中文分词方案已选定并验证召回率
- 所有查询带
LIMIT,用户输入走PARAMS+DIALECT 2,深分页已改游标翻页 - 已配置
TIMEOUT与慢查询监控,FT.INFO的内存占用已纳入容量看板 - 索引重建流程已演练(双索引切换)
结语
RediSearch 把「缓存」升级成了「可检索的数据平台」,用极低的延迟和运维成本覆盖了大部分中小规模的检索需求。核心要点回顾:
- 先分清 TEXT 与 TAG:全文检索用 TEXT,枚举精确匹配用 TAG,选错会让索引膨胀且查询失准
- 中文要显式处理:默认分词对中文无效,客户端预分词是最省事的通用解,词典分词是精度上限
- 查询必须参数化:用户输入一律走
PARAMS与DIALECT 2,既防注入也避免特殊字符破坏语法 - SORTABLE 有代价:每个可排序字段都会额外存一份值,只给真正排序的字段加
- 深分页要避开:offset 越大越慢,改用基于时间戳或 ID 的游标翻页
- 聚合能力有限:GROUPBY 与 REDUCE 够用但不如 ES,高基数分组要预聚合
- 向量与关键词互补:硬过滤用 TAG/NUMERIC,语义排序用 KNN,混合检索才是生产答案
RediSearch 的哲学是「够用就好」:它不追求 ES 那样的极致灵活,而是把 80% 的检索场景压缩进一个模块、一份内存、一次部署。当你的数据规模在千万级以内、延迟要求在毫秒级、团队不想维护一套独立搜索集群时,它就是最优解。超过这个边界,再考虑专用搜索引擎。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。