SQL 全文搜索的 tsvector / tsquery vs LIKE ‘%xx%’ vs trigram 索引性能对比

tsvector+gin是语义搜索唯一合理选择,支持词干提取、停用词过滤和语言学处理;pg_trgm+gin/gist适用于错别字和模糊匹配;like '%xx%'仅限调试或补救,性能差且索引失效。

tsvector + GIN 索引:真正为语义搜索设计的方案

当你要查“数据库性能优化”,而用户输入的是 performance & databasetsvector 能自动忽略停用词、统一大小写、还原词干(如 optimizing → optimize),再通过 GIN 索引在倒排表里快速定位——这才是语义级匹配。

它不是字符串匹配,是语言学处理后的检索。所以如果你的场景涉及自然语言输入(比如后台内容管理系统的搜索框、文档库关键词查找),tsvector 是唯一合理选择。

  • 必须配合 to_tsvector('english', column) 显式指定配置,否则默认 simple 不做词干提取和停用词过滤
  • content_vector TSVECTOR GENERATED ALWAYS AS (...) STORED 是最稳写法,避免触发器漏更新或函数重复计算
  • 中文必须额外装插件(如 zhparser),原生不支持分词;别指望 to_tsvector('chinese', ...) 能跑起来
  • 索引体积约是原文本的 30–40%,但查询耗时从 1200ms 降到 10–15ms(100 万行实测)

LIKE '%xx%':只适合极轻量、低频、开发调试用

它不做任何预处理,就是逐字节扫字符串。哪怕字段上有 B-tree 索引,只要开头带 %(如 '%error%'),索引就完全失效——这是硬性限制,不是优化能绕开的。

它的存在意义,仅限于临时查日志字段、补救没建全文索引的老表、或者验证数据是否存在某段固定文本。

  • 'error%' 可走 B-tree 索引,但 '%error''%error%' 都不行
  • 字段长度越长、行数越多,性能断崖式下跌;10 万行以上基本不可用于线上查询
  • 无法表达“包含 A 且不含 B”“A 和 B 相距不超过 5 个词”这类逻辑

pg_trgm + GIN/GiST:模糊拼写/错别字/中英文混输的折中解

当你需要查 'postgessql'(少了个 r)也能命中 PostgreSQL,或者用户搜 '微信支付' 但数据库存的是 '微信 支付'(中间有空格),pg_trgm 就派上用场了。它把字符串切成三元组(trigram),靠相似度打分匹配。

但它不是语义搜索:不会理解“run”和“running”是一回事,也不懂“database”和“DB”是否等价。

  • 启用前必须 CREATE EXTENSION pg_trgm,否则 GIN 索引会报错“operator does not exist”
  • USING gin(column gin_trgm_ops)USING gist(column gist_trgm_ops) 效果不同:GIN 更快但写入略重;GiST 支持 % 查询外的 相似度排序
  • SELECT * FROM t WHERE col % 'postgessql' 这种写法依赖 pg_trgm 的相似度阈值(默认 0.3),太低易误召,太高漏结果
  • 对纯英文短词(如 'api')效果差:三元组太少,区分度低

选哪个?看你的 query pattern 和数据特征

没有银弹。如果用户搜的是完整单词、带逻辑关系(“Java 并发”但不要 “Spring”)、且你能控制分词语言,tsvector 是首选。如果用户常打错、粘连、缺字,或者字段里塞了大量非结构化短文本(如标签、用户名、商品标题),pg_trgm 更实在。而 LIKE '%...%' 应该被当成最后手段,上线前务必 grep 掉。
famulanwx-gz.jsjshdzb.com
baogeliwx-gz.jsjshdzb.com
rolexwx-gz.jsjshdzb.com
xiaobangwx-gz.jsjshdzb.com
aibiwx-gz.jsjshdzb.com
yadianwx-gz.jsjshdzb.com
lichamierwx-gz.jsjshdzb.com
langgewx-gz.jsjshdzb.com
luojiedubiwx-gz.jsjshdzb.com
yakedeluowx-gz.jsjshdzb.com
yubowx-gz.jsjshdzb.com
zhenlishiwx-gz.jsjshdzb.com
xiangnaierwx-gz.jsjshdzb.com
zhibowx-gz.jsjshdzb.com
diduowx-gz.jsjshdzb.com
最容易被忽略的一点:三者不能混用索引。你建了 GIN on tsvector,再对同一列加 pg_trgm 索引,不仅空间翻倍,查询计划还可能选错索引——PostgreSQL 不会自动判断“这次该用语义还是三元组”。得靠 SET enable_seqscan = off 测试,或用 EXPLAIN (ANALYZE, BUFFERS) 看实际走哪条路。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • """1.个性化消息: 将用户的姓名存到一个变量中,并向该用户显示一条消息。显示的消息应非常简单,如“Hello ...
    她即我命阅读 12,690评论 0 6
  • 1、expected an indented block 冒号后面是要写上一定的内容的(新手容易遗忘这一点); 缩...
    庵下桃花仙阅读 4,167评论 1 2
  • 一、工具箱(多种工具共用一个快捷键的可同时按【Shift】加此快捷键选取)矩形、椭圆选框工具 【M】移动工具 【V...
    墨雅丫阅读 4,524评论 0 0
  • 跟随樊老师和伙伴们一起学习心理知识提升自已,已经有三个月有余了,这一段时间因为天气的原因休课,顺便整理一下之前学习...
    学习思考行动阅读 3,958评论 0 2
  • 一脸愤怒的她躺在了床上,好几次甩开了他抱过来的双手,到最后还坚决的翻了个身,只留给他一个冷漠的背影。 多次尝试抱她...
    海边的蓝兔子阅读 2,851评论 1 4

友情链接更多精彩内容