连载中 2/20

倒排索引:正排查我,倒排查谁

2026-08-11 · 1924 阅读 · 0 评论 · 0 赞

从玩具到引擎

上一篇那张「词 → 商品」的手工表只解决了思想问题。真让它在十亿文档、百万词条下工作,还差三块核心零件:分词器、词典、倒排表。这一篇把每块零件拆开看。

零件一:分词器(Analyzer)

倒排的前提是分词——把「燕麦拿铁(大杯)」切成词条。ES 的分析器是三段式流水线:

原文「Oat Latte 大杯 #新品」
  -> Character Filters:清洗字符(如去掉 HTML 标签)
  -> Tokenizer:切段(按空格/规则切出 Oat、Latte、大杯、#新品)
  -> Token Filters:加工(转小写、去停用词、同义词扩展)
最终词条:oat、latte、大杯、新品

分词质量直接决定搜索质量:切得太粗,搜「拿铁」搜不到「生椰拿铁」;切得太碎,语义就散了。中文分词是重灾区,第 4 篇专门讲。

零件二:词典(Term Dictionary)

所有词条组织成一个有序词典。查词快不快,取决于词典用什么结构。Lucene 的答案是 FST(Finite State Transducer,有限状态转换器),理解它只需要抓住两个词:

  • 前缀共享:latte、latteart、lattenut 三个词共用前缀 latte,重复的字只存一份——百万词条压缩后能整个塞进内存;
  • 边查边走:查 lattern 不用从头比对,沿着 l-a-t-t-e-r-n 的路径走,走到头没有就是没有,还能顺路给出所有前缀为 lattern* 的词(前缀搜索白送的)。

MySQL 系列讲过的 B+ 树是「磁盘友好」的结构,FST 则是「内存友好」的结构——成本模型不同,选择就不同。

零件三:倒排表(Posting List)

词典里每个词条后面挂着文档号列表,还附带频率与位置信息:

拿铁 -> [doc1(出现3次,位置0,5,9), doc2(出现1次,位置2), doc4...]

三样信息各有用途:docId 告诉你命中谁,频率(TF)参与算分,位置(Position)支撑短语查询(match_phrase 靠它判断「燕麦」和「拿铁」是否相邻)。存盘时倒排表还要做压缩:相邻 docId 的差值很小(增量编码),Frame of Reference 按块压缩,过滤缓存再用 Roaring Bitmap 按位运算加速。

多词条查询时,两个倒排表要合并:AND 取交集,OR 取并集。交集不逐个比对——docId 有序,配上跳表(Skip List),一方的游标可以大步跳着追赶另一方,交集合并不再是遍历。

评分的直觉:BM25 从哪来

倒排解决了「找到谁」,还要解决「谁排前面」。BM25 评分的三个直觉:

  • 词频(TF):这个词在这篇文档里出现越多,文档越相关——但有饱和效应,出现 10 次不代表比 5 次相关一倍;
  • 逆文档频率(IDF):全库只有两篇文档含「燕麦」,命中它的价值远高于人人都有的「咖啡」——稀缺的词更能代表意图;
  • 文档长度归一:同样的词频,短文档比长文档更相关——两百字提三次,比两万字提三次分量重。

公式不用背,直觉记牢,第 8 篇还会回来调参。

倒排的另一面:doc values

倒排索引擅长「词找文档」,不擅长「文档找字段值」——排序、聚合、脚本取值要反复问「这一篇的 price 是多少」,倒排表干不了这个活。于是 ES 给每个字段配了doc values:建索引时就生成的一列式正排数据,按文档号顺序排列,天然适合扫描聚合。一句话分工:倒排管搜,doc values 管排和聚。

小结

一台搜索引擎的四大件:分词器切词,FST 词典查词,压缩倒排表管命中,doc values 管排序聚合。词频与稀缺度撑起评分直觉。零件都认识了,下一篇回到使用者的视角:Index、Document、Shard、Replica——ES 的世界观,以及它和 MySQL 概念的对应表。

☕
503

10 年全栈工程师 · 503咖啡馆主理人

#Elasticsearch#倒排索引#FST#BM25#doc values

评论 (0)

热门推荐

连载中 11/22

主从搭建实操:从零配出一主两从

光讲原理不过瘾?手把手搭一主两从:my.cnf 六个参数、复制账号、GTID、CHANGE REPLICATION SOURCE TO、SHOW REPLICA STATUS 验收,附翻车排查清单。

#MySQL#主从复制#GTID#主从搭建#高可用
2026-05-07 · 10101 阅读 · 0 评论 · 0 赞
连载中 16/22

连接池:HikariCP 参数与连接风暴

连接池不是越大越好:8 核机器配 1000 连接反而更慢的数学原理,HikariCP 四个必调参数,maxLifetime 与 wait_timeout 的隐形陷阱。

#MySQL#连接池#HikariCP#maxLifetime#连接风暴
2026-05-10 · 9873 阅读 · 0 评论 · 0 赞
连载中 4/16

缓存穿透:恶意 ID 打穿 MySQL 的四道防线

请求的数据在缓存和数据库里都不存在时,缓存形同虚设。聊聊参数校验、空值缓存、布隆过滤器、限流熔断四道防线的原理与组合打法。

#Redis#缓存穿透#布隆过滤器#高可用
2026-05-16 · 9294 阅读 · 21 评论 · 287 赞