PostgreSQL 全文检索深度指南:内置 FTS、zhparser 与 pg_search 全解

张开发
2026/6/22 3:38:35 15 分钟阅读
PostgreSQL 全文检索深度指南:内置 FTS、zhparser 与 pg_search 全解
在 AI 与海量文本检索的时代PostgreSQL 凭借原生全文检索能力与丰富扩展成为轻量、高效、可直接落地的文本搜索与数据存储一体化方案。本篇文章为大家详细介绍PostgreSQL 全文检索。一. 什么是全文检索全文检索Full-Text SearchFTS是一种从海量文本中快速找出相关内容的搜索技术。与常见的LIKE %keyword%不同全文检索的核心价值在于三点倒排索引加速相关度排序词根匹配。对于一个文章系统、搜索框、日志分析平台来说全文检索几乎是标配能力。PostgreSQL 从很早就内置了完整的全文检索功能近年来还涌现出 zhparser中文分词和 pg_searchBM25 相关度排序两个强力补充使得 PG 的检索能力足以与 Elasticsearch 一较高下。二. PostgreSQL 内置全文检索2.1 核心概念tsvector 与 tsqueryPG 全文检索围绕两个数据类型工作tsvector — 文档的存储格式文档文本经过分词、去停用词、词形归一化后生成一个词位序列lexeme每个词位带上它在原文中的位置信息。-- 原始字符串直接转 tsvector空格分词SELECT a fat cat sat on a mat and ate a fat rat::tsvector;-- 输出-- a and ate cat fat mat on rat satto_tsvector() — 带语言处理的转换函数SELECT to_tsvector(english, The Fat Rats);-- 输出fat:2 rat:3to_tsvector() 做了三件事小写化The Fat Rats→the fat rats词形归一化Rats→rat复数归一去停用词如a、on、it等高频无意义词被丢弃-- 对比有 to_tsvector 和没有的差别SELECT a fat cat sat on a mat - it ate a fat rats::tsvector;-- 输出- a ate cat fat it mat on rats satSELECT to_tsvector(english, a fat cat sat on a mat - it ate a fat rats);-- 输出ate:9 cat:3 fat:2,11 mat:7 rat:12 sat:4注意标点符号-和停用词a、on、it在to_tsvector()中都被过滤掉了。tsquery — 查询条件格式tsquery 是经过规范化的查询条件支持布尔逻辑-- AND同时包含SELECT to_tsquery(english, cat rat);-- OR包含其一SELECT to_tsquery(english, cat | rat);-- NOT排除SELECT to_tsquery(english, cat ! rat);-- 短语查询词1紧跟词2SELECT to_tsquery(english, fat - rat);-- 前缀匹配SELECT to_tsquery(english, rat:*);2.2 匹配操作符 tsvector 和 tsquery 之间用连接表示文档是否匹配查询-- 两种写法等价to_tsvector(fat cats ate rats) to_tsquery(cat rat) -- → tfat cats ate rats to_tsquery(cat rat) -- → ttext 类型会在比较时隐式调用 to_tsvector() 转换。2.3 内置全文检索的局限PG 内置分词器默认按空格分割这对英文友好对中文却束手无策-- 中文整句被当成一个词无法部分匹配SELECT to_tsvector(english, 不可用或超时导致);-- 不可用或超时导致:1-- 精确匹配整个词组才有效SELECT * FROM wenzhang WHERE content_tsvector 不可用或超时::tsquery;-- → 0 rowsSELECT * FROM wenzhang WHERE content_tsvector 不可用或超时导致::tsquery;-- → 有结果要支持中文需要引入中文分词插件。三. 中文分词支持zhparser3.1 为什么需要 zhparser中文文本的词与词之间没有空格分隔直接用空格分词器会把一整句话当成一个词。zhparser 基于 SCWS 中文分词库实现能将中文句子正确切分为独立的词语。3.2 创建扩展和文本搜索配置以下操作假设 zhparser 扩展已安装编译安装步骤请参考 GitHub amutu/zhparser。-- 1. 创建 zhparser 扩展CREATE EXTENSION zhparser;-- 2. 验证扩展\dx zhparser-- List of installed extensions-- Name | Version | Schema | Description-- --------------------------------------------------------------------- zhparser | 2.3 | public | a parser for full-text search of Chinese-- 3. 查看当前默认文本搜索配置SHOW default_text_search_config;-- → pg_catalog.english-- 4. 创建中文文本搜索配置关键步骤CREATE TEXT SEARCH CONFIGURATION chinese (PARSER zhparser);-- 5. 核心步骤将各词性映射到 simple 词典-- 如果跳过这步to_tsvector(chinese, ...) 返回空ALTER TEXT SEARCH CONFIGURATION chinese ADD MAPPING FOR n, v, a, i, e, l WITH simple;mapping 中各词性的含义标记词性标记词性n名词i成语v动词e叹词a形容词l习用语simple词典不做任何词形处理原样存储和匹配非常适合不需要词形变化的汉语。如果不指定 mapping或者 mapping 到其他语言的词典如 english分词结果将是空。3.3 对比分词效果-- 默认 english 分词器中文被当成一个词 -- zhparser chinese 分词正确切分为多个词语3.4 注意事项1. tsquery 必须精确匹配分词结果-- 排查 是完整词可以搜到SELECT * FROM search_wenzhang WHERE content_zh_tsvector 排查::tsquery;-- 排 不是独立词搜不到任何结果SELECT * FROM search_wenzhang WHERE content_zh_tsvector 排::tsquery;-- → 0 rows2. 不配置 mapping 导致分词为空-- mapping 缺失时to_tsvector(chinese, ...) 返回空-- 表现更新后 content_zh_tsvector 字段为空四. 智能检索pg_searchParadeDB4.1 为什么要用 pg_search4.2 创建扩展以下操作假设 pg_search 扩展已安装安装步骤请参考 ParadeDB 官方文档。需要预先在postgresql.conf中添加shared_preload_libraries pg_search重启 PG 后执行CREATE EXTENSION pg_search;目前HULK的PostgreSQL实例默认已经安装了pg_search扩展方便大家使用。4.3 可用的中文分词器SELECT * FROM paradedb.tokenizers(); default keyword keyword_deprecated raw literal_normalized white_space regex_tokenizer chinese_compatible source_code ngram chinese_lindera japanese_lindera korean_lindera jieba lindera unicode_words_deprecated unicode_words其中与中文相关的分词器主要是以下四个4.4 创建 BM25 索引BM25 索引是 pg_search 的核心语法与普通 B-Tree 索引不同CREATE INDEX ch_idx ON public.search_wenzhangUSING bm25 (id, content)WITH ( key_field id, text_fields { content: {tokenizer: {type: chinese_compatible}} });参数说明参数含义key_field主键字段必须唯一text_fields要索引的文本字段及配置tokenizer.type使用的分词器类型4.5 查询语法-- 基础查询content 字段包含问题SELECT * FROM search_wenzhangWHERE id paradedb.parse(content:问题)LIMIT 10;更多查询示例-- 搜索英文 TiKVSELECT * FROM search_wenzhangWHERE id paradedb.parse(content:TiKV)LIMIT 10;-- 搜索定位SELECT * FROM search_wenzhangWHERE id paradedb.parse(content:定位)LIMIT 10;-- 前缀匹配Ti 可以匹配 TiKV、TiDB 等SELECT * FROM search_wenzhangWHERE id paradedb.parse(content:Ti*)LIMIT 10;-- 前缀匹配查不到的情况英文分词器按空格分词SELECT * FROM search_wenzhangWHERE id paradedb.parse(content:Ti)LIMIT 10;-- → 0 rows需要完整词 TiKV 或前缀 Ti*注意id ...是 pg_search 的查询语法BM25 索引不依赖额外的 tsvector 存储列。4.6 中文分词zhparser和智能搜索pg_search查询对比--查询关键词问题--pg_searchdbrte# SELECT id, content FROM search_wenzhang WHERE id paradedb.parse(content:问题) LIMIT 2; id | content ----------------------------------------------------------------------------------- 1 | 通过 HTTP API 来排查 goroutine 泄露的问题。 2 | 该问题通常由于 TiKV Region 不可用或超时导致需要看 TiKV 的监控指标定位问题。--zhparserdbrte# SELECT id, content FROM search_wenzhang WHERE content_zh_tsvector 问题::tsquery LIMIT 2 ; id | content ----------------------------------------------------------------------------------- 1 | 通过 HTTP API 来排查 goroutine 泄露的问题。 2 | 该问题通常由于 TiKV Region 不可用或超时导致需要看 TiKV 的监控指标定位问题。--查询关键字问--pg_serachdbrte# SELECT id, content FROM search_wenzhang WHERE id paradedb.parse(content:问) LIMIT 2; id | content ----------------------------------------------------------------------------------- 1 | 通过 HTTP API 来排查 goroutine 泄露的问题。 2 | 该问题通常由于 TiKV Region 不可用或超时导致需要看 TiKV 的监控指标定位问题。--zhparserdbrte# SELECT id, content FROM search_wenzhang WHERE content_zh_tsvector 问::tsquery LIMIT 2; id | content -------------(0 rows)结论可以看出pg_search在非常规词组的匹配上更准确。五. 选型建议与最佳实践5.1 三种方案横向对比5.2 选型决策我们可以根据以下策略进行分词器选择5.3 表设计建议触发器自动维护 tsvector每次 INSERT/UPDATE 都手动调用to_tsvector()容易遗漏。用触发器自动完成CREATE OR REPLACE FUNCTION update_wenzhang_tsvector()RETURNS TRIGGER AS $$BEGIN NEW.content_tsvector : to_tsvector( english, COALESCE(NEW.title, ) || || COALESCE(NEW.content, ) ); NEW.content_zh_tsvector : to_tsvector( chinese, COALESCE(NEW.title, ) || || COALESCE(NEW.content, ) ); RETURN NEW;END;$$ LANGUAGE plpgsql;CREATE TRIGGER wenzhang_tsvector_updateBEFORE INSERT OR UPDATE ON search_wenzhangFOR EACH ROW EXECUTE FUNCTION update_wenzhang_tsvector();有了这个触发器应用程序只需要写入content字段tsvector 列会自动保持最新。5.4 性能优化清单建索引GIN 索引内置/zhparser或 BM25 索引pg_search是性能的根基预先生成 tsvector不要在查询时实时调用to_tsvector()那是 CPU 密集操作分区表按时间对大表做 Range 分区如按月减少每次检索扫描的数据量定期维护VACUUM ANALYZE保持统计信息准确REINDEX保持索引效率避免 LIKE所有文本搜索场景一律使用全文检索删除所有LIKE %keyword%以下是基于200 万行数据进行的查询性能基准测试查询性能对比GIN 索引让查询从全表扫描变为索引查找性能提升约7 倍。5.5 常见问题速查现象最可能的原因解决方案to_tsvector(chinese, ...)返回空没有配置 mapping执行ALTER TEXT SEARCH CONFIGURATION chinese ADD MAPPING FOR ... WITH simple中文查询只能搜完整词部分匹配不到tsquery 精确匹配分词结果使用chinese_compatible或jieba分词器支持更细粒度切分pg_search 查询报错shared_preload_libraries未配置修改 postgresql.conf重启 PG查询变慢索引失效或数据量增长执行REINDEX检查分区策略分词结果不符合预期词典配置问题检查 SCWS 词典版本或换用 pg_search 的 jieba 分词器六. 总结PostgreSQL 的全文检索生态已经从能用进化到了好用PG 内置 FTS英文场景完全够用内置无需安装性能稳定zhparser解决中文布尔搜索问题代价是需要编译安装插件且只有有/没有的判断pg_search中文 BM25 排序的最佳方案安装简单功能强大是当前最推荐的生产级选择在实际项目中可以将多种方案组合使用用 zhparser 或 pg_search 做中文全文检索同时保留内置 FTS 处理英文场景各取所长。END更多技术干货请关注“360智汇云开发者”360智汇云是以汇聚数据价值助力智能未来为目标的企业应用开放服务平台融合360丰富的产品、技术力量为客户提供平台服务。目前智汇云提供数据库、中间件、存储、大数据、人工智能、计算、网络、视联物联与通信等多种产品服务以及一站式解决方案。官网https://zyun.360.cn复制在浏览器中打开更多好用又便宜的云产品欢迎试用体验~添加工作人员企业微信get更快审核通道产品免费试用包哦~

更多文章