PG 全文搜索实战(9):选型与避坑 · PG FTS vs Elasticsearch
本系列最后一篇。什么时候用 PG 全文搜索够了什么时候该上 ES以及一堆真实踩过的坑。一、PG 全文搜索 vs Elasticsearch维度PostgreSQL FTSElasticsearch运维成本低已有 PG不加组件高额外集群、同步、监控数据一致性强事务内实时最终一致需同步有延迟中文分词需装扩展词库一般生态成熟IK 等相关性/打分基础够用强大可深度调优聚合分析弱强facet、聚合海量 高并发搜索单机瓶颈明显天生分布式高亮/纠错/联想基础丰富二、决策建议实战经验优先用 PG FTS当数据本来就在 PG量级在千万级以内或分区后单区可控。搜索通常带过滤条件时间、租户、分类不是全库任意搜。要求搜索结果和业务数据强一致、实时。团队不想多养一套 ES。该上 ES当数据量上亿且需要全局任意关键词高并发检索。需要复杂的相关性调优、聚合分析、搜索联想/纠错。中文分词要求高且 PG 装不了合适扩展如云 RDS 受限。已经有分表几十上百张跨表全局搜索用 PG 很吃力见第 8 章。经验法则别一上来就 ES。先评估 PG FTS 分区能不能扛扛不住或明显不合适再上 ES。很多“搜索需求”其实 PG 就够了省下一套中间件的运维。三、真实踩坑清单1. NULL 导致 tsvector 整个为空-- ❌ title 为 NULL 时整个 tsvector NULL搜不到to_tsvector(english,title|| ||body)-- ✅ 用 coalesce 兜底to_tsvector(english,coalesce(title,)|| ||coalesce(body,))2. 建索引配置和查询配置不一致建索引用english查询用simple或中文用了不同配置结果匹配不上。两边必须完全一致。3. 表达式索引没命中对to_tsvector(english, title || || body)建的索引查询表达式必须逐字一致连拼接顺序、空格都要一样才走索引。推荐用生成列避免这个坑。4. 用户输入直接喂 to_tsquery 报错to_tsquery要求合法语法用户输入a b之类会抛异常。接收前端输入一律用websearch_to_tsquery。5. ts_headline 拖垮查询对全表做高亮 灾难。只对分页后的当前页做见第 5 章。6. 小数据量 EXPLAIN 看不到用索引数据少时 PG 认为全表扫更快不用 GIN这是正常的优化器行为不是索引坏了。数据量上来自然会用。7. 大表直接 ADD COLUMN GENERATED 锁表亿级表一把梭会锁很久。先加空列、分批回填、CONCURRENTLY 建索引见第 7 章。8. 改了词库/配置老数据不生效分词配置或自定义词典变更后历史行的 tsvector 不会自动重算需要 rebuild重跑 UPDATE 或重建生成列。9. 云 RDS 装不了 zhparser采购前确认云数据库是否支持中文分词扩展否则中文搜索会很尴尬。四、一句话总结全系列PG 全文搜索 tsvector预计算落列 GIN 索引 tsquery 匹配 ts_rank 排名。中小规模、数据在 PG、搜索带过滤条件的场景它完全够用且省一套 ES 的运维。数据到海量、要全局高并发检索和复杂分析时再上 ES。