聚合国内IT技术精华文章,分享IT技术精华,帮助IT从业人士成长

10s内百亿级召回——腾讯TRS之分布式倒排索引架构实践

2023-08-01 19:31 浏览: 791972 次 我要评论(0 条) 字号:

导读 今天的介绍会围绕下面三点展开:

全文目录:

1. 背景介绍
2. 整体架构
3. 详细设计
4. Q&A

分享嘉宾|欧阳金华,腾讯 高级工程师

编辑整理|罗壮 Soul

内容校对|李瑶

出品社区|DataFun


01

背景介绍

推荐是一个漏斗筛选的过程。TRecall位于从千万级甚至亿级的物品库中筛选出成千上万个文档的环节。它面临的系统挑战主要有三个方面:

  • 处理海量数据,可能会到百亿级别的索引;

  • 数据实时性,比如在冷启动场景下,用户希望自己新发布的视频可以很快地得到曝光,这就要求视频从进入系统到最终曝光出来可能只有秒级甚至更短的时延;

  • 系统本身的性能、成本和可用性。

TRecall位于系统的最下游,和其他组件(比如向量召回、图召回)一起构成召回环节。

TRecall是腾讯内部打造的高性能、低成本、高时效性分布式索引平台;提供推荐、类推荐场景下统一倒排召回服务;为业务提供一站式、托管式运维。

我们对TRecall提出的目标为:

  • 性能方面,我们希望TRecall 可以满足百亿级doc、千万qps的召回场景,并将平均响应时间控制在10ms以内;

  • 可用性方面,我们希望TRecall能够提供高并发大流量场景下系统failover能力,可用性不低于4个9;

  • 扩展性方面,我们希望TRecall能够结合业务数据和请求频繁变化场景,比如倒排索引的变更以及正排字段的增减,能够支持用户无感的动态扩缩,最大化节约成本;

除以上之外,还有实时性、插件库、算子库等方面的要求,参见下图。

除了系统本身外,我们也对外提供了一些运营能力,包括业务接入、业务部署、业务调试、业务运维以及运营管理。

因此,TRecall在公司内部得到了广泛的应用,主要是前面所说的两类场景,一类是推荐场景,比如ICF类召回、冷启类召回、CB类召回等,另一类是类推荐召回,比如评论、弹幕等场景。

整体来说,TRecall在公司内部应用覆盖较为广泛,性能也很好,成本也相对较低。在公司中已有几十个业务场景接入了TRecall,每天的调用量会达到百亿级别。针对推荐场景,P99耗时低于10ms。10000次的检索成本低于0.001元,单集群能够支撑的文档量级超过百亿级。TRecall对外的接口也较为多样,包括推荐类、全链计数类(比如一个视频下的评论有多少条,弹幕有多少条)、流式接口(比如圈选一个文档相关的所有用户,可以通过这个接口,按批拉出数据)、对外协议(包括fbs无解码开销和pb跨语言协议等)。

02

整体架构

1. 整体介绍

接下来介绍TRecall的整体架构。

首先来介绍下大数据领域的一个典型的Lambda架构。这个架构主要分成三个部分。首先,batch processing批处理层,处理用户历史数据。其次,real-time processing实时处理层,处理用户实时增量数据。最后,有一个加载历史数据和实时增量数据的serving层,将数据结合起来统一对外提供服务。这个架构很好地平衡了时延和吞吐的能力,也在一定程度上兼顾了分区容错。

当然,这种架构也存在一些问题。比如real-time processing和batch processing会用不同的存储,这就会导致系统可能需要两套代码。但是这种架构的思想是值得借鉴的,TRecall架构设计就借鉴了这种思想。

TRecall的整体架构设计如下图所示,虚线框部分是系统本身,左边是数据流,右边是检索流,用户通过Kafka 或者COS(腾讯内部的分布式对象存储系统)提供数据,我们会通过增量服务对流式数据更新到Kafka,通过builder将定期批量数据和通过HBase存储的流式数据更新到COS,最终推到线上,在线上环境下,每个分片都包含实时数据和批量数据,服务会将实时和批量结合到一起,放到一个索引中对外提供服务。从大的层面来讲,整个系统也可以分为速度层、批量层、服务层。

