PostgreSQL 全文检索是怎么做的?tsvector 和 GIN 索引有什么作用?
简化版
PostgreSQL 全文检索会把文本转换成 tsvector,查询词转换成 tsquery,再用匹配运算符检索。GIN 索引能加速词项倒排查询。它适合中小规模站内搜索和简单相关性排序,但复杂中文分词、海量搜索和高级召回通常要考虑 Elasticsearch 等搜索引擎。
详细版
核心概念:
tsvector:文本分词、归一化后的词项集合。tsquery:查询条件,可表达 AND、OR、NOT。- GIN 索引:适合加速词项包含关系。
ts_rank:用于相关性排序。
示例:
select *
from article
where search_vector @@ plainto_tsquery('english', 'postgres index');
PostgreSQL 全文检索适合和事务数据放在一起的轻量搜索,但中文分词和复杂搜索体验要额外评估。
完整版教学
一、全文检索不是简单 like
like '%keyword%' 是字符串匹配,通常难以利用普通 B-tree 索引,且不能理解词形变化、相关性和组合查询。全文检索会先把文本变成词项,再按词项检索。
例如英文里 running、runs、run 可以归一化到相近词根。查询不再只是字符包含,而是词项匹配。
原文: PostgreSQL indexes improve searching
tsvector: 'index':2 'improv':3 'postgresql':1 'search':4
记忆钩子:全文检索查的是“词项”,不是单纯查一段字符串。
二、tsvector 保存可检索词项
tsvector 是 PostgreSQL 全文检索的核心数据结构。它保存从文本中提取出来的词项和位置。
select to_tsvector('english', 'PostgreSQL indexes improve searching');
结果会包含归一化后的词项。实际表中可以用生成列或触发器维护 search_vector 字段,避免每次查询都临时转换。
如果每次查询都对正文调用 to_tsvector,大表会很慢。预先存好并建索引才是常见工程做法。
三、tsquery 表达查询条件
查询词会被转换成 tsquery。简单输入可以用 plainto_tsquery,它会把普通文本转成查询表达式。
select *
from article
where search_vector @@ plainto_tsquery('english', 'postgres index');
@@ 表示全文匹配。复杂查询还可以表达 AND、OR、NOT,例如 postgres & index。
用户搜索框通常不要直接拼接 tsquery 字符串,而应使用安全函数转换,避免语法错误和注入风险。
四、GIN 索引用来加速倒排查询
全文检索本质上需要从词项找到包含它的文档,这和倒排索引思路一致。PostgreSQL 常用 GIN 索引加速 tsvector 查询。
create index idx_article_search
on article using gin(search_vector);
没有 GIN 索引时,查询可能要扫描大量行并计算匹配;有索引后,可以快速定位包含词项的行。
GIN 索引写入和维护成本比普通 B-tree 高,所以适合读多搜索场景,写入极高的表要评估。
五、相关性排序可以用 ts_rank
搜索结果不只要匹配,还要排序。PostgreSQL 提供 ts_rank 等函数计算相关性。
select title,
ts_rank(search_vector, plainto_tsquery('english', 'postgres index')) as rank
from article
where search_vector @@ plainto_tsquery('english', 'postgres index')
order by rank desc
limit 20;
相关性排序会增加计算成本。大结果集排序时要结合过滤条件、limit 和索引策略,不能无脑全量算 rank。
六、中文搜索要特别评估分词
PostgreSQL 内置全文检索对英文等语言支持较成熟,但中文没有天然空格分词,效果取决于配置和扩展。简单中文站内搜索可以用 trigram 或扩展方案,复杂搜索通常交给专业搜索引擎。
| 需求 | 可选方案 |
|---|---|
| 英文文档搜索 | PostgreSQL FTS |
| 简单模糊匹配 | pg_trgm |
| 复杂中文分词 | 搜索扩展或 Elasticsearch |
| 海量搜索与召回 | 专业搜索引擎 |
面试时要说明 PostgreSQL 能做全文检索,但不是 Elasticsearch 的完全替代品。
七、常见误区与追问
- 误区:全文检索就是 like。 全文检索基于词项、词典、查询表达式和倒排索引。
- 误区:建了 GIN 索引就没有成本。 GIN 写入和维护更重,要评估读写比例。
- 误区:PostgreSQL 全文检索能完全替代 ES。 复杂中文分词、海量召回和搜索运营能力仍适合专业搜索引擎。
- 追问:tsvector 和 tsquery 区别? 前者是文档词项,后者是查询表达式。
- 追问:为什么用 GIN? GIN 适合一个值包含多个词项的倒排检索。
- 追问:如何做相关性排序? 使用
ts_rank,但要注意大结果集排序成本。
八、面试中可以这样落地
文章搜索可以增加 search_vector 字段,写入或更新文章时维护它,并建 GIN 索引。
alter table article add column search_vector tsvector;
create index idx_article_search on article using gin(search_vector);
如果中文搜索要求高,就说明 PostgreSQL 可做轻量站内搜索,复杂召回、分词、同义词、拼写纠错应考虑 Elasticsearch 或 OpenSearch。
九、加强记忆
PostgreSQL 全文检索记住四个词:tsvector 存文档词项,tsquery 表达查询,@@ 做匹配,GIN 做加速。它适合轻量搜索和事务数据贴近场景,但中文复杂搜索和海量召回要评估专业搜索引擎。