题记与说明
横纵综合报告 · 研究时间截面:2026-10-31
本文由两份章节文件综合而成:纵轴(发展脉络)与横轴(横向对比)。关键事实均核对官方文档、官方发布说明或可靠来源并附 URL;两章数据源不一致或未能在线核验之处,在正文与文末"核验边界"中明确标注,不作臆测。
评估透镜:全篇贯穿"个人知识库的最小方案"——单机、单人、一万到十万量级笔记/文档、低写入频率、无专职运维。
TL;DR
四条技术线索共享同一个祖先 Lucene(1999),三十年主线是"检索引擎化(独立服务、分布式、功能堆叠)"与"检索内建化(塞进数据库、零运维、随库迁移)"的拉锯。2026 年的横截面上:Elasticsearch 是能力天花板也是成本天花板(JVM heap 规则、进程族、三许可并存);PostgreSQL tsvector 顺带就有但原生无 BM25,相关性排序是结构性弱项;Tantivy 是"Lucene 的 Rust 重写",质量与嵌入性兼得但仍是 0.x 库形态、要自己组装;SQLite FTS5 零进程零网络、BM25 内建,是真正的"最小"。中文分词是四者共同短板,默认分词器都不做中文词级切分,都要外挂(simple/zhparser/IK/jieba-rs)。结论:个人知识库默认答案 = SQLite FTS5 虚表 + trigram 或 simple 分词;数据已在 PG 就用 tsvector + GIN(排序不满意再上 pg_search);自己做检索工具选 Tantivy;不要为单人知识库部署 Elasticsearch。规律:越靠近"数据住在哪里",检索就应该长在哪里。
一、纵轴:发展脉络
1.1 一个祖先,一对张力
把 SQLite FTS5、PostgreSQL tsvector、Elasticsearch、Tantivy 放在同一条时间轴上,最先浮现的事实是:它们共享同一个祖先——Apache Lucene。Lucene 是 1999 年 Doug Cutting 写下的第 5 个搜索引擎(此前他在 Xerox PARC 写过两个、Apple 和 Excite 各一个,见 Elastic 官方纪念文 Celebrating 20 years of Apache Lucene)。Elasticsearch 是 Lucene 之上的分布式服务器;Tantivy 自我定位是"受 Lucene 启发的 Rust 实现"(tantivy 仓库);而 SQLite FTS 系列与 PostgreSQL 的 tsvector,则是数据库世界对"检索能力应该内建在库里"这一命题的两条独立回答。
于是纵轴的主线可以概括为一对张力的三十年拉锯:检索引擎化(独立服务、分布式、功能堆叠) vs 检索内建化(塞进数据库、零运维、随库迁移)。个人知识库这类"单机、小规模、长期运行"的场景,恰好站在这对张力的天平最轻的一端——这也是本文第三节选型判断的出发点。
1.2 前史(1999–2007):Lucene 单极,与数据库内置检索的萌芽
1999–2002:Lucene 从个人项目到 Apache 顶梁。 Doug Cutting 于 1999 年写出 Lucene,最初托管在 SourceForge;Apache 软件基金会 2001 年接手,2002 年成为顶级项目(Wikipedia: Apache Lucene)。它确立的核心数据结构——倒排索引 + 分词器 + 评分器(早期为 TF-IDF)——至今仍是本课题四条线索的公共底座。
2001–2003:PostgreSQL 阵营的双人组。 莫斯科大学的 Oleg Bartunov 与 Teodor Sigaev 开始为 PostgreSQL 构建全文检索:先有 contrib 里的 tsearch 与外部项目 OpenFTS(PGCon 2007 两位作者的报告 记载,OpenFTS 自 0.35 版起改用 tsearch2)。约 2003 年进入 contrib 的 tsearch2 确立了 tsvector/tsquery 的数据模型与"解析器—词典—配置"的分层设计,这一模型延续至今。
2004:Elasticsearch 的伏笔。 Shay Banon 为妻子(一位厨师)做菜谱应用的搜索层,在 Lucene 之上写了封装库 Compass(Wikipedia: Elasticsearch)。谁也没想到这个厨房项目日后会成为检索引擎的代名词。
2006–2007:两枚关键落子。 2006 年 12 月 5 日发布的 PostgreSQL 8.2 引入 GIN(Generalized Inverted Index)索引访问方法,发布说明原文:"Add GIN (Generalized Inverted iNdex) index access method (Teodor, Oleg)"(PostgreSQL 8.2 Release Notes)——倒排索引正式进入 PG 内核,为一年后的核心化铺平道路。同年,CNET 的 Yonik Seeley 将基于 Lucene 的搜索服务器 Solr 开源并进入 Apache 孵化器,"把 Lucene 包成一个带 HTTP 接口的服务"这一模式被验证。2007 年 9 月 4 日,SQLite 3.5.0 带上了 FTS3 虚表模块,其原始代码由 Google 的 Scott Hess 贡献(SQLite FTS3/FTS4 官方文档)——嵌入式数据库第一次有了全文检索能力。
本阶段驱动因素: Web 时代文档量激增,Lucene 证明倒排索引可以做成通用库;而数据库用户受够了"数据在库里、检索要出库"的割裂。三条路同时出发:PG 选择自研内建,SQLite 借 Google 之力补课,Solr/Compass 选择外挂服务。
1.3 内建化与引擎化分道扬镳(2008–2015)
2008-02-04:PostgreSQL 8.3——全文检索转正。 发布说明写道:"Full text search is integrated into the core database system (Teodor, Oleg)","Text search has been improved, moved into the core code, and is now installed by default. contrib/tsearch2 now contains a compatibility interface."(PostgreSQL 8.3 Release Notes)。tsearch2 经五年孵化进入内核,tsvector/tsquery 成为标准类型,配合上一年的 GIN 索引,"在关系库里做搜索引擎"从此是一等公民。这是数据库内建化路线的标志性胜利。
2010:Elasticsearch 诞生,Compass 退役。 2010 年 2 月 8 日,Shay Banon 发布 Elasticsearch 首版,宣告博文题为 "You Know, for Search"(Wikipedia 引注)。同年 7 月他在博客《The Future of Compass & ElasticSearch》中说明:Compass 3.0 的重写最终演变成了 Elasticsearch——从 Java 库变为分布式、JSON/REST 的搜索服务。同年 Lucene 与 Solr 合并为单一 Apache 项目。Elasticsearch 站在 Lucene 肩上补齐了分布式(分片/副本)、近实时与 JSON 生态,恰好踩中移动互联网与日志分析(ELK 栈)两个爆发点。
2010-12-07:SQLite FTS4。 SQLite 3.7.4 交付 FTS4,官方文档明确它是"FTS3 的增强",两者共享绝大部分代码(SQLite FTS3/FTS4 文档)。
2012–2015:商业化提速与 FTS5 落地。 2012 年 Elasticsearch BV(后来的 Elastic NV)成立;2014 年 2 月 ES 1.0 GA 承诺稳定 API;2015 年 3 月公司更名 Elastic。2015 年 10 月 14 日,SQLite 3.9.0 发布,变更日志记载:"Added Full Text Search version 5 (FTS5) to the amalgamation, enabled using SQLITE_ENABLE_FTS5. FTS5 will be considered 'experimental'…for at least one more release cycle."(SQLite 3.9.0 Release Notes)。FTS5 是对 FTS3/4 的完全重写:内建 BM25 评分函数与 rank 列、全新的查询语法(NEAR、列过滤、前缀查询)、外部内容表与内容无关表(SQLite FTS5 文档)。如今 SQLite 文档与各语言封装库(如 Peewee、GRDB)均把 FTS3/4 称为 legacy、建议新应用直接用 FTS5。
本阶段驱动因素: 云与日志场景让"检索即服务"的规模化需求压倒一切,Elasticsearch 借势成为 DB-Engines 排名中最流行的企业级搜索引擎;与此同时,移动端 SQLite 的部署量决定了 FTS 必须以"零依赖、随 amalgamation 走"的方式存在——两条路线按各自的物理约束演化,互不干扰。
1.4 Rust 嵌入式引擎登场(2016–2020)
2016:Tantivy 出世。 GitHub 时间戳显示 tantivy 仓库创建于 2016-01-11(现属 quickwit-oss 组织,MIT 协议);crates.io 显示 0.1.0 发布于 2016-08-11,首发仓库还是作者的 github.com/fulmicoton/tantivy(crates.io API)。作者 Paul Masurel(fulmicoton)本来把它当作学 Rust 的副业项目,随后发表系列博文拆解内部实现,自述 "Tantivy is strongly inspired by Lucene"(fulmicoton.com)。早期版本还捆绑 C 语言 SIMD 压缩库,0.6.0(2018-06)起改为纯 Rust 实现——从 crates.io 元数据中各版本的语言构成与体积变化可以直接看出这条"去 C 化"轨迹。
同期的 PostgreSQL 与 SQLite: 2016 年 9 月 29 日发布的 PostgreSQL 9.6 引入短语检索算子 <->(FOLLOWED BY)与 <N> 距离限定(PostgreSQL Text Search 文档、Crunchy Data 实践文章);2018 年 10 月的 PG 11 提供 websearch_to_tsquery(),让用户可以像写网页搜索框那样输入"引号短语、OR、-排除"(PostgreSQL Text Search Types)。SQLite 一侧则持续打磨 FTS5 细节:3.34.0(2020-12)内建 trigram 分词器,3.43.0(2023-08)起支持 contentless 表的删除(contentless-delete)。
Elasticsearch 的登顶: 2016 年 10 月 26 日 Elastic Stack 5.0 发布,ELK 各组件统一版本号、直接从 2.x 跳到 5.0(Elastic 官方博客);2017 年收购 Swiftype 切入站点搜索;2018 年 10 月 5 日在纽交所上市。7.0 于 2019 年 4 月 10 日发布,底座升级 Lucene 8,引入 Block-Max WAND 加速 top-k 检索。更早一年的 2018 年,Rust 生态里 Meilisearch 已由 Clément Renault 立项(GitHub 仓库创建于 2018-04-23),定位"开箱即用的轻量搜索引擎",2023 年 2 月发布 1.0(meilisearch 仓库)——它与 Tantivy 一道,证明搜索引擎可以在 JVM 之外长成。
本阶段驱动因素: 三股力量汇合——(1) 云成本与 JVM 内存的沉重感让"小而快"重新有市场;(2) Rust 1.0(2015)后内存安全 + 无 GC 的系统语言成熟,正好匹配 Lucene 式的底层数据结构工程;(3) 容器与单机自托管文化回潮,"嵌入式检索库"重新成为合理选项。
1.5 许可证博弈、向量检索与 Rust 生态成势(2021–2026)
2021:Elasticsearch 开源史上最大的分叉。 2021 年 1 月 14 日,Elastic 宣布自 7.11 起将 Elasticsearch/Kibana 从 Apache 2.0 改为 SSPL + Elastic License 双许可,直接动因是 AWS 提供托管服务"未回馈社区"(Elastic 官方声明《Doubling down on open, Part II》与《Amazon: NOT OK》,见 Wikipedia 引注)。AWS 于 1 月 21 日宣布 fork,4 月更名 OpenSearch;OpenSearch 1.0 于同年 7 月 GA。2024 年 8 月 29 日,Elastic 以《Elasticsearch is Open Source, Again》宣布自 8.16.0 起新增 AGPLv3 许可选项,重回 OSI 认可的开源阵营(Elastic 官方博客)。这一回合的最大启示是:许可证本身成了产品战略,也促使用户认真评估"我真的需要 Elasticsearch 吗"。
向量检索的收编。 ES 8.0(2022 年 2 月)默认开启安全配置并引入原生向量/ANN 检索与 PyTorch NLP 模型(特性见官方 8.0.0 Release Notes:Security auto-configuration、kNN search、PyTorch 推理均在该页);到 9.0(2025-04-14 GA,Lucene 10 内核)BBQ 二值量化 GA,9.1 起 BBQ 成为向量检索默认,9.2(2025-10)引入磁盘版 DiskBBQ(Elastic 9.0 发布博客)。PostgreSQL 阵营以 pgvector 应对,SQLite 生态出现全文检索 + 向量混合检索的实践(Simon Willison 的 sqlite 实验合集:simonwillison.net/tags/sqlite)。全文检索从"独立赛道"变成大检索栈里的一层。
Tantivy 从库变成生态。 从 crates.io API 的完整版本史可见其稳定节奏:0.12(2020-02)→ 0.13(2020-08)→ 0.15(2021-06)→ 0.16(2021-08,仓库迁移至 tantivy-search 组织)→ 0.19(2022-12)→ 0.20(2023-06,引入无 schema 列存与聚合)→ 0.21(2023-09,引入 HNSW 向量检索)→ 0.22.0(2024-04)→ 0.24(2025 年,加入 SIMD 向量加速)→ crates.io 最新正式发布 0.25.0(2025-08-20,累计 60 个版本、下载量约 1,007 万,crates.io API);GitHub Releases 页面在本次检索时已将 0.26.x 标为 Latest(Releases)。截至 2026-10-31,tantivy 仍是 0.x 版本、未见 1.0——这刻画了它的自我定位:稳定可用,但保留演进自由。两个下游把它推上牌桌:其一是 Quickwit——基于 Tantivy 的 PB 级日志搜索引擎,2025 年 1 月 9 日被 Datadog 收购(Quickwit 公告、Datadog 博客);其二是 ParadeDB——2023 年 10 月以 pg_bm25 之名发布,把 Tantivy 引擎作为索引访问方法塞进 Postgres 扩展(后更名 pg_search),目标是"Postgres 内达到 Elasticsearch 级搜索质量"(ParadeDB)。
本阶段驱动因素: 云厂商与原厂的商业冲突把许可证推上主战场;LLM/RAG 让"关键词检索 + 向量检索"成为标配组合;Rust 基建完成成熟,Tantivy 系(Tantivy/Quickwit/Meilisearch/ParadeDB)从边缘实验变成并购标的——2025 年 Datadog 收购 Quickwit 是这一路线被主流工业界承认的节点。
1.6 时间线速览(合并两章数据,含 2026-10-31 当前版本)
| 年份 | SQLite | PostgreSQL | Lucene 系 | Rust 系 |
|---|---|---|---|---|
| 1999 | — | — | Doug Cutting 写下 Lucene | — |
| 2003–2004 | — | tsearch2 进 contrib | Compass 诞生(ES 伏笔) | — |
| 2006–2007 | 3.5.0 带 FTS3(2007-09) | 8.2 加 GIN(2006-12) | Solr 开源(2006) | — |
| 2008 | — | 8.3 全文检索进核心(2008-02-04) | — | — |
| 2010 | 3.7.4 带 FTS4(2010-12) | — | ES 首版(2010-02-08);Lucene/Solr 合并 | — |
| 2014–2015 | 3.9.0 引入 FTS5(2015-10-14) | — | ES 1.0 GA(2014-02);更名 Elastic(2015) | — |
| 2016–2017 | — | 9.6 短语检索 <->(2016-09) | ES 5.0 统一版本号(2016-10) | tantivy 仓库 2016-01、0.1.0(2016-08-11) |
| 2018–2019 | — | 11 加 websearch_to_tsquery(2018-10) | ES 上市(2018-10);7.0 + Lucene 8(2019-04) | Meilisearch 立项(2018);tantivy 去 C 化(0.6.0) |
| 2020–2021 | 3.34.0 trigram 分词器(2020-12) | — | SSPL 许可证,AWS 分叉出 OpenSearch(2021) | tantivy 0.12–0.16 稳定迭代 |
| 2022–2023 | 3.43.0 contentless-delete(2023-08) | — | ES 8.0 原生向量(2022-02);Meilisearch 1.0(2023-02) | tantivy 0.19(2022-12)、0.21 加 HNSW(2023-09);ParadeDB 发布 pg_bm25(2023-10) |
| 2024–2025 | — | 18(2025-09-25,当前) | 8.16.0 恢复 AGPL(2024-08);9.0/9.1/9.2(2025,Lucene 10 + BBQ) | Datadog 收购 Quickwit(2025-01);tantivy 0.24 SIMD(2025)、0.25.0(2025-08-20) |
| 2026 | 3.54.1(2026-10-16,当前) | — | — | tantivy 0.26.x 为 GitHub Releases Latest(截至 2026-10-31) |
二、横轴:横向对比(2026-10-31 截面)
2.1 对象界定与一句话定位
| 对象 | 形态 | 一句话定位 |
|---|---|---|
| SQLite FTS5 | 数据库内置虚拟表模块(嵌入式) | "零部署"的全文检索:跟随 SQLite 文件走,BM25 内建 |
| PostgreSQL tsvector | 数据库内建类型 + 索引(C/S 架构) | "顺带就有"的全文检索:数据已在 PG 里时最顺手,但原生没有 BM25 |
| Elasticsearch | 独立搜索引擎服务(JVM/Lucene) | 检索领域的事实标准重型方案:能力最全,运维成本最高 |
| Tantivy | Rust 搜索引擎库(非服务) | "Lucene 的 Rust 重写":性能约为 Lucene 2 倍(厂商自报),以库形式嵌入应用 |
范围外但相关的同类替代见 2.7 节:Meilisearch、Typesense、Manticore Search、OpenSearch。
2.2 总对比表
| 维度 | SQLite FTS5 | PostgreSQL tsvector | Elasticsearch | Tantivy |
|---|---|---|---|---|
| 当前版本 | SQLite 3.54.1(2026-10-16)(changes) | PostgreSQL 18(2025-09-25 发布)(postgresql.org) | 9.x(9.0 GA 2025-04-14;9.1 2025-07;9.2 2025-10)(Elastic 博客) | crates.io 正式发布 0.25.0(2025-08-20);GitHub Releases 已标 0.26.x Latest |
| 形态 | 嵌入式虚拟表,随 SQLite 走 | 数据库内建类型/函数/索引 | 独立 JVM 服务 + Kibana | Rust 库(crates.io),无服务进程 |
| 相关性排序 | 内建 bm25()(k1=1.2、b=0.75,取负值使 ORDER BY rank 即最优在前)(fts5.html) | ts_rank / ts_rank_cd,无 BM25,官方文档明言这两个函数"仅是示例"(textsearch-controls) | BM25(Lucene 10 内核) | BM25(与 Lucene 同款实现)(README) |
| 分词器 | unicode61(默认)/ ascii / porter / trigram,可用 C API 自定义 | 文本搜索配置(词典+解析器),英语等自带词干化与停用词;中文需第三方扩展 | 丰富:内建多语言 analyzer、ICU、自定义插件 | 独立 tokenizer API(单独 crate 保持稳定),词干化覆盖 18 种语言(拉丁字母语言 13 种,另有希腊语/俄语/阿拉伯语等,见 stemmer.rs),中日韩靠第三方分词器 |
| 中文支持 | 原生不行:unicode61 会把连续汉字当一个长 token;3.34.0(2020-12)起 trigram 分词器可做子串/中文检索(SQLite Forum、StackOverflow) | 需 zhparser(SCWS)或 pg_jieba(zhparser、pg_jieba) | 内建或装 ICU/拼音/smartcn 等插件即可 | 需第三方中文分词器(jieba-rs 系) |
| 高亮/摘要 | highlight() / snippet() 内建 | ts_headline()(在原文上生成,较慢;官方提醒输出不保证 HTML 安全) | 最完整(多种 highlighter) | 有(fragment 生成) |
| 索引调优 | prefix 索引;detail=full/column/none(官方示例索引体积 743→340→134 MiB);外部内容表/无内容表 | GIN(查询快,默认推荐) vs GiST(更新快、更小) | 倒排 + doc_values + HNSW 向量索引等全套 | 倒排 + fast fields(doc values 等价物)+ HNSW |
| 向量检索 | 无 | 靠 pgvector 扩展生态 | 最强:9.0 BBQ 量化 GA,9.2 DiskBBQ | 0.21(2023-09)起 HNSW,0.24(2025)加 SIMD 加速 |
| 资源占用 | 极低(进程内,单文件) | 中(常驻 PG 实例,通常几百 MB 起) | 高:heap 不得超过机器内存 50%,且不超过压缩 oops 的 26–30GB 上限(Elastic 官方文档) | 低(进程内库,冷启动 <10ms,适合 CLI) |
| 许可 | 公有领域(copyright) | PostgreSQL License(BSD 类)(licence) | 三许可:AGPLv3(2024-08 起)+ SSPL + Elastic License 2.0(Elastic 博客) | MIT |
| 成熟度 | FTS5 约 11 年(2015-10 进 amalgamation),SQLite 社区维护 | 文本检索内建约 18 年(2008-02 起,已核 8.3 发布说明) | 十余年,事实标准;2021 年许可事件催生 OpenSearch 分叉 | 约 10 年(0.1.0 于 2016-08-11),仍为 0.x 版本号 |
| 个人知识库适配 | ★★★★★(最"最小") | ★★★☆(若数据本就在 PG 则 ★★★★) | ★★(能力过剩、运维负担) | ★★★★(质量与嵌入性兼得,需写点代码) |
2.3 相关性排序:三个有 BM25 的与一个没有的
相关性排序是四者差异最"结构性"的一列:
- ES 与 Tantivy:都是标准 BM25,同一起跑线(ES 自 Lucene 6 / ES 5.0 起以 BM25 为默认评分——此条为常识级史实,本 session 未单独核验原始发布说明)。
- FTS5:内建 BM25 但参数硬编码(k1=1.2、b=0.75,仅可按列加权)、无词干化(porter 只对拉丁语有效)、无停用词处理——对笔记检索够用,对"搜索产品"偏粗糙。
- PG tsvector:排序函数只有
ts_rank(词频)与ts_rank_cd(覆盖密度),两者都不使用任何全语料统计(idf),官方文档明说它们"只是示例,相关性概念因应用而异,你可以写自己的排序函数"。长查询区分度差。要 BM25 必须引入 ParadeDB 的pg_search——它把 Tantivy 作为索引访问方法嵌进 PG,提供@@@操作符与 BM25 评分。这是 PG 原生方案与"搜索引擎"之间最大的能力鸿沟。
2.4 中文分词:四者共同的短板
四者的默认分词器都面向拉丁语系(SQLite unicode61、PG 默认配置、Lucene 标准分词器、tantivy tokenizer 均不做中文词级切分),中文场景都要外挂:
- SQLite FTS5:内建不解决,三条出路——(1) trigram 分词器(3.34.0 内建、零依赖,把任意连续文本切成 3 字滑窗,天然支持中文子串匹配并优化
LIKE/GLOB,但少于 3 个字的查询永远不命中、索引更大);(2) 通过 FTS5 的 C API 自定义分词器(如封装 jieba,即 wangfenjin/simple,jieba 词组命中 + 拼音检索);(3) 入库前先用外部分词器切好词再写入。 - PostgreSQL:装扩展解决(zhparser 基于 SCWS,已被华为云 RDS、阿里云 PolarDB 集成(华为云 RDS 文档、阿里云 PolarDB 文档);或 pg_jieba),但扩展的安装/升级随 PG 版本绑定,个人机器上偶有摩擦;模糊匹配的 pg_trgm 对中文基本无用(pg_bigm 是 2-gram 替代)。
- Elasticsearch:插件化支持最成熟(IK、smartcn、ICU、拼音等),个人用没问题,但"为了中文搜索装一个 ES"依旧不划算。
- Tantivy:与 FTS5 类似靠第三方(jieba-rs 等),但 Rust/Python 里装第三方分词器比在 C 里舒服得多。
结论:中文检索没有谁真“开箱即用”——四者都要外挂,所以给两条口径分开的排序。外挂齐备后的集成成熟度(判据:方案本身的打磨程度与接入顺滑度——ES 插件体系最舒服、FTS5 的 C API 门槛最高,见 3.5):ES(IK)> PG(zhparser/pg_jieba)≈ Tantivy(jieba-rs)> FTS5(simple)。安装摩擦度(判据:从零到中文可检索要动多大的手术——是否本体自带、装在哪一侧、升级是否与宿主版本绑定):FTS5(trigram) > Tantivy > PG > ES——trigram 是 SQLite 本体内建、零安装;Tantivy 经 Cargo 引一个第三方 crate、随构建走;PG 要在数据库服务器侧装扩展包、随 PG 大版本绑定;ES 要往 JVM 服务里装插件并重启。
2.5 资源与运维成本:"最小"的度量
- 进程数:FTS5=0,Tantivy=0(随你的应用),PG=1,ES=2+(ES+Kibana)。
- 内存基线:FTS5/Tantivy 与数据量成正比且可 mmap;PG 通常数百 MB 起;ES 官方 heap 规则把上限钉在"机器内存的 50%、且 ≤26–30GB(压缩 oops)",min 必须等于 max——一台 8GB 笔记本理论上限也就给 ES 4GB heap,还没算 Kibana 与堆外内存。
- 备份/迁移:FTS5=拷文件;Tantivy=拷索引目录;PG=pg_dump;ES=snapshot/reindex(最重)。
- 版本升级:FTS5 最省心(SQLite 向后兼容传统极强);PG 主版本升级需 dump/restore 或逻辑复制;ES 大版本有 rolling upgrade 但流程繁;Tantivy 0.x 需关注索引兼容线(官方 CHANGELOG 明言:0.22 可读 0.21 建的索引,0.24 可读 0.21/0.22 建的索引且 0.23 被跳过,CHANGELOG)。
2.6 Tantivy 的能力清单与代价
Tantivy 值得单独一节,因为它是四者中"最不像数据库、也最不像服务"的一个。能力上:BM25(与 Lucene 相同实现)、自然查询语法((a AND b) OR "phrase")、短语/范围/分面查询、JSON 字段、聚合收集器(直方图/区间桶/统计量)、fast fields、LZ4/Zstd 压缩、Mmap 目录、SIMD 整数压缩,0.21 起 HNSW 向量检索,0.24 起 SIMD 向量过滤加速。性能上官方基准平均约为 Lucene 的 2 倍、英文 Wikipedia 索引耗时不到 3 分钟、冷启动 <10ms(均为厂商自报,本次未复测)。代价是"没有现成 UI/协议"——REST API、前端、增量同步都要自己写或借社区方案(lnx 提供 REST API,seshat 是 Matrix 消息检索器,Bichon 是邮件归档);文档不可变(改文档=删除+重索引);不支持分布式(官方让你去看同团队的 Quickwit)。生态上,tantivy-py 提供 Python 绑定(个人知识库脚本化的捷径),被 Etsy、ParadeDB、Nuclia、Element 等采用(tantivy README 「Companies Using Tantivy」 一节:README)。
2.7 同类替代方案速览(对比表外的候选)
| 方案 | 形态/语言 | 许可 | 与本题四者的相对位置 |
|---|---|---|---|
| Meilisearch | Rust 单二进制搜索服务 | MIT(核心) | 开箱即用、容错拼写、分面;社区版单节点,大索引内存占用偏高是社区常见抱怨 |
| Typesense | C++ 单二进制搜索服务 | GPL-3.0 | 定位与 Meili 相近,社区口碑偏"生产考验更久" |
| Manticore Search | C++,单二进制/分布式 | GPLv2(部分组件许可不同) | 从 Sphinx 系演化,多份基准中比 Meilisearch 更快(多为 Manticore 官方发布、未见独立复现,见 其仓库基准说明与文末核验边界第 8 条) |
| OpenSearch | ES 7.10.2 的 AWS 分叉 | Apache-2.0 | ES 的许可规避版本,能力对齐但演进独立 |
说明:本表来自公开对比页(manticoresearch.com、meilisearch.com 等),许可条目建议以各项目 GitHub 仓库(meilisearch、typesense、manticoresearch、opensearch)的 LICENSE 文件为最终依据,本次未逐一打开核验。
三、交叉洞察:两轴合在一起说明了什么
3.1 纵轴的每一个节点,都写进了横轴的每一列
两轴对读,最直观的发现是:横轴对比表里的每一格都不是"天生如此",而是纵轴某个历史节点的沉积。ES 列的"三许可并存"来自 2021 年的 SSPL 事件;FTS5 列的"BM25 内建"来自 2015 年对 FTS3/4 的完全重写;PG 列的"无 BM25"来自 2008 年核心化时定下的 ts_rank 设计;Tantivy 列的"0.x 版本号"来自 2016 年至今"稳定可用但保留演进自由"的自我定位。不懂纵轴就看不懂横轴:比如 PG 为什么没有 BM25?不是技术做不到,而是 2003 年 tsearch2 的设计者把相关性视为"应用相关"的问题,十八年来没有回到核心去改;再比如 ES 为什么 heap 最小也要以 GB 计?因为它是为 2010 年后的日志洪流设计的分布式系统,个人场景只是它顺便能覆盖的最小档位。
3.2 螺旋上升:引擎内核正在回到数据库里
纵轴的张力——引擎化 vs 内建化——并非直线分野,而是螺旋:2008 年 PG 把检索收进内核(内建化胜出),2010 年 ES 又把检索拆成独立服务(引擎化胜出),而 2023 年之后出现了第三幕——数据库开始把引擎内核请回来。ParadeDB 的 pg_search 把 Tantivy 整个作为索引访问方法嵌进 Postgres,补上原生 ts_rank 与搜索引擎之间的 BM25 鸿沟;这正是纵轴结尾"Datadog 收购 Quickwit、Tantivy 系被工业界承认"在横轴上的直接投影:PG 想要搜索引擎级的排序,最终是"请来了 Tantivy"。对用户的含义:内建与引擎的边界正在模糊,"用哪个产品"逐渐变成"把哪个内核放在哪里"。
3.3 许可证成了产品战略,且已经反向影响选型
2021 年 SSPL 事件 → OpenSearch 分叉 → 2024 年 AGPL 回归,这条纵轴线索在横轴上的意义是:许可列从"可以忽略的脚注"变成了选型变量。对个人使用,AGPL/SSPL/ELv2 三许可并存没有实质影响(自托管、不分发服务即可放心用);对商业托管与二次分发,它决定了你能不能省心。更深的启示是:当核心基础设施的许可可以一夜改变时,"零依赖、许可最简"的方案(SQLite 公有领域、Tantivy MIT)获得了额外溢价——这正是个人知识库场景与许可博弈的交汇点。
3.4 BM25 已经是"空气",PG 是唯一的例外
纵轴显示 BM25 已是全面标配:ES 5.0/Lucene 6 默认(约 2016)、FTS5 内建(2015)、Tantivy 首版即有(2016)。横轴显示今天四者中三个有内建 BM25,唯独 PG 原生缺位且官方明言 ts_rank"仅是示例"。两条轴合起来说明:相关性排序已从"搜索引擎的独门绝技"降格为"基础设施工具箱的标准件",数据库完全可以内建(FTS5 就是证明),PG 的缺口是历史包袱而非能力边界——所以社区用 pg_search 引入 Tantivy 来补,而不是等 PG 内核去改。
3.5 中文分词三十年未解,因为它是语言问题不是索引问题
纵轴上,这条线自 Lucene 1999 年算起近三十年,但四条线索各自入场的年头并不相同——Tantivy 约 10 年、FTS5 约 11 年、PG 内建约 18 年(见 2.2 成熟度行);横轴上它们却卡在同一块短板上:默认分词器全部以拉丁语系的空格/词边界为中心,中文都要外挂。这不是巧合——分词是语言学问题,倒排索引只是消费分词结果的一方,谁的索引内核都替代不了词典。三十年里解法也趋同:SQLite 走 C API 外挂(simple)、PG 走扩展(zhparser/pg_jieba)、ES 走插件(IK)、Tantivy 走独立 crate(jieba-rs)。架构越开放,中文解法越成熟:ES 的插件体系最舒服,FTS5 的 C API 门槛最高,这反过来又影响"集成成熟度"与"安装摩擦度"两条排序(见 2.4)。
3.6 对用户的判断与建议
把纵轴的历史包袱与横轴的能力边界折叠回"我有一堆笔记/文档,想要一个好用的全文搜索"这个具体问题,按约束从强到弱给三条路径:
- 默认答案:SQLite FTS5。 单文件、零进程、零网络、随应用打包(备份就是拷文件),BM25 内建、
ORDER BY rank即最优在前。建一张外部内容虚表,并配齐三个触发器把数据表的增/删/改同步进索引(三个触发器缺一不可,否则索引不随数据更新、查询永远命中旧数据;写法照搬 fts5.html §4.4.3 External Content Tables 官方示例):
-- 前提:notes 表含 INTEGER PRIMARY KEY 列 id
CREATE VIRTUAL TABLE notes_fts USING fts5(title, body, content='notes', content_rowid='id');
-- 触发器一:INSERT 之后写入索引
CREATE TRIGGER notes_ai AFTER INSERT ON notes BEGIN
INSERT INTO notes_fts(rowid, title, body) VALUES (new.id, new.title, new.body);
END;
-- 触发器二:DELETE 之后以 'delete' 命令传入旧值、从索引移除
CREATE TRIGGER notes_ad AFTER DELETE ON notes BEGIN
INSERT INTO notes_fts(notes_fts, rowid, title, body) VALUES('delete', old.id, old.title, old.body);
END;
-- 触发器三:UPDATE 之后先删旧值、再插新值
CREATE TRIGGER notes_au AFTER UPDATE ON notes BEGIN
INSERT INTO notes_fts(notes_fts, rowid, title, body) VALUES('delete', old.id, old.title, old.body);
INSERT INTO notes_fts(rowid, title, body) VALUES (new.id, new.title, new.body);
END;
SELECT ... FROM notes_fts WHERE notes_fts MATCH ? ORDER BY rank;中文笔记二选一:零依赖用 trigram 分词器(接受 <3 字查询失效);要词级语义与拼音用 simple(jieba)。旁证是 Simon Willison 的 Dogsheep:用 sqlite-utils + Datasette 把自己的 GitHub/Twitter/健康数据汇成一个 SQLite 库做全库分面全文检索(dogsheep-beta 0.10.2)——"个人数据仓库 + SQLite FTS"这条路已被完整走过一遍。个人知识库典型的万级到十万级文档量,它绰绰有余。
- 若你的知识库已在 PostgreSQL 里:直接 tsvector 生成列 + GIN 索引,中文装 zhparser/pg_jieba,查询用
websearch_to_tsquery免去手写语法;对排序不满意再上 pg_search(即内嵌 Tantivy 的 BM25)。不要为了搜索新引入一个 PG——那是"为了锤子买钉子"。
- 若你在写一个面向自己的检索工具、在意相关性与可编程性:tantivy-py(Python 绑定)是甜蜜点——库形态、MIT、HNSW 向量检索也有了,交付物可以是单个可执行文件,代价是自己写薄薄一层封装。
明确不推荐:为单人知识库部署 Elasticsearch。它的收益(分布式、Kibana、重度聚合、多用户)在单机单人场景用不上九成,而成本(heap 规则、进程族、运维)全部用得上。若确实想要"现成 REST 服务 + 容错拼写 + 分面"的体验,去看 Meilisearch / Typesense / Manticore 这类轻量替代(见 2.7),它们比 ES 更贴合个人规模。
进化路径(沿纵轴方向推演):SQLite FTS5 →(已有 Postgres 时)tsvector + GIN(zhparser/pg_jieba)→(需要更高检索质量或做工具分发时)Tantivy / pg_search →(几乎不需要)Elasticsearch。
一句话:最小方案 = SQLite FTS5 + trigram(或 simple 预分词);想要更高质量的排序与向量化再考虑 Tantivy;数据已在 PG 则顺势用 tsvector;Elasticsearch 属于"团队级答案",不属于"个人最小方案"。历史规律与此一致:越靠近"数据住在哪里",检索就应该长在哪里——这正是 2008 年 PG 8.3 核心化与 2015 年 FTS5 诞生以来,"内建化"路线一以贯之的方向。
四、参考来源
SQLite
- SQLite FTS5 官方文档:https://www.sqlite.org/fts5.html
- SQLite 3.9.0 发布说明(FTS5 引入,2015-10-14):https://sqlite.org/releaselog/3_9_0.html
- SQLite FTS3/FTS4 文档(FTS3 于 3.5.0、FTS4 于 3.7.4、Scott Hess 贡献):https://www.sqlite.org/fts3.html
- SQLite 版本历史(当前 3.54.1,2026-10-16):https://www.sqlite.org/changes.html
- SQLite 版权(public domain):https://www.sqlite.org/copyright.html
- FTS5 trigram 起源(SQLite Forum, 2020):https://sqlite.org/forum/forumpost/ca90da691a?t=h
- FTS5 unicode61 对 CJK 的局限(StackOverflow):https://stackoverflow.com/questions/52422437/
- wangfenjin/simple(FTS5 中文/拼音分词器):https://github.com/wangfenjin/simple ;https://www.wangfenjin.com/posts/simple-jieba-tokenizer
PostgreSQL
- PostgreSQL 8.2 Release Notes(GIN,2006-12-05):https://www.postgresql.org/docs/8.2/release-8-2.html
- PostgreSQL 8.3 Release Notes(全文检索进核心,2008-02-04):https://www.postgresql.org/docs/8.3/release-8-3.html
- Bartunov & Sigaev, Full-Text Search in PostgreSQL(PGCon 2007):https://www.pgcon.org/2007/schedule/attachments/12-fts.pdf
- PostgreSQL 文档 Text Search Controls(ts_rank/ts_rank_cd"仅是示例"):https://www.postgresql.org/docs/current/textsearch-controls.html
- PostgreSQL 文档 Text Search Types(websearch_to_tsquery 等):https://www.postgresql.org/docs/current/textsearch.html
- PostgreSQL Text Search 介绍(短语算子):https://www.postgresql.org/docs/current/textsearch-intro.html
- PostgreSQL 主页(当前版本 18,2025-09-25):https://www.postgresql.org/
- Crunchy Data: Postgres Full-Text Search:https://www.crunchydata.com/blog/postgres-full-text-search-a-search-engine-in-a-database
- zhparser:https://github.com/amutu/zhparser ;pg_jieba:https://github.com/jaiminpan/pg_jieba
- ParadeDB pg_search(PG 内嵌 Tantivy 提供 BM25):https://github.com/paradedb/paradedb ;https://www.paradedb.com/blog/introducing-search
Elasticsearch / Lucene
- Wikipedia: Elasticsearch:https://en.wikipedia.org/wiki/Elasticsearch
- Wikipedia: Apache Lucene:https://en.wikipedia.org/wiki/Apache_Lucene
- Elastic, Celebrating 20 years of Apache Lucene:https://www.elastic.co/celebrating-lucene
- Elastic, Elastic Stack 5.0.0 Released(2016-10-26):https://www.elastic.co/blog/elastic-stack-5-0-0-released
- Elasticsearch 9.0 发布博客(Lucene 10 / BBQ):https://www.elastic.co/blog/whats-new-elastic-search-9-0-0
- Elastic, "Elasticsearch is Open Source, Again"(AGPL,2024-08):https://www.elastic.co/blog/elasticsearch-is-open-source-again
- ES heap 尺寸官方指导:https://www.elastic.co/guide/en/elasticsearch/reference/current/advanced-configuration.html
Tantivy / Rust 生态
- tantivy 仓库与 README:https://github.com/quickwit-oss/tantivy
- tantivy Releases:https://github.com/quickwit-oss/tantivy/releases
- crates.io API(tantivy 全版本史,0.1.0 于 2016-08-11、0.25.0 于 2025-08-20):https://crates.io/api/v1/crates/tantivy
- Paul Masurel, Of tantivy, a search engine in Rust:https://fulmicoton.com/posts/behold-tantivy
- Quickwit joins Datadog(2025-01-09):https://quickwit.io/blog/quickwit-joins-datadog ;https://www.datadoghq.com/blog/datadog-acquires-quickwit
- Meilisearch:https://github.com/meilisearch/meilisearch ;https://www.meilisearch.com/open-source
- Typesense / Manticore / OpenSearch 仓库:https://github.com/typesense/typesense ;https://github.com/manticoresoftware/manticoresearch ;https://github.com/opensearch-project/OpenSearch
实践案例
- Simon Willison, dogsheep-beta 0.10.2(个人数据仓库 + SQLite FTS):https://simonwillison.net/2021/Jun/13/dogsheep-beta-0102/
- Simon Willison 的 sqlite 实验合集:https://simonwillison.net/tags/sqlite/
修订版按审读意见新增来源
- 华为云 RDS for PostgreSQL:使用 zhparser 插件:https://support.huaweicloud.com/usermanual-rds-pg/rds_09_0065.html
- 阿里云 PolarDB PostgreSQL 版全文检索(含 zhparser):https://help.aliyun.com/zh/polardb/polardb-for-postgresql/full-text-search-introduction
- pg_bigm(2-gram 模糊匹配扩展):https://github.com/pgbigm/pg_bigm
- tantivy tokenizer stemmer.rs(Language 枚举 18 种语言):https://github.com/quickwit-oss/tantivy/blob/main/src/tokenizer/stemmer.rs
- tantivy CHANGELOG(0.22 可读 0.21 索引;0.24 可读 0.21/0.22、0.23 跳过):https://github.com/quickwit-oss/tantivy/blob/main/CHANGELOG.md
- Elasticsearch 8.0.0 Release Notes(Security auto-configuration、kNN search、PyTorch 推理):https://www.elastic.co/guide/en/elasticsearch/reference/8.0/release-notes-8.0.0.html
- meilisearch 仓库元数据(创建于 2018-04-23,GitHub API):https://api.github.com/repos/meilisearch/meilisearch
核验边界:未验证事项与两章差异的处理
- 已核验(两章在本 session 通过网络工具读取原始来源):SQLite FTS5 的函数/分词器/版本事实;PG 排序函数与 GIN/GiST 建议、8.2/8.3 发布说明原文;ES 9.0/9.1/9.2 特性、heap 规则、许可事件时间线;Tantivy 的定位/特性/许可/生态与 crates.io 版本史。
- 标注为常识级、本 session 未单独核验:BM25 自 ES 5.0/Lucene 6 起为默认评分。
- 厂商自报、未复现:"Tantivy 性能约为 Lucene 2 倍"来自官方基准;"ES BBQ 最高 5 倍查询加速"来自 Elastic 官方宣称。
- 二手来源、未直接核对官方页:Tantivy 首篇博客的发布日期在不同来源中存在 2016-08 与 2017-01 两种说法,本文未下断言。
- 两章差异的统一口径:
- Tantivy 最新版本:纵轴引 crates.io API 为 0.25.0(2025-08-20),横轴引 GitHub Releases 页面为 0.26.x——两者数据源不同(正式发布 vs GitHub tag),本文并列呈现。
- Tantivy 0.24 的发布时间:crates.io 元数据为 2025-04,横轴写 2025-06,本文统一写"2025 年(0.24 加 SIMD 向量加速)",避免虚假精度。
- Tantivy 历史年数:横轴正文写"约 8 年",与 crates.io 0.1.0(2016-08-11)不符,按其修正为约 10 年。
- PG 文本检索内建年数:横轴表格写"约 20 年"、正文写"约 18 年",两者矛盾;按 8.3 发布说明(2008-02-04)计算,2026-10-31 截面应为约 18 年,取 18。
- PG 2008 年内建全文检索:横轴原标"模型既有知识",纵轴已用官方 Release Notes 核验,本文按已核验处理。
- SQLite 中文方案:纵轴推荐 simple(jieba)分词器,横轴推荐 trigram/预分词——两者不矛盾,本文合并为零依赖(trigram)与高质量(simple/预分词)两条路径。
- contentless-delete:纵轴时间线放在 2024–2025 行,横轴与纵轴正文均指明自 3.43.0(2023-08)起支持,本文取 3.43.0(2023-08)。
- 版本时效:各产品版本号为 2026-10-31 截面,ES 与 Tantivy 迭代较快,引用时建议再查官方页。
- 修订版补充核验(2026-10-31,按审读意见逐条复核):Meilisearch 立项年份——GitHub API 显示仓库创建于 2018-04-23,正文 1.4 的"同年"已改为 2018 年,与 1.6 时间线一致;FTS5 外部内容表触发器——照 fts5.html §4.4.3 官方示例补全三条触发器 SQL;Tantivy 采用者(Etsy/ParadeDB/Nuclia/Element/Humanfirst)与 lnx/seshat/Bichon——见 README「Companies Using Tantivy」及其 FAQ;Tantivy 词干化语言数——源码 stemmer.rs 的 Language 枚举为 18 种(拉丁字母语言 13 种),原稿"拉丁语系 17 种"据此更正;索引兼容线——CHANGELOG:0.22 可读 0.21、0.24 可读 0.21/0.22(0.23 跳过);tantivy 0.19.0 发布日——crates.io API 显示 2022-12-12;zhparser 云集成——华为云 RDS 文档与阿里云 PolarDB 文档均有专页;ES 8.0 特性——官方 8.0.0 Release Notes(8.0 主题博客原文链接已失效,故改引官方 Release Notes)。
- 仍未核验:Manticore Search 相对 Meilisearch 的速度优势——可比基准多为 Manticore 官方发布(其仓库),本文未做独立复现,2.7 表中已相应弱化措辞;"ES 自 Lucene 6 / ES 5.0 起默认 BM25"仍按第 2 条以常识级标注。