RediSearch 开发实战
摘要
- 本文介绍 Redis 扩展模块 – RediSearch 的使用方法
- 本文基于
redis-7.4.7,springboot-3.5.8 - 操作系统:
Amazon Linux 2023(内核 6.1) - Redis官网:https://redis.io/
- Redis 命令文档:https://redis.io/docs/latest/commands/
- RediSearch 的安装参见 Redis 扩展模块 -- RediSearch 的安装方法
- 示例代码:GitHub
RediSearch 命令
-
为了展示命令的使用方法,这里以JSON文档进行索引,初始化数据如下:
1 | JSON.SET user:10001 $ '{"name":"Alice Bob","age":28,"vip":"yes","orders":[{"amount":199.99,"status":"PAID"},{"amount":59.9,"status":"CREATED"}],"comment": "I have a phone","items":["SpringCloud技术指南","Shell脚本基础"]}' |
-
SpringBoot暂时没有支持 RediSearch ,你可以编写Lua脚本来实现相应的功能,另外 Redisson已经提供了对 RediSearch 的支持,下面结合命令给出代码示例。
1 | <!-- 引入 Redisson ,这里要注意,现在最新版是 4.0.0,需要 springboot 4.x --> |
-
通用代码
1 |
|
一、索引生命周期管理类
| 命令 | 作用 | 核心参数 | 示例 |
|---|---|---|---|
FT.CREATE |
创建索引 | 索引名、ON、PREFIX、SCHEMA | 见下 |
FT.ALTER |
给已有索引新增字段 | 索引名、SCHEMA ADD | 见下 |
FT.DROPINDEX |
删除索引 | 索引名、DD | 见下 |
FT.INFO |
查看索引信息 | 索引名 | 见下 |
FT._LIST |
列出所有索引 | 无 | 见下 |
1️⃣ FT.CREATE
-
FT.CREATE 基本语法
1 | FT.CREATE index_name |
-
FT.CREATE 参数说明表
| 参数 | 作用说明 |
|---|---|
index_name |
索引名称 |
ON HASH | JSON |
指定索引数据来源类型,默认 HASH |
PREFIX count prefix... |
指定索引 Key 前缀 |
FILTER filter |
对索引数据设置过滤表达式 |
LANGUAGE |
指定默认分词语言(默认 english,中文是 chinese) |
LANGUAGE_FIELD |
指定文档中的语言字段 |
SCORE |
设置文档默认评分 |
SCORE_FIELD |
从字段中读取评分 |
PAYLOAD_FIELD |
指定存储的二进制负载字段 |
MAXTEXTFIELDS |
允许更多 TEXT 字段(消耗更多内存) |
TEMPORARY seconds |
创建临时索引,超时后自动删除 |
NOOFFSETS |
不存储文本偏移量(节省内存) |
NOHL |
禁用高亮 |
NOFIELDS |
不保存字段内容 |
NOFREQS |
不保存词频信息 |
STOPWORDS count ... |
指定停用词 |
SKIPINITIALSCAN |
创建索引时不扫描已有数据 |
SCHEMA |
索引字段定义起始 |
AS alias |
字段别名 |
TEXT |
全文索引字段 |
TAG |
精确匹配字段 |
NUMERIC |
数值字段 |
GEO |
地理位置字段 |
VECTOR |
向量字段 |
SORTABLE |
允许排序 |
UNF |
不规范化排序 |
NOINDEX |
字段不参与索引 |
-
FT.CREATE 的核心语法
1 | FT.CREATE <index> |
-
创建索引示例
1 | FT.CREATE idx:user |
1 | rSearch.createIndex( |
-
索引字段的类型
| 字段类型 | 中文名称 | 功能说明 | 典型使用场景 | 备注 / 限制 |
|---|---|---|---|---|
| TEXT | 全文文本字段 | 支持对字段值进行全文检索(分词、相关度计算、模糊匹配等) | 文章内容、用户名、描述信息 | 支持权重(WEIGHT)、排序(SORTABLE)等选项 |
| TAG | 标签字段 / 精确匹配字段 | 支持精确匹配查询,适用于枚举值或离散分类 | 分类、状态、主键、类型字段 | 不分词;可自定义分隔符 |
| NUMERIC | 数值字段 | 支持数值范围查询 | 年龄、价格、分数、时间戳 | 支持区间查询([min max]) |
| GEO | 地理位置字段(点) | 支持以“点”为中心的半径范围查询 | 门店位置、用户位置 | 值格式必须为 "经度,纬度" |
| VECTOR | 向量字段 | 支持向量相似度搜索(KNN 等) | 语义搜索、推荐系统、Embedding 向量 | 需要 Query Dialect ≥ 2(RediSearch ≥ 2.4) |
| GEOSHAPE | 地理形状字段(多边形) | 支持多边形空间查询 | 行政区、商圈、地图区域 | 使用 WKT 格式;不支持 JSON 多值和 SORTABLE |
GEOSHAPE 字段补充说明
| 项目 | 说明 |
|---|---|
| 数据格式 | WKT(Well-Known Text)格式,如:POLYGON((x1 y1, x2 y2, ...)) |
| 坐标系统 | SPHERICAL(球面坐标,经纬度)FLAT(平面坐标,笛卡尔 X/Y) |
| 默认坐标系统 | SPHERICAL |
| 当前限制 | ❌ 不支持 JSON 多值 ❌ 不支持 SORTABLE 选项 |
索引字段类型选型建议
| 使用场景 | 推荐字段类型 | 说明 |
|---|---|---|
| 全文检索 | TEXT | 支持分词、相关度计算、模糊匹配等全文搜索能力 |
| 精确过滤 / 分类 | TAG | 精确匹配,不分词,适合枚举值、状态、类型等字段 |
| 区间筛选 / 排序 | NUMERIC | 支持数值区间查询与排序,适合价格、时间、分数等 |
| 附近的人 / 门店 | GEO | 基于经纬度点进行半径范围查询 |
| 语义搜索 / 向量召回 | VECTOR | 基于向量相似度(KNN)的语义搜索,需要 Dialect ≥ 2 |
| 复杂地理区域判断 | GEOSHAPE | 支持多边形(Polygon)区域查询,适合行政区、商圈等 |
哪些字段适合使用 SORTABLE
| 字段类型 | 是否适合 SORTABLE | 原因 |
|---|---|---|
| NUMERIC | ✅ 强烈推荐 | 数值排序最常见,如价格、时间、评分 |
| TAG | ⚠️ 视情况 | 一般用于过滤,排序意义不大 |
| TEXT | ⚠️ 谨慎 | 文本可能很大,内存开销高 |
| GEO | ⚠️ 特殊场景 | 通常按距离排序,更多依赖 GEOFILTER |
小贴士:如何在redis终端输入多行的命令?
- 先说结论:redis终端 不支持多行输入,但可以通过如下方式输入多行命令:
1 | redis-cli <<EOF |
- 这种方法是利用了 Bash 的能力,即
<<EOF后面的内容会作为标准输入,直到EOF为止,并通过\符换行。 - 如果在VSCode中,可以选中要合并的多行内容,然后通过
Ctrl + Shift + J快捷键将多行合并到一行。
2️⃣ FT.ALTER
⚠️ 只能 新增字段,不能修改或删除已有字段
1 | FT.ALTER idx:user SCHEMA ADD $.vip AS vip TAG |
1 | rSearch.alter( |
3️⃣ FT.DROPINDEX
⚠️ 删除索引
1 | FT.DROPINDEX idx:user |
1 | rSearch.dropIndex("idx:user"); |
4️⃣ FT.INFO
查看索引信息
1 | FT.INFO idx:user |
1 | IndexInfo info = rSearch.info("idx:user"); |
重点关注字段
| 字段 | 含义 | 解释 |
|---|---|---|
num_docs |
当前被索引、可被搜索的文档数量 | user:10001 ~ user:10010,10条文档 |
num_terms |
词典中唯一词项 term 的数量 主要来源于 TEXT(name) 和 TAG(vip, status) 字段去重之后的数量 |
name: alice, bob, carol, … vip: yes, no status: PAID, CREATED, CANCELLED |
num_records |
倒排索引中的记录条目总数 所有 term 在所有文档中的出现次数之和 |
JSON 数组字段 orders[],每个 order 都会生成独立的索引 entry 多值字段 × 多文档 = record 爆炸式增长 num_records 真正影响:内存、查询速度、聚合成本 |
5️⃣ FT._LIST
列出所有索引
1 | FT._LIST |
1 | List<String> indexes = rSearch.getIndexes(); |
二、查询类(核心)
| 命令 | 作用 | 适用场景 |
|---|---|---|
FT.SEARCH |
标准搜索 | 90% 场景 |
FT.AGGREGATE |
分组 / 统计 / 聚合 | 报表、分析 |
FT.HYBRID |
文本 + 向量混合 | 向量搜索 |
FT.EXPLAIN |
查询执行计划 | 调优 |
FT.EXPLAINCLI |
CLI 可读执行计划 | 调试 |
FT.PROFILE |
性能分析 | 慢查询 |
1️⃣ FT.SEARCH
-
FT.SEARCH 基本语法
1 | FT.SEARCH index query |
-
FT.SEARCH 参数说明表
| 参数 | 作用说明 |
|---|---|
index |
索引名称 |
query |
查询条件(类似 SQL WHERE) |
NOCONTENT |
仅返回文档 ID,不返回内容 |
VERBATIM |
禁用查询优化,完全按原查询执行 |
NOSTOPWORDS |
查询时不忽略停用词 |
WITHSCORES |
返回匹配文档的相关性评分 |
WITHPAYLOADS |
返回文档的 payload 数据 |
WITHSORTKEYS |
返回排序使用的 key |
FILTER |
数值字段过滤(范围查询) |
GEOFILTER |
地理位置范围查询 |
INKEYS |
仅在指定的 key 集合中搜索 |
INFIELDS |
仅在指定字段中搜索 |
RETURN |
指定返回的字段 |
SUMMARIZE |
返回字段内容摘要 |
HIGHLIGHT |
高亮匹配的关键词 |
SLOP |
设置短语查询中允许的词距 |
TIMEOUT |
设置查询超时时间 |
INORDER |
短语必须按顺序匹配 |
LANGUAGE |
指定查询语言 |
EXPANDER |
使用自定义查询扩展器 |
SCORER |
使用自定义评分函数 |
EXPLAINSCORE |
返回评分计算详情 |
PAYLOAD |
给评分函数传入自定义参数 |
SORTBY |
按指定字段排序 |
ASC / DESC |
排序方向 |
LIMIT |
分页控制 |
PARAMS |
参数化查询 |
DIALECT |
指定查询语法版本 |
-
FT.SEARCH 核心语法
1 | FT.SEARCH index |
标准搜索
1 | FT.SEARCH idx:user |
1 | SearchResult result = rSearch.search( |
1 | # 全文检索,从 所有 TEXT 字段中匹配,不区分大小写,即 phone/Phone/PHONE 都匹配 |
1 | SearchResult result = rSearch.search( |
1 | # 中文检索 |
1 | SearchResult result = rSearch.search( |
小贴士
- 看到中文输出是乱码,可以在登录客户端时加上 --raw
1 | redis-cli --user admin -a 123456 --raw |
SQL WHERE 与 RediSearch 查询语法对照表
-
x/y/name→TEXT/TAG(要加上{})字段,num→NUMERIC字段,查询语法用于FT.SEARCH/FT.AGGREGATE的查询字符串部分
| ID | SQL 条件 | RediSearch 对等写法 | 说明 |
|---|---|---|---|
| 1 | WHERE x = 'book' AND y = 'phone' |
@x:book @y:phone |
AND 为默认关系(空格) |
| 2 | WHERE x = 'book' AND y != 'phone' |
@x:book -@y:phone |
- 表示 NOT |
| 3 | WHERE x = 'book' OR y = 'phone' |
(@x:book) | (@y:phone) |
OR 必须显式写 | |
| 4 | WHERE x IN ('book','phone','hello world') |
@x:(book|phone|"hello world") |
多值 OR,短语需加引号 |
| 5 | WHERE y='book' AND x NOT IN ('book','phone') |
@y:book -(@x:book|@x:phone) |
NOT IN = NOT + OR |
| 6 | WHERE x NOT IN ('book','phone') |
-@x:(book|phone) |
整体否定 |
| 7 | WHERE num BETWEEN 10 AND 20 |
@num:[10 20] |
数值区间(闭区间) |
| 8 | WHERE num >= 10 |
@num:[10 +inf] |
+inf 表示正无穷 |
| 9 | WHERE num > 10 |
@num:[(10 +inf] |
( 表示不包含 |
| 10 | WHERE num < 10 |
@num:[-inf (10] |
小于(不含 10) |
| 11 | WHERE num <= 10 |
@num:[-inf 10] |
小于等于 |
| 12 | WHERE num < 10 OR num > 20 |
@num:[-inf (10] | @num:[(20 +inf] |
数值 OR |
| 13 | WHERE name LIKE 'Li%' |
@name:Li* |
前缀匹配 |
2️⃣ FT.AGGREGATE
分组 / 统计 / 聚合,语法
-
FT.AGGREGATE 基本语法
1 | FT.AGGREGATE index query |
-
FT.AGGREGATE 参数说明表
| 参数 | 作用说明 |
|---|---|
index |
索引名称 |
query |
查询条件(类似 WHERE) |
VERBATIM |
禁用查询优化 |
LOAD |
加载原始字段 |
TIMEOUT |
查询超时时间 |
GROUPBY |
分组字段(类似 SQL GROUP BY) |
REDUCE |
聚合函数(COUNT / SUM / AVG / MIN / MAX 等) |
AS |
聚合结果别名 |
SORTBY |
对聚合结果排序 |
ASC / DESC |
排序方向 |
MAX |
最大返回条数 |
APPLY |
对结果字段进行表达式计算 |
LIMIT |
分页控制 |
FILTER |
结果过滤(WHERE / HAVING) |
WITHCURSOR |
使用游标(大结果集) |
COUNT |
每次读取数量 |
MAXIDLE |
游标最大空闲时间 |
PARAMS |
参数化查询 |
DIALECT |
查询语法版本 |
-
FT.AGGREGATE 的核心语法
1 | FT.AGGREGATE <index> <query> |
-
示例 1:按 status 分组统计数量
1 | FT.AGGREGATE idx:user |
1 | AggregationResult result = rSearch.aggregate( |
-
示例 2:按 status 分组统计金额总和
1 | FT.AGGREGATE idx:user '*' |
1 | AggregationResult result = rSearch.aggregate( |
-
示例 3:同时输出数量 + 金额,并按金额降序,数量升序
1 | FT.AGGREGATE idx:user '*' |
1 | AggregationResult result = rSearch.aggregate( |
-
示例 4:按状态分组,筛选总金额 > 200
1 | FT.AGGREGATE idx:user '*' |
1 | AggregationResult result = rSearch.aggregate( |
语义执行顺序: SCAN → GROUPBY → REDUCE → FILTER (HAVING) → SORTBY → LIMIT
-
常见可用 REDUCE 函数
| 函数 | 用途 |
|---|---|
| COUNT | 行数 |
| SUM | 求和 |
| AVG | 平均 |
| MIN / MAX | 极值 |
| TO_LIST | 收集字段 |
3️⃣ FT.EXPLAIN/FT.EXPLAINCLI
查询执行计划, 不会考虑分组聚合,只看查询条件,它的定位是:看“会不会用索引、怎么用索引”,而不是看“跑得慢不慢”。
1 | FT.EXPLAINCLI idx:user '@status:{PAID} @amount:[100 +inf]' |
4️⃣ FT.PROFILE
性能分析,看“跑得慢不慢”。
1 | FT.PROFILE idx:user |
关注指标
-
第一优先级指标(直接决定查询是否慢)
| 指标 | 所在位置 | 含义 | 健康阈值 | 需要警惕 | 常见优化手段 |
|---|---|---|---|---|---|
| Total profile time | Shards → Total profile time | 查询在搜索引擎层面的总耗时(不含网络) | < 1 ms:优秀 1–10 ms:可接受 |
> 20 ms:需要优化 > 50 ms:结构性问题 |
减少结果集、拆分查询、优化索引字段 |
| Iterator Type | Iterators profile → Type | 查询执行策略 | INTERSECT / UNION | SCAN / WILDCARD | 增加可索引字段、避免模糊前缀 |
| Estimated number of matches | Child iterators | 索引层估算的候选文档数 | < 1k:理想 1k–10k:可接受 |
> 50k:过滤条件过宽 | 改用 TAG / NUMERIC / 冗余字段 |
| Loader Time | Result processors → Loader | 从 Redis 加载并反序列化文档 | < 30% Total time | > 40% Total time | RETURN 精简字段、NOCONTENT、缩小 JSON |
-
第二优先级指标(判断索引或模型是否合理)
| 指标 | 所在位置 | 含义 | 健康阈值 | 需要警惕 | 常见优化手段 |
|---|---|---|---|---|---|
| Number of reading operations | Iterators profile | 实际扫描的倒排表条目数 | ≈ Estimated matches | 明显大于结果数 | 提高字段选择性、拆分条件 |
| Estimated matches vs 实际结果数 | Iterator + Result | 索引估算精度 | 估算值略大于实际 | 估算 >> 实际(10x+) | 调整 schema,避免低基数字段 |
| Parsing time | Shards → Parsing time | 查询语法解析耗时 | < 0.1 ms | > 1 ms | 减少动态拼接、简化语法 |
| Pipeline creation time | Shards → Pipeline creation | 执行计划构建时间 | < 0.05 ms | > 0.5 ms | 减少子句、避免复杂嵌套 |
-
经验型“红线判断”速查表(非常实用)
| 现象 | 含义 | 是否必须处理 |
|---|---|---|
| Estimated matches > 100k | 索引字段选择错误 | 必须 |
| Iterator Type = SCAN | 全表扫描 | 必须 |
| Loader > 50% 总耗时 | 返回数据过重 | 强烈建议 |
| reading ops ≫ results | 倒排效率低 | 建议 |
| Total time > 20 ms | 在线查询风险 | 必须 |
-
核心执行策略型 Iterator(最重要)
| Type | 语义 | 典型触发场景 | 性能特征 | 调优结论 |
|---|---|---|---|---|
| INTERSECT | 多条件取交集(AND) | @a:{x} @b:[1 10] |
基于最小倒排表,效率最高 | 理想状态,优先使用 |
| UNION | 多条件取并集(OR) | @a:{x|y} |
候选集扩大,随 OR 数量线性增长 | 可接受,避免过多 OR |
| SCAN | 全量扫描 | 字段未索引 / JSON Path 不可索引 | O(N),文档越多越慢 | 必须消灭 |
| WILDCARD | 无过滤条件 | "*"、@field:* |
候选集=全部文档 | 仅限分析,线上慎用 |
-
索引访问型 Iterator(INTERSECT / UNION 的子节点)
| Type | 对应字段类型 | 示例查询 | 性能特征 | 备注 |
|---|---|---|---|---|
| TAG | TAG | @status:{PAID} |
哈希倒排,最快 | 强烈推荐 |
| NUMERIC | NUMERIC | @amount:[100 +inf] |
跳表范围扫描 | 范围越窄越快 |
| TEXT | TEXT | @name:alice |
分词匹配 | 分词数影响性能 |
| GEO | GEO | @loc:[13.4 52.5 10 km] |
空间索引 | 半径影响结果数 |
| VECTOR | VECTOR | =>[KNN 10 @v $q] |
向量搜索 | 常与 FILTER 联用 |
| IDLIST | 内部 | 小结果集 | 枚举 ID | 自动优化 |
-
逻辑 / 结构型 Iterator
| Type | 含义 | 触发示例 | 性能影响 | 是否常见 |
|---|---|---|---|---|
| NOT | 取反 | -@status:{PAID} |
依赖全集 | 较少 |
| OPTIONAL | 可选子句 | ( @a:{x} )? |
增加候选集 | 少见 |
| EMPTY | 无匹配 | 条件不可能满足 | 极快 | 偶发 |
| PHRASE | 短语匹配 | "hello world" |
比 TEXT 慢 | 低频 |
| PREFIX | 前缀匹配 | abc* |
可能退化 | 需谨慎 |
三、别名管理(生产必备)
| 命令 | 作用 |
|---|---|
FT.ALIASADD |
添加别名 |
FT.ALIASDEL |
删除别名 |
FT.ALIASUPDATE |
更新别名指向 |
-
一个索引可以有多个别名,一旦创建别名,所有 FT.SEARCH / FT.AGGREGATE / FT.INFO 等命令都可以直接使用 alias
1 | # 添加别名: FT.ALIASADD alias index |
1 | rSearch.addAlias("alias:user", "idx:user"); |
索引别名的核心价值
-
- 索引无损重建(最典型场景)
- RediSearch 不支持在线修改 schema,字段变更通常需要重建索引,索引重建时业务将不可用。
- 有了别名,我们可以创建一个新的索引,创建好后将别名指向新的索引,旧索引不再使用,业务无影响。
1
2
3
4
5
6
7
8
9
10
111. 旧索引
FT.ALIASADD user_idx user_idx_v1
2. 新建索引
FT.CREATE user_idx_v2 ...
3. 数据回填完成后,原子切换
FT.ALIASUPDATE user_idx user_idx_v2
4. 删除旧索引(可选)
FT.DROPINDEX user_idx_v1 -
- 灰度 / 多版本并存
- 不同环境(dev / staging / prod)
- 不同 schema 版本
-
- 运维与发布规范化(强烈推荐)
- 在生产环境中:永远不要让业务代码直接使用物理索引名
1
2索引真实名: 业务名_版本_时间戳
索引别名 : 业务名
四、游标(大结果集)
| 命令 | 作用 |
|---|---|
FT.CURSOR READ |
分批读取 |
FT.CURSOR DEL |
删除游标 |
-
游标用于聚合查询(AGGREGATE)的大结果集分页/分批拉取,典型场景包括:
1 | GROUPBY / REDUCE 后结果集很大 |
-
通过
FT.AGGREGATE ... WITHCURSOR创建游标,通过FT.CURSOR READ获取结果。
1 | FT.AGGREGATE idx:user "*" |
1 | AggregationResult result = rSearch.aggregate( |
1 | public AggregationResult readCursor(String indexName, long cursorId, int count) { |
-
完整生命周期总结
1 | FT.AGGREGATE ... WITHCURSOR # 创建游标(并返回第一批 + cursorId) |
-
游标与 OFFSET/LIMIT 的区别
| 对比项 | 游标 | OFFSET / LIMIT |
|---|---|---|
| 性能 | 高(流式) | 大 OFFSET 性能差 |
| 适合大结果集 | 是 | 否 |
| 服务器状态 | 有 | 无 |
| 适用命令 | FT.AGGREGATE | FT.SEARCH |
五、词典 & 同义词(搜索增强)
-
词典(DICT)和同义词(SYNONYM)只对 TEXT / TAG 搜索生效
-
与
FT.AGGREGATE无关,但与FT.SEARCH强相关。
| 命令 | 作用 |
|---|---|
FT.DICTADD |
添加词 |
FT.DICTDEL |
删除词 |
FT.DICTDUMP |
查看词典 |
FT.SYNUPDATE |
更新同义词 |
FT.SYNDUMP |
查看同义词 |
词典
-
词典作用:控制“合法搜索词”,比如将词典的内容发送给用户,让用户只能搜索这些词。
- ⚠️ 词典本身不参与搜索匹配
- ⚠️ 只是为搜索增强提供数据来源
1 | # 示例:添加商品相关词 |
1 | rSearch.addDict("product_dict", "iphone", "mobile","phone","android","huawei"); |
同义词
-
同义词的作用: 让不同的搜索词 命中同一批文档
1 | 例如: |
-
添加 / 更新同义词(FT.SYNUPDATE)
1 | # 定义“手机”的同义词集合,注意同义词是绑定到 索引 的 |
1 | rSearch.updateSynonyms("idx:user", "phone_group", "iphone", "mobile", "phone"); |
-
此时我们再进行搜索,就会命中所有同义词
1 | FT.SEARCH idx:user 'phone' |
-
词典 + 同义词的组合使用
1 | 1. 用户输入: 手机 |
六、FT.SPELLCHECK(拼写纠错)
-
FT.SPELLCHECK 用于对用户输入的搜索词进行拼写纠错与相似词推荐
-
它并不会真正执行搜索,而是:
✅ 分析输入词是否存在于索引词典
✅ 如果不存在,基于编辑距离(Levenshtein)给出候选纠错词
✅ 支持自定义词典(Dictionary)增强效果 -
🎯 核心目标
| 能力 | 说明 |
|---|---|
| 拼写纠错 | iphone → ipohne |
| 模糊推荐 | 用户拼错关键词 |
| 搜索前置纠错 | 提升召回率 |
| 自动学习索引词 | 自动从索引词库构建词典 |
-
基本语法
1 | FT.SPELLCHECK index query |
-
参数说明表
| 参数 | 说明 | 默认值 |
|---|---|---|
| index | 索引名称 | 必填 |
| query | 用户输入词 | 必填 |
| DISTANCE | 最大编辑距离(1~4) | 1 |
| TERMS INCLUDE | 仅使用指定词典 | 全部 |
| TERMS EXCLUDE | 排除指定词典 | 无 |
-
示例
1 | FT.SPELLCHECK idx:user 'iphnoe' DISTANCE 2 |
1 | Map<String, Map<String, Double>> spellcheck = rSearch.spellcheck( |
-
score = 编辑距离 / max(len(用户输入词), len(候选词)),但并不是总是这样
| 输入 | 候选 | 编辑距离 | maxLen | score |
|---|---|---|---|---|
| phnoe | iphone | 2 | 6 | 2/6 = 0.333 ≈ 0.4 |
| phnoe | phone | 1 | 5 | 0.2 → ~0.8(内部权重可能反转或调整) |
| phnoe | have | “4” → 表示 phnoe 与 have 的编辑距离是 4 |
1 | phnoe → have |
-
建议策略
| 场景 | 建议 |
|---|---|
| 搜索框实时提示 | 1 |
| 搜索提交后纠错 | 2 |
| 模糊容错系统 | 2~3 |
-
词典增强(Dictionary Integration)
通过 FT.DICTADD / FT.DICTDEL 人工维护一个或多个词典,把“业务词汇”主动注入到 SpellCheck 的候选词空间中。
1 | 默认情况下: |
1 | FT.DICTADD dict:tech iphone ipad macbook airpods |
1 | Map<String, Map<String, Double>> spellcheck = rSearch.spellcheck( |
-
FT.SPELLCHECK 与 Suggest 的差异对比(关键)
| 维度 | FT.SPELLCHECK | FT.SUGGET |
|---|---|---|
| 目标 | 拼写纠错 | 前缀联想 |
| 输入 | 完整词 | 前缀 |
| 返回 | 相似词 | 补全词 |
| 数据来源 | 索引词典 | 独立 Trie |
| 是否排序 | 否(距离优先) | 是(score) |
| 是否模糊 | 编辑距离 | 前缀 + FUZZY |
| 使用阶段 | 搜索前修正 | 输入过程中 |
已废弃命令(不建议使用)
| 命令 | 状态 | 说明 |
|---|---|---|
FT.CONFIG GET |
Reids8 将其标记为 Deprecated | 使用 CONFIG GET search-* |
FT.CONFIG SET |
Reids8 将其标记为 Deprecated | 同上 |
FT.TAGVALS |
已经标记为 Deprecated | 改用 AGGREGATE |
-
⚠️ 这里要注意,只有
Redis 8.0.0以上版本支持CONFIG GET search-*。
FT.CONFIG GET * 和 CONFIG GET search-* 对应参数整理
-
1️⃣ 索引基本 / 文档表配置
| FT.CONFIG 参数 | FT.CONFIG 值 | CONFIG search-* 参数 | CONFIG 值 | 说明 |
|---|---|---|---|---|
| MINPREFIX | 2 | search-min-prefix | 2 | 自动补全最小前缀长度 |
| MINSTEMLEN | 4 | search-min-stem-len | 4 | 最小词干长度 |
| MAXDOCTABLESIZE | 1000000 | search-max-doctablesize | 1000000 | 最大文档表大小 |
| MAXSEARCHRESULTS | 1000000 | search-max-search-results | 1000000 | 搜索结果限制 |
| MAXAGGREGATERESULTS | unlimited | search-max-aggregate-results | 2147483648 | 聚合结果限制 |
| MAXEXPANSIONS | 200 | search-max-prefix-expansions | 200 | 前缀扩展限制 |
| MAXPREFIXEXPANSIONS | 200 | search-max-prefix-expansions | 200 | 前缀扩展限制(兼容) |
| RAW_DOCID_ENCODING | false | search-raw-docid-encoding | no | 文档ID是否原始编码 |
| UPGRADE_INDEX | Upgrade config for upgrading | N/A | N/A | 升级索引配置提示 |
-
2️⃣ 查询 / 评分相关配置
| FT.CONFIG 参数 | FT.CONFIG 值 | CONFIG search-* 参数 | CONFIG 值 | 说明 |
|---|---|---|---|---|
| TIMEOUT | 500 | search-timeout | 500 | 查询超时(ms) |
| ON_TIMEOUT | return | search-on-timeout | return | 超时策略 |
| DEFAULT_SCORER | TFIDF | search-default-scorer | BM25STD | 默认评分算法 |
| BM25STD_TANH_FACTOR | 4 | search-bm25std-tanh-factor | 4 | BM25 调整因子 |
| MULTI_TEXT_SLOP | 100 | search-multi-text-slop | 100 | 多字段查询 slop |
| DEFAULT_DIALECT | 1 | search-default-dialect | 1 | 查询方言 |
-
3️⃣ GC / 后台索引配置
| FT.CONFIG 参数 | FT.CONFIG 值 | CONFIG search-* 参数 | CONFIG 值 | 说明 |
|---|---|---|---|---|
| NOGC | false | search-no-gc | no | 禁用 GC |
| GC_POLICY | fork | search-fork-gc-policy (implicit) | fork | GC 策略 |
| FORKGC_SLEEP_BEFORE_EXIT | 0 | search-fork-gc-sleep-before-exit | 0 | fork GC 退出前睡眠 |
| FORK_GC_RUN_INTERVAL | 30 | search-fork-gc-run-interval | 30 | fork GC 执行间隔(秒) |
| FORK_GC_CLEAN_THRESHOLD | 100 | search-fork-gc-clean-threshold | 100 | fork GC 清理阈值 |
| FORK_GC_RETRY_INTERVAL | 5 | search-fork-gc-retry-interval | 5 | fork GC 重试间隔 |
| FORK_GC_CLEAN_NUMERIC_EMPTY_NODES | true | search-_numeric-compress | no | 清理空数值节点 |
| GCSCANSIZE | 100 | search-gc-scan-size | 100 | GC 每次扫描大小 |
-
4️⃣ 游标 / 线程 / 并发配置
| FT.CONFIG 参数 | FT.CONFIG 值 | CONFIG search-* 参数 | CONFIG 值 | 说明 |
|---|---|---|---|---|
| CURSOR_MAX_IDLE | 300000 | search-cursor-max-idle | 300000 | 游标最大空闲时间 |
| INDEX_CURSOR_LIMIT | 128 | search-index-cursor-limit | 128 | 单索引游标限制 |
| UNION_ITERATOR_HEAP | 20 | search-union-iterator-heap | 20 | UNION 查询堆大小 |
| NO_MEM_POOLS | false | search-no-mem-pools | no | 禁用内存池 |
| _FREE_RESOURCE_ON_THREAD | true | search-_free-resource-on-thread | yes | 线程释放资源 |
| search_min_operation_workers | N/A | search-min-operation-workers | 4 | 最小操作线程数 |
| search-workers | N/A | search-workers | 0 | 工作线程数 |
| search-workers-priority-bias-threshold | N/A | search-workers-priority-bias-threshold | 1 | 线程优先级偏置 |
| INDEXER_YIELD_EVERY_OPS | 1000 | search-indexer-yield-every-ops | 1000 | 索引器 yield 每操作数 |
-
5️⃣ 实验 / 内部 /其他
| FT.CONFIG 参数 | FT.CONFIG 值 | CONFIG search-* 参数 | CONFIG 值 | 说明 |
|---|---|---|---|---|
| EXTLOAD | nil | search-ext-load | “” | 外部加载模块 |
| FRISOINI | nil | search-friso-ini | “” | 中文分词初始化 |
| ENABLE_UNSTABLE_FEATURES | false | search-enable-unstable-features | no | 启用实验特性 |
| _PRINT_PROFILE_CLOCK | true | search-_print-profile-clock | yes | Profiling 开关 |
| _NUMERIC_RANGES_PARENTS | 0 | search-_numeric-ranges-parents | 0 | 数值范围父节点数 |
| VSS_MAX_RESIZE | 0 | search-vss-max-resize | 1024 | 向量索引最大重分配 |
| PARTIAL_INDEXED_DOCS | false | search-partial-indexed-docs | no | 部分索引文档标记 |
| _PRIORITIZE_INTERSECT_UNION_CHILDREN | false | search-_prioritize-intersect-union-children | no | 优先处理 INTERSECT/UNION 子节点 |
| _BG_INDEX_MEM_PCT_THR | 100 | search-_bg-index-mem-pct-thr | 100 | 后台索引内存阈值 |
| _NUMERIC_COMPRESS | false | search-_numeric-compress | no | 数值压缩 |
| _BG_INDEX_OOM_PAUSE_TIME | 0 | search-_bg-index-oom-pause-time | 0 | 后台索引 OOM 暂停时间 |