全文检索:从 LIKE 到倒排索引的实现
紫烛夜
6 分钟阅读
判断先说:LIKE '%关键词%' 做全文搜索,本质是全表逐行扫描加字符串匹配,数据量一大就崩。真正的全文检索靠倒排索引:把"词"映射到"出现在哪些文档",查询时直接查词、取交集,复杂度从 O(文档数) 降到接近 O(词数)。两者是两种完全不同的检索思路,后者为"搜"而生,前者只是数据库的顺手功能。
LIKE 为什么撑不住
SELECT * FROM articles WHERE content LIKE '%分布式事务%';
这个查询无法利用 B+ 树索引(前导通配符失效),数据库只能把每行 content 读出来做子串匹配。表有上百万行时,每次搜索都是一次全表扫描,且中文还要面对"词之间没有空格"的分词难题——LIKE 根本不具备分词能力,只能做最原始的字串匹配。想搜"事务"却搜不到"分布式事务处理",因为它只匹配连续子串。规模一大,LIKE 的响应时间随数据量线性恶化,完全不可接受。
倒排索引的结构
倒排索引是"词到文档列表"的映射,核心两张逻辑表:
词典 (Term Dictionary): 分布式 | 事务 | 一致性 | 锁
倒排表 (Posting List):
分布式 → [doc1, doc3, doc7]
事务 → [doc1, doc5]
一致性 → [doc3, doc7, doc9]
查询"分布式 事务"时,取两个词的 posting list 做交集得到 [doc1],无需扫描任何无关文档。词典本身通常再用 B+ 树或 FST(有限状态转换器)组织,支持前缀、模糊等高效查找。倒排索引把"在文档里找词"反转为"在词里找文档",这正是它快的根因——检索的单位是词,不是文档。
中文必须分词
英文天然按空格切词,中文不行。"分布式事务一致性"若不分词,只能整串匹配,搜"事务"就找不到。分词把连续汉字切成有意义的词元:
原文: 分布式事务保证数据一致性
分词: 分布式 | 事务 | 保证 | 数据 | 一致性
分词质量直接决定召回率和准确率。过粗会漏召回(该命中的没命中),过细则噪音多、索引膨胀("分布式事务"被切成"分布""式""事务"反而难用)。常用做法是结合词典匹配与统计模型,并维护停用词表(的、了、在等)减少无意义词元。分词器选错,再好的检索架构也白搭。
索引构建与查询流程
构建时,文档先分词,再逐词把文档 id 追加进对应 posting list,随后按词排序、压缩存储。查询时经历四个步骤:分词、查词典取 posting list、多词间做交集或并集、按相关性排序。
搜索 "分布式 一致性"
1. 分词 → [分布式, 一致性]
2. 查倒排 → 分布式:[doc1,doc3,doc7] 一致性:[doc3,doc7,doc9]
3. 交集 → [doc3, doc7]
4. 按相关性(词频/位置/字段权重)排序返回
相关性打分通常结合词频(TF)、逆文档频率(IDF)和字段权重,让更匹配的结果排前面。这比 LIKE 只能"有或没有"丰富得多,也是搜索引擎体验好的关键。
落地选型与边界
数据库内置全文索引(如 MySQL 的 FULLTEXT、PostgreSQL 的 tsvector)适合中小规模、和主数据强一致的场景,配置简单,不用引入新组件。数据量大、需要复杂相关性排序、高并发检索时,专用引擎更合适——它们把倒排索引、分词、分片、近实时刷新都封装好了,扩展性和检索能力远强于数据库内置方案。
要认清的边界:倒排索引为"搜"优化,不为"事务"优化。它和主库之间通常存在同步延迟,要求"写入后立即可搜"的场景,需要明确同步策略与一致性预期,不能默认两者强一致。另外,全文检索不擅长精确数值范围、聚合统计这类事务型查询,它和数据库是互补关系,不是替代关系。
性能与一致性的权衡
倒排索引更新成本不低,所以多数检索引擎采用"近实时"而非"实时"刷新:文档写入后先进内存缓冲区,定期(例如每秒)刷成新的段文件再可被检索,因此写入到可搜之间存在很短的延迟。这个延迟换来的是极高的写入吞吐和检索性能。如果业务要求写入后立刻能搜到,要么接受略微下降的性能做实时刷新,要么在主库用数据库查询兜底新写入、用检索引擎扛历史检索。理解这段延迟的存在,才能正确设定用户对"搜不到刚发的内容"的预期,也避免把检索引擎当成强一致的实时数据源来用。
选型时别忽略运维成本
引入专用检索引擎意味着多维护一个分布式组件:集群、分片、副本、版本升级都要人管。中小团队若数据量没到亿级、查询 QPS 不高,先用数据库内置全文索引往往够用,等真的触到瓶颈再迁移也不迟。过早引入重组件,运维成本可能超过它带来的收益。判断标准很简单:现有方案是否已经在响应时间或资源上明显撑不住,而不是"听说搜索引擎更好"。把检索能力当作随业务发展逐步演进的能力,比一次性上重型架构更务实。
浏览 0
点赞 0
评论 0