Sonic — 极简前缀搜索引擎
待复核Sonic 是法国 Crisp 公司的工程师 Valerian Saliou 用 Rust 写的”超轻量”搜索后端。一句话:几十 MB 内存就能搜几十万条文档。
日常类比:
- elasticsearch 像超市大型货架——什么都摆得下,但要租大仓库
- meilisearch 像便利店——挑着摆,但门店还是得几百平
- Sonic 像随身书包——只能装少量东西,但装得下、走得动、扔哪都能跑
定位关键词:schema-less / 前缀搜索 / 自动补全 / 几 MB 级内存占用。
不是给”全公司日志检索”准备的,是给”客服聊天搜索 / 应用内搜联系人 / IoT 本地索引”准备的。
不理解 Sonic 的设计取舍,下面这些事会困惑:
- 极致轻量:起步 5 MB 堆内存,百万条目也就几十 MB——给小 SaaS 和嵌入式一个新选项
- FST 工程化典范:BurntSushi 的
fstcrate 在工业搜索里怎么用,读 Sonic 一遍就懂 - 写慢读快的极端例子:consolidation 机制把”FST 不可变”的代价摊到后台,前台读起来飞快
- 轻量服务端组件三件套:Rust + 自定义文本协议 + 嵌入式 KV 是这个时代很多基础组件的标配
Sonic 的”为什么这么省内存”可以拆成 三件事:
1. FST 自动机做词典
Section titled “1. FST 自动机做词典”所有索引过的词被压进一个有限状态转换器(FST,Finite State Transducer)。可以理解成把字典里所有词共享前缀和后缀都合并的一棵图。
类比:日常的 trie 是”词共享前缀”,FST 是”词共享前缀 + 共享后缀”。比如 running / runner / run 共享 run,running / singing 共享 ing 后缀。结果是几十万词压成几 MB。
FST 还自带一个能力:Levenshtein 自动机——查”和某词差 1 个编辑距离的所有词”是 O(查询长度),于是拼写容错和模糊搜索几乎免费。
2. RocksDB 存”词到文档”的映射
Section titled “2. RocksDB 存”词到文档”的映射”FST 只存”哪些词存在”,真正的倒排索引(词 → 文档 ID 列表)放在 RocksDB 里。两层分工:
- FST = 词典查找层(in-memory、超紧凑、不可变)
- RocksDB = 文档关联层(持久化、可写、嵌入式 KV)
读流程:用户搜 mat,先在 FST 里找前缀匹配的所有词(matrix / material / …),然后去 RocksDB 拿这些词对应的文档 ID 集合,合并返回。
3. consolidation:用”重建”换”不可变”
Section titled “3. consolidation:用”重建”换”不可变””FST 不可变意味着新加一个词必须重建整个 FST。Sonic 的解决方案:
- 新 push 的词先落在内存里的 pending(未 consolidate 前不进主 FST 文件)
- 定时(默认 180 秒)触发 consolidation——把 pending 合并进主 FST,整图重建落盘
- 查询可同时看「主 FST + pending」;重建吃 CPU,大图时前台会感觉卡顿
这就是 Sonic”写慢读快”的根因——写不是立即可见,但读主要是 O(查询长度) 的 FST 查找。
案例 1:起服务 + push 文档
Section titled “案例 1:起服务 + push 文档”# 用官方 Docker 镜像,配一份 config.cfgdocker run -p 1491:1491 valeriansaliou/sonic:v1.4.0通过 Sonic Channel 协议(telnet 友好的文本协议)push 文档:
> START ingest SecretPassword< CONNECTED <sonic-server v1.4.0>> PUSH messages user-1 conv-42 "hello, can you help with my order"< OK三层命名空间:messages(collection)/ user-1(bucket)/ conv-42(object ID)。文档内容是最后那串字符串,引擎自己分词、入索引。
案例 2:search 和 suggest
Section titled “案例 2:search 和 suggest”> START search SecretPassword< CONNECTED> QUERY messages user-1 "help orde"< EVENT QUERY xxx conv-42> SUGGEST messages user-1 "hel"< EVENT SUGGEST xxx hello help helperQUERY 是全文搜索(命中文档 ID),SUGGEST 是前缀补全(命中词)。两者都走 FST,但出口不同:QUERY 再去 RocksDB 取文档 ID,SUGGEST 直接返回 FST 里的匹配词。
案例 3:等 consolidate 才搜得到
Section titled “案例 3:等 consolidate 才搜得到”- PUSH 一条新词后立刻 QUERY → 常搜不到(还在 pending)。
- 控制通道发
TRIGGER consolidate(或等默认 180 秒)→ pending 并进主 FST。 - 再 QUERY → 命中。
consolidate_after调短=更及时但更费 CPU;冷数据可调长。
# config.cfg 片段(官方默认)[store.fst.pool]inactive_after = 300[store.fst.graph]consolidate_after = 180- 写入不是立刻可见:默认 180 秒才 consolidate。PUSH 完立刻 QUERY 可能空;调短
consolidate_after或TRIGGER consolidate。 - 不支持 BM25 等相关性评分:返回顺序近乎 FST 遍历,不是相关度;要排序得自己后处理。
- 中文需要预分词:自带 tokenizer 走 rust-stemmers,对中文基本无效;客户端先分词或改源码接 jieba。
- schema-less 的代价:不能”只搜 title”,只能在 collection / bucket 层切;更细就拆 collection。
- consolidation 吃 CPU:大图重建可能卡几秒,不是无限 STW,但生产要盯延迟尖峰。
适用 vs 不适用场景
Section titled “适用 vs 不适用场景”适用:
- 客服聊天 / 站内消息搜索(作者本职场景就是 Crisp 客服系统)
- 中小博客 / 文档站搜索(< 10 万条目,预算 < 64 MB 内存)
- 自动补全场景(IDE / 搜索框前缀建议)
- IoT 设备本地索引(内存极紧)
不适用:
- 需要复杂相关性排序 / facet / 聚合 → 用 meilisearch 或 elasticsearch
- 中文为主的全文搜索(不预分词不可用)
- 写入要求秒级可见 → 改 meilisearch
- 需要 SQL 风格条件过滤 → 任意一个都比 Sonic 合适
历史小故事(可跳过)
Section titled “历史小故事(可跳过)”- 2019 年:Crisp 工程师 Valerian Saliou 开源 Sonic,定位「几 MB 内存的 ES 替代」,服务客服聊天搜索。
- 同一时期:站在 BurntSushi
fstcrate 与 RocksDB 上,用自定义 Sonic Channel 文本协议做 ingest / search / control。 - 之后:功能刻意不加 BM25 / facet;社区把它当「前缀补全 + 轻量全文」组件,而不是通用搜索中台。
- “功能少换资源省”是合理取舍——不是所有场景都需要 ES 全功能
- FST 是轻量词典/前缀/模糊查找的利器,压缩率极高
- 不可变结构 + 后台重建 适合读多写少(思路近 lsm-tree)
- 自定义文本协议 仍有市场:telnet 可调、解析便宜、客户端简单
- Rust + 嵌入式 KV + 内存自动机 是当代轻量基础组件常见组合
- 仓库:valeriansaliou/sonic
- 作者发布博客:Announcing Sonic — A super light alternative to Elasticsearch
- FST crate:BurntSushi/fst(读完这个再读 Sonic 几乎无门槛)
- Sonic Channel 协议规范:仓库
protocol.md - BurntSushi 关于 FST 的长文:Index 1,600,000,000 Keys with Automata and Rust(教科书级)
- meilisearch —— 同样 Rust 写、定位”开发者友好搜索”,但功能更全代价是内存大几倍
- elasticsearch —— 工业级全功能搜索,Sonic 是其轻量替代
- the-silver-searcher —— 命令行版的”轻量搜索”,思路一脉相承
- minisearch —— JS 实现的迷你前缀搜索库,思路同样是 FST 的弱化版
- opensearch —— ES fork,重量级全文搜索
- essentia —— Essentia — 音乐信息检索的 C++/Python 工具箱