接下来介绍下TRecall中行列式的思想,这是一种很常见的做法,即分片与副本机制,行代表一个完整的数据集,列代表副本,用分片来解决大数据的问题,用副本来解决分区容错的问题。

TRecall中有正排、倒排、布隆过滤以及插件等各个环节,下图中是一个简单的例子。我们从存储中查询性别为1,年龄大于10,地理位置在a或者b的用户。其中db为索引库;性别为倒排索引,包含索引域和索引值,是文档列表的映射;年龄和地理位置是小正排字段,是存储在引擎中用来计算的字段;当然,系统本身还包括布隆过滤和插件化的功能。

以下,就是TRecall整体的架构,其中用三种颜色表现了不同的组成部分,黄色部分是业务组件,绿色部分是离线组件,蓝色部分是在线组件。用户通过Kafka或者COS提供数据,增量索引构建服务监听Kafka的消息并在获得消息后做校验,校验后把数据写到HBase,同时也会向线上Kafka推送一份消息,在线sever也会监听线上Kafka,并将消息拉取到本地,从而达到快速上线的目的。另外,离线builder会用COS中的数据以及HBase中的数据按照用户需求定期构建索引,然后推送到线上。在线组件主要分为三个部分,一个是master节点,一个是server节点,一个是虚拟的proxy节点。

Master节点是一个paxos节点,主要负责整个集群的管控,比如当索引发生变更的时候,它会通知在线索引,并且控制server按照行列的方式或者按照双buffer的方式加载索引,也会将索引、分片、物理机、Kafka的partition一一对应。

Server节点是存储计算一体的节点。

Proxy是一个虚拟节点,也可以是一个物理部署的节点。当用户发起请求后,如果发现数据不完全在自己的机器上,它会从其它节点拿数据,并返回给用户,如果数据完全在自己的机器上会本地计算完毕后直接返回给用户。它也可以是一个物理部署的节点,比如在类推荐场景下,需要缓存的能力,我们会将这种能力集成在proxy节点,在流量峰值的时候起到一定的容错和降级的作用。

2. 增量设计

接下来将分模块对TRecall进行详细地介绍。

增量的部分主要负责从Kafka消费消息,持久化到HBase中去,同时根据索引的诉求,将用户传过来的文档转化成正排倒排的各个字段推送给线上的Kafka,以上过程采用内存计算的方式实现。用户可以以插件的形式自定义倒排的排序方法。

3. 离线构建

我们把离线构建分成了四个主要阶段,第一个阶段是离线源数据的整合,主要工作包括合法性校验,数据类型转换,数据源聚合,数据计数,阈值预警;第二个阶段是数据预处理,主要工作包括数据合并,插件处理链,动态特征join;第三个阶段是DocRow生成,主要工作包括正排信息提取,倒排信息提取,外部正倒排聚合;第四个阶段是索引产出,主要工作包括Localid生成,倒排索引生成,正排索引生成。用户可以很方便地扩展自己的插件和字段。

4. 在线引擎架构

在线引擎架构是一个极简的单层架构,也可以退化成2层或者3层的架构,当分片非常多的时候,为了降低开销,我们可以通过多级归并的方式来实现。master节点、proxy节点、server节点的具体功能在前面已做过相应的介绍,在这里就不做赘述了。

在存储层面,在线引擎架构主要包括倒排索引、正排索引、ID映射;在计算层面,在线引擎架构主要包括倒排检索、过滤、打分排序;在功能层面,在线引擎架构主要包括倒排检索、正排过滤、统计监控、流式查询等能力。

03

详细设计

1. 索引管理

正如前面提到的,这一部分主要是靠master节点来完成的。它会负责和server节点交互,提供心跳保活、数据分片管理、索引版本管理的能力。它也会通过DB来和运营系统交互。还会和腾讯的名字服务交互,比如我们发现单节点的负载过高,可以降低对应节点的权重。当索引切换的时候,如果是通过断流的方式实现,可以通过名字服务来控制,把节点的流量切断,实现安全切换。

