召回系统介绍

一、以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后,进行排序就可以使用这个列式存储,结合一个堆排序进行。

二、介绍广告召回系统

相关推荐
uhakadotcom3 小时前
视频直播与视频点播:基础知识与应用场景
后端·面试·架构
沉登c7 小时前
第 3 章 事务处理
架构
数据智能老司机9 小时前
CockroachDB权威指南——CockroachDB SQL
数据库·分布式·架构
数据智能老司机10 小时前
CockroachDB权威指南——开始使用
数据库·分布式·架构
c无序10 小时前
【Docker-7】Docker是什么+Docker版本+Docker架构+Docker生态
docker·容器·架构
数据智能老司机10 小时前
CockroachDB权威指南——CockroachDB 架构
数据库·分布式·架构
矿渣渣10 小时前
RM Cortex-A7 架构中“SEV”汇编指令解析
汇编·架构
uhakadotcom11 小时前
Flutter入门指南:快速构建高性能移动应用
面试·架构·github
月阳羊12 小时前
【无人机】无人机PX4飞控系统高级软件架构
嵌入式硬件·架构·系统架构·无人机
uhakadotcom12 小时前
MVC 和 MVVM 架构模式:基础知识与实践
后端·面试·架构