召回系统介绍

一、以Lucene为例介绍召回系统

1、倒排检索

Lucene的倒排索引由 Term Index -> TermDictionary -> Posting List 三层组成,倒排检索实际上就是通过分词Term查询到倒排拉链,然后对所有拉链进行合并。

Term-> Posting List,可以直接通过B+树来完成(对Term创建索引,叶子结点存储拉链的磁盘位置+长度),但是数据量较大时Term索引无法完整放在内存里,因此Lucene加了一个TermIndex,FST有限状态机转换器(类似Trie树),为了进一步压缩空间,Trie树里不存储所有Term,只包含Term的一些前缀,Term的后缀存放在磁盘上,通过TermIndex快速定位到后缀block在磁盘上的位置,遍历找到匹配的Term,进而得到PostingList在磁盘上的位置。

TermIndex相当于对term进行了前缀压缩,公共前缀只存储一份,而使用map存储term -> List映射,相当于每个term都要存储一份,内存无法放下全部term。

相比于倒排检索,Mysql 只有 term dictionary 这一层,是以 b-tree 排序的方式存储在磁盘上的,检索一个 term 需要若干次的 random access 的磁盘操作,速度非常慢。

对于联合查询(拉链合并),Lucene提供了两种方法:

  • 使用跳表结构,合并时同时遍历两条拉链,互相skip,时间复杂度O(m+n);
  • 使用位图结构,对两条拉链分别计算位图,然后对位图进行AND,OR操作;

如果查询的Term在内存中有bitset的缓存,就用bitset合并,否则使用跳表。

因为bitset要表示Doc全集所以一条拉链的bitset是比较稀疏的,因此使用Roaring Bitmap压缩存储。

为了防止一条拉链的跳表全加载进来内存放不开,会将DocId差值编码,使用最大值所占空间存储每个值,然后每128个DocId压缩成一个PackBlock作为跳表的一个节点(使用packblock首元素表示节点值),合并时公共的PackBlock先从磁盘上读取并解压缩,再计算公共DocId。

ElasticSearch官网上有这两种方式的性能对比:因为 PackBlock 编码非常高效,对于简单的相等条件的过滤缓存成纯内存的 bitset 还不如需要访问磁盘的 skip list 的方式要快。

2、正排检索

需要对某个正排属性进行聚合,或者希望返回结果按照某个正排属性进行排序。先检索出所有DocId在分别读取正排信息进行排序效率较低,还非常占内存,Lucene使用了DocValue,一个基于docid的列式存储。当我们拿到一系列的docid后,进行排序就可以使用这个列式存储,结合一个堆排序进行。

二、介绍广告召回系统

相关推荐
强哥之神2 小时前
英伟达发布 Llama Nemotron Nano 4B:专为边缘 AI 和科研任务优化的高效开源推理模型
人工智能·深度学习·语言模型·架构·llm·transformer·边缘计算
G皮T3 小时前
【Elasticsearch】深度分页及其替代方案
大数据·elasticsearch·搜索引擎·scroll·检索·深度分页·search_after
DemonAvenger3 小时前
高性能 TCP 服务器的 Go 语言实现技巧:从原理到实践
网络协议·架构·go
Code季风4 小时前
深入理解微服务中的服务注册与发现(Consul)
java·运维·微服务·zookeeper·架构·go·consul
小马哥编程4 小时前
【iSAQB软件架构】架构决策记录-ADR
数据库·架构·系统架构·设计规范
木鱼时刻4 小时前
容器与 Kubernetes 基本概念与架构
容器·架构·kubernetes
G皮T5 小时前
【Elasticsearch】检索排序 & 分页
大数据·elasticsearch·搜索引擎·排序·分页·检索·深度分页
zhuyasen6 小时前
定义即代码!这个框架解决了90%的Go开发者还在低效开发项目的问题
架构·go·gin
LCG元6 小时前
云原生微服务间的异步消息通信:最终一致性与系统容错的架构实战
微服务·云原生·架构
骑着王八撵玉兔7 小时前
【性能优化与架构调优(二)】高性能数据库设计与优化
数据库·性能优化·架构