广告引擎的索引设计思路,是将广告设置的标签作为 Key 来构建倒排索引,在 posting list 中记录对应的广告设置列表,然后为标签进行 ID 编号,让系统处理标签的过程能更高效。这么说比较抽象,我来举个例子。
如果广告设置的标签是「地域:北京」「兴趣:篮球」「媒体:体育网站」,那我们可以使用一个 32 位的整数为每个标签进行编号。具体来说就是将 32 位的整数分为两部分,高位用来表示定向类型,低位用来表示这个定向类型下具体的标签。
比如说,我们采用高 8 位作为定向类型的编码,用来表示地域定向、兴趣定向和媒体定向等。用低 24 位,则作为这个定向类型下面的具体内容。那在地域定向里,低 24 位就是每个地区或者城市自己的编码。这样,我们就可以将广告设置的标签都转为一个编号了(高、低位的分配是可以根据实际需求灵活调整的)。
- 将标签加入过滤列表
那是不是所有的标签都可以作为倒排索引的 Key 呢?你可以先自己想一想,我们先来看一个例子。
如果所有的广告投放设置都选择投放在 App 上,那么「媒体类型:App」这个标签后面的 posting list 就保存了所有的广告设置。但是,这样的标签并不能将广告设置区分开。为了解决这个问题,我们可以使用类似 TF-IDF 算法中计算 IDF 的方式,找出区分度低的标签,不将它们加入倒排索引。
那我们什么时候使用这些标签呢?我们可以将这些标签加入「过滤列表」中,然后在倒排索引中检索出结果以后,加上一个过滤环节,也就是对检索结果进行遍历,在遍历过程中使用「过滤列表」中的标签进行检查,这样就完成了标签是否匹配的判断。
- 用标签进行索引分片
其实,对于标签的匹配使用,我们还有其他的方案。我们再来看一个例子,假设平台中有一半的广告投放设置希望投放在移动 App 上,另一半希望投放在 PC 网站上,那如果我们以「媒体类型:App」和「媒体类型:PC 网站」作为标签来建立倒排索引的话,这样的标签是有区分度的。但是由于这两个标签后面的 posting list 都会非常长,各自都保存着一半的广告设置。因此在进行 posting list 归并的时候,实际上就等于要遍历一半的广告设置。这反而会降低检索效率。
因此,对于「媒体类型」这类(以及「性别」、「操作系统」等)具有少量的标签值,但是每个标签值都有大规模区分度的设置维度来说,我们可以不把它们加入到倒排索引中,而是根据标签来将广告设置进行 分片。也就是把投放 PC 网站的广告设置作为一组,投放 App 的广告设置作为另外一组,分别建立倒排索引。
如果这样的有区分度的设置维度不止一个,那我们就使用树形结构进行划分,将最有区分度的设置维度(如「媒体类型」)作为根节点,不同的设置值作为分叉(如 PC 网站和 App 就是「媒体类型」维度下的两个分叉)。在这个节点下,如果有其他的设置也具有足够的区分度,那也可以作为子节点继续划分。然后对于被划分到同一个叶子节点下的一组,我们再利用标签建立倒排索引。
通过这样的树形结构,我们根据广告请求上的标签,就能快速定位到要找的索引分片,之后,再查找分片中的倒排索引就可以了。
总结来说,广告设置对广告引擎来说,就像搜索词对搜索引擎一样重要。但是对于广告设置,我们不会像关键词一样,全部加入倒排索引中,而是会分别加入到三个环节中:第一个环节,作为树形结构的节点分叉进行分流;第二个环节,作为倒排索引的 Key;第三个环节,在遍历候选结果时作为过滤条件。通过这样的设计,广告引擎中的检索空间就能被快速降低,从而提升检索效率,快速返回候选结果了。