解释“$text”全文索引与“_fti”端点在实现层面的差异。
解读
国内面试官问这道题,并不是想听你背文档,而是想看你是否真的在生产环境“踩过坑”。
“$text”是 CouchDB 3.x 之后官方内置的 Mango 全文索引,走的是 CouchDB 自己维护的 ICU 分词 + 倒排索引;
“_fti”则是 老版本 1.x/2.x 时代依赖外部 Cloudant-Lucene 插件 的遗留接口,本质上是把文档同步到 Lucene 索引目录,再通过 独立 Java 进程 提供搜索。
一句话:前者是“官方亲儿子”,后者是“历史干儿子”。能否把两者在 索引生命周期、查询语法、事务一致性、运维成本 四个维度拆开讲,是得分关键。
知识点
-
索引存储位置
- $text:与主库同机,index 文件落在 .couch 目录下的 _text_idx 子目录,由 couch_index_server 统一管理,随 shard 一起分片、一起复制。
- _fti:必须额外部署 cloudant-lucene 服务,索引文件放在 lucene-data/ 目录,不参与 CouchDB 内部分片复制,需要你自己做 NFS 或 DRBD 保证高可用。
-
触发机制
- $text:文档写入后,由 couch_index_updater 进程异步消费 seq_tree,更新倒排索引,同一事务内保证一致性视图。
- _fti:CouchDB 通过 httpdb 钩子 把文档 POST 到 lucene 监听端口(默认 5985),跨进程、跨网络,失败就丢消息,需要手工补偿重建。
-
查询语法
- text:在 Mango 里用 **{"text": "北京 天气"}**,支持 ICU 中文分词、布尔逻辑、短语匹配,返回结果天然带 {"$rank": score} 字段。
- _fti:走 /_fti/local/{ddoc} 端点,参数是 ?q=北京 AND 天气,语法是 Lucene QueryParser,中文需要提前 IK 或 Jieba 插件,否则只能单字切。
-
性能与调优
- $text:索引段与 CouchDB 的 IO 队列合并,不需要额外 JVM 内存;但大库首次构建会 抢磁盘 IOPS,建议 低峰期触发。
- _fti:独立 JVM,堆内存要单独调;热点分片容易造成 lucene 节点 OOM;国内云主机 4C8G 场景下,单次 merge 会飙到 200% CPU,必须 cgroup 限流。
-
运维与监控
- $text:直接 /_node/_local/_stats/couchdb/httpd/text_search 看延迟;索引损坏时只需 删除 _text_idx 目录重建,自动从 0 开始顺序扫描。
- _fti:需要 JMX + Prometheus jmx_exporter 才能看 lucene 指标;索引 corruption 后只能 停 lucene、删目录、全量重建,TB 级库重建一次 6~8 小时, SLA 血崩。
-
国产化合规
- $text:ICU 分词库已 内置在 CouchDB 主包,不依赖外部服务,信创环境可直接通过麒麟 V10 适配。
- _fti:cloudant-lucene 依赖 OpenJDK + Apache Lucene 8.x,信创名录里无 Lucene 8,需要降级到 7.x 并重新编译,甲方验收会被挑刺。
答案
“text**,_fti 只出现在历史系统改造场景,且必须 加消息队列补偿 + 双读开关 保证平滑下线。
拓展思考
- 如果甲方要求 “lucene 语法兼容性”,又不想维护 _fti,你可以 **在 text 语法,用 Node-RED 或 Go 网关实现,两周可上线。
- 当单库 >2 亿条、索引目录 >500 GB 时,$text 的 首次构建时间可能超过 6 小时,此时可 临时关闭 search_enabled、先让业务跑冷数据、再灰度开启;_fti 在这种体量下基本不可维护。
- 国内金融客户常问 “全文索引是否影响 point-in-time 备份”——$text 因为 与主 shard 共享一致性快照,备份恢复后索引自动复用;而 _fti 独立目录,必须额外做 lucene 目录的 fsync + tar,否则恢复后搜索直接为空,审计过不了。