以下是索引切换的流程。为什么要做定期构建?首先,这是一个内存整理的过程,其次,它可以提供snapshot,当一个新的节点加入集群时,这个节点可以通过这个snapshot快速追上增量数据,而不用从头开始消费Kafka。新索引产生后,会首先判断是否可以切换,比如当CPU负载很高或者索引不健康时会停止或推迟切换。目前系统支持两种切换方式,一种是rolling update,一种是dbd,可以灵活配置。在切换过程中如果遇到问题会立即告警并停止切换。切换的并行度是可控的,可以根据负载情况控制并行度。索引是通过p2p的方式进行分发的。

2. 存储设计

存储主要包含三个部分,正排、倒排和id-mapping。我们会将文档id转化成从小到大递增的数字id,这种做法有两个好处,一个是减少内存空间,另一个是能够快速定位,可以通过offset的方式像访问数组一样去定位。从下图可以看出,完整的存储包括倒排信息、正排信息、id-mapping、版本信息、schema信息、Kafka信息(消费什么partition,从什么offset开始消费等)。

正排在TRecall中采用列存储的方式。这种存储方式有几个好处,首先,不会存储与计算无关的字段;其次,这方便我们做字段的原地更新;同时,它还能通过统一的offset来访问(前面提到的id-mapping);另外,它支持多种类型的字段,比如单值、多值。

倒排的本质是一个KQV。第一级key是用户选择的倒排字段,第二级key是字段的值,第三级是倒排拉链。我们支持的倒排丰富多样。

3. 计算设计

计算的核心点是我们计算的模型是push模型还是pull模型。pull模型的好处是逻辑比较简单。push模型的好处是比较好做并行化。TRecall中采用pull和push相结合的方式。

我们对pull模型做了一些优化。如果使用pull模型来设计,整个过程包括以下几个环节:检索、校验、过滤、打分、结果采集。这种实现主要有两个缺点,一是每个文档都需要判断结束条件,二是会有很多虚函数对应的开销(next方法通常采用继承的方式实现)。

针对以上两个问题,我们采取了一些优化方法,首先是CRTP,它通过静态多态的方式来实现虚函数,这种方式可以减少虚函数的调用开销,将查找开销转化为编译开销。其次是向量化执行,批处理,减少函数调用次数和频繁判断结束条件开销。

我们还在计算设计上做了很多其它的优化。

  • 池化技术:通用池、特殊对象池

  • 无所化:DBD(双buffer)、RCU(read copy update)通过数据拷贝的方式替换锁

  • 零拷贝:协议零拷贝(通过move、const引用传递协议对象)、正排计算(原地计算,避免拷贝正排对象)、中间结果(统一采用原始数据指针,避免转换和拷贝)

我们在计算内部做了很多并行的优化:

  • 数据分片:节点级并行

  • 多任务并行:线程级

  • 数据级:pipeline内并行

  • 指令级并行:SIMD指令

4. 柔性能力

我们提供了很多柔性能力:

  • 熔断能力:框架级、任务级

  • 降级能力:分片缩召回

  • cache能力:基于分段锁高性能cache

04

Q&A

Q1:哪些字段作为倒排的key,哪些字段作为小正排的key?

A:由业务来选择的,比如说我们需要一个文档里面,它需要来检索,它能够很好地把数据集划分更小,比如说像性别男女这种,可能就会作为一个倒排的key,而其他的一些变化比较频繁而且维度又比较高,可能会把它作为一个正排的状态做过滤,本身也是一个内存和计算的权衡。

Q2:倒排过滤用到正排,如何降低这个过程的内存占用和耗时?

A:列存储和并行。

Q3:索引如何分片?

A:按照Doc ID哈希或者根据查询(用户自定义)的维度去分片。

Q4:TRecall有搜索团队使用吗?

A:搜索和推荐有一定的共性,但也有特殊性,目前搜索团队没有用到。

今天的分享就到这里,谢谢大家。


分享嘉宾

INTRODUCTION


欧阳金华

腾讯

高级工程师


RS推荐系统高级工程师,主要负责TRecall倒排召回系统与业务融合架构推进。


点个在看把好内容分享给更多人



网友评论已有0条评论, 我也要评论

发表评论

*

* (保密)

Ctrl+Enter 快捷回复