Typesense 信息提取与性能评估
刚开始用 Typesense 做中文搜索的时候,我一度以为用个 locale: zh 就完事了, 直到线上数据一多,才发现分词效果、召回质量、内存占用这些细节,全都绕不过去。
ICU 自带的分词规则很稳,但在垂直领域又总觉得差那么一点点,Jieba 之类外部分词器看起来很香, 却会把索引体积和维护成本一起拉上来。
于是我花了一段时间把这几块细细拆开:从 ICU 的工作方式、和 Jieba 配合的几种实战玩法, 到 schema 怎么设计、更改哪些字段配置会直接反映在 RAM 账单上。
这篇文章,就是把这些试错和对比过程整理出来,既当一份给自己的备忘,也希望能帮后来者少踩一点坑。
Typesense 的信息提取:基于 ICU 的中文分词
locale + ICU:Typesense 的语言感知分词
Typesense 在做文本处理时,会根据字段的 locale 参数,
把文本交给 ICU(International Components for Unicode)库来分词和边界分析。
典型的中文字段 schema 大概是这样:
{
"fields": [{ "name": "title", "type": "string", "locale": "zh" }]
}
当 locale: "zh" 时:
- Typesense 会把文本交给 ICU 的中文分词规则;
- ICU 会尝试用内置的 cjdict.txt(中日联合词典)做词典分割;
- 如果字典匹配失败,就退回到 逐字切分 模式。
这套逻辑对所有 CJK 语言(Chinese / Japanese / Korean)都是类似的: 没有空格 → 用字典 + 字符边界做分词。
你可以把它理解成:先查词典,查不到就按字符滑动。
小结:不配置 locale 时,Typesense 默认按英文及欧语系规则分词,
对中文场景,一定要给需要搜索的字段加上 locale: "zh"。
ICU vs Jieba:稳定性 vs 行业适配能力
从搜索效果和工程维护的角度,ICU 和 Jieba 对中文分词的特点可以这么看:
-
ICU(Typesense 内置)
优点:
- 遵循 Unicode 规范,跨语言一致,同一套机制可以处理多语种;
- 分词结果相对稳定,升级成本低;
- 已经集成在 Typesense 内部,无需额外部署。
不足:
- 词典体量相对克制,细分行业术语覆盖有限;
- 对中文长词、新词、专有名词的识别能力一般;
- 某些场景命中失败时会退回到逐字切分,导致「召回多、相关性弱」。
-
Jieba / 其他中文专用分词器
优点:
- 词典易扩展,适合引入业务领域词库;
- 配合 HMM / 统计模型,对新词、组合词识别更友好;
- 一般来说,语义准确度和业务相关性更高。
不足:
- 需要你自己部署和维护;
- 预分词或查询分词都要额外的 CPU 时间;
- 如果把「切得很碎」的结果直接塞到全文搜索里,也有可能增加索引体积。
因此:
如果你需要快上手、多语言支持、维护成本低,用 ICU(locale: "zh")即可。
如果你对中文召回和排序很敏感,且有行业词表,可以考虑 配合 Jieba 自行分词,Typesense 只负责存和搜。
如何在 Typesense 中接入自定义分词结果?
Typesense 在查询接口中提供了 pre_segmented_query 参数:当它为 true 时,
Typesense 会认为你已经把查询分好词,只做基于空格的拆分,不再走内建 tokenization。
这给我们留出了两个常见用法:
-
仅对查询做自定义分词
文档:仍然用 ICU +
locale: "zh"建索引;查询:用 Jieba 把用户输入按空格拼好,传给 Typesense,并设置
pre_segmented_query: true。 -
对文档 + 查询同时做分词
文档索引前,用 Jieba 把中文拆成用空格分隔的 token 字符串;
Typesense 字段可以用默认
locale(甚至不设),因为你已经「模拟出有空格的语言」;查询同样用 Jieba 分词后传入 Typesense。
注意:如果你完全放弃 ICU,改用「自己分好词再喂给 Typesense」,就要自己保证不同字段、不同版本之间的分词策略和词典 完全一致,否则会出现「搜索不回文档」的坑。
典型中文 schema 设计示例
下面这个 schema,将搜索范围集中在标题、正文、分类等字段,同时利用 locale、infix、facet 等特性:
{
"name": "articles",
"fields": [
{ "name": "title", "type": "string", "locale": "zh" },
{ "name": "content", "type": "string", "locale": "zh", "infix": true },
{ "name": "category", "type": "string", "facet": true },
{ "name": "published_at", "type": "int64", "range_index": true },
{ "name": "image_url", "type": "string", "index": false, "optional": true }
]
}
关键点:
title/content是全文字段,启用locale: "zh",content还开了infix,支持子串匹配但会增大内存占用。category用作分面/过滤,设置facet: true;published_at开启range_index,更适合做时间范围过滤;image_url只作为元信息展示,index: false避免它占用宝贵的内存。
这就顺滑地过渡到下一节:哪些字段的索引会常驻内存?对 RAM 有多大影响?
Typesense 的性能评估
内存模型
Typesense 的一大特点,是采用索引常驻内存 + 原始文档落盘的混合模型:
- 可搜索字段的倒排 / 范围 / 向量索引会被加载到内存中;
- 原始 JSON 文档(包括未索引字段)主要存放在磁盘,按需读取。
官方给出的经验规则是:
如果仅包括需要搜索的字段的数据集大小为 X MB,
那么通常需要 2X–3X MB 的 RAM 来存储索引。
例如:
- 数据集总大小:5 GB;
- 你只在
title、content、category、published_at上做搜索 / 过滤,这部分累计大小约 1 GB; - 则推荐预留:2–3 GB RAM 给 Typesense 的索引。
社区实践也大致印证了这个量级:几百万到上千万文档的集合,索引常常会占用数 GB 甚至十几 GB 内存,尤其是在引入向量搜索之后。
字段配置如何影响 RAM 占用?
在 Typesense 里,某个字段是否进内存以及占多少内存,主要由 schema 配置决定。
几个关键开关:
-
index(是否索引)默认
index: true,意味着 Typesense 会为这个字段建立适当的索引结构(倒排 / 范围等)。如果一个字段只用于展示,不需要参与搜索、过滤、排序、分面,应该显式设为:
{ "name": "image_url", "type": "string", "index": false }这样它主要占磁盘,不占内存索引空间。
-
facet(分面)facet: true会让 Typesense 为该字段建额外的数据结构,用于快速统计各取值的计数;在「过滤维度多、分面字段多」的场景(比如电商)会明显增加内存使用。
-
range_index(范围索引)对时间戳、数值字段开启
range_index,可以加速区间过滤;内存开销相对可控,但仍然属于「要考虑」的项。
-
infix(子串搜索)infix: true让字段支持「包含」类搜索(如从中间匹配),文档中明确说明会带来显著的内存开销;一般只对少数关键字段开启,比如标题或短标签,不宜大面积使用。
-
向量字段(向量搜索场景)
引入 embedding 向量后,索引内存会与「向量维度 × 文档数」线性相关;
社区里迁移到 0.25.x 并启用向量搜索的案例,2.3M 文档就已经消耗了 12GB RAM,可见向量对内存的影响非常显著。
小结:
schema 配置不是「纯逻辑层设计」,它直接等价为「内存账单」。
每多一个 index=true、facet=true、infix=true,都在给 RAM 增压。
一套可落地的 RAM 估算流程
结合官方文档和社区经验,可以用下面这套简单流程来做 Typesense 的内存规划:
-
确定需要搜索的字段集合
只统计:参与全文检索、过滤、排序、分面的字段;
统计这些字段的压缩后大小,相加得到
X。 -
按 2–3 倍估算索引 RAM
初步估计:
RAM_index ≈ 2X–3X;若启用了
infix或大规模向量字段,可倾向上限甚至更高。 -
考虑副本数和写入峰值
如果部署多副本(比如 3 个节点),同一集合的索引会在每个节点各存一份;
写入/重建索引期间,内存可能短时高于稳态。
-
预留系统与缓冲空间
官方建议监控 RAM 使用,保证至少 15% 留给操作系统和其他进程;
简单做法:总内存至少为
RAM_index / 0.85。 -
实测校正
启动 Typesense 后,通过
/metrics.json、/stats.json以及 Prometheus 导出器持续观测:- 初始化加载完成后的 RSS;
- 索引构建过程中的峰值;
- 查询压力下的波动。
信息提取策略对性能的影响
信息提取(分词 / tokenization)看起来只是「搜索相关」,其实也会反向影响索引体积和查询性能:
-
分词越细,token 越多,索引越大
ICU 的词典模式相比逐字切分会更「省 token」;
若你用外部分词器把文本切得非常细(甚至带很多短停用词),索引体积会明显增大。
-
drop_tokens_threshold与召回/性能的平衡Typesense 会在结果过少时尝试丢弃部分 token,以保证有足够结果;可以通过
drop_tokens_threshold控制。设置为
0可以禁止丢 token,保证「所有关键词必须同时命中」,但可能增加扫描成本和响应时间。 -
错别字容忍(typo tolerance)
较高的 typo 容忍度会增加候选 token 匹配数,CPU 和内存压力上升;
Typesense 默认将 typo 数限制为 2,原因之一就是避免组合爆炸。
-
预分词查询(
pre_segmented_query)当你自己分好词再交给 Typesense,可以更精准地控制 token 数量;
对长查询(例如多句子组合搜索)尤其有利,能减少不必要的 token。