Earlybird: Twitter的实时搜索引擎 - Searcher's Log

Earlybird: Twitter的实时搜索引擎 - Searcher's Log

    Earlybird: Twitter的实时搜索引擎

    /* -*- author: Tan Menglong; email: tanmenglong_at_gmail; twitter/weibo: @crackcell; 转载请注明出处 -*- */
    Table of Contents

        1 初识Earlybird
        2 总体架构
        3 索引结构
            3.1 Term字典
            3.2 分段索引
                3.2.1 活动分段
                3.2.2 只读分段
        4 小结

    1 初识Earlybird

    Earlybird是Twitter的实时搜索引擎,它目前服务于Twitter的tweet的搜索。它在建立之初,Twitter的工程师就为它定下几大目标:

        低延迟高吞吐的检索能力
        低延迟高处理能力的索引构建能力。特别地,由于Twitter上的经常会有“信息爆发”,索引系统需要能够处理突发的峰值。
        索引的并发读写能力
        时效性为中心的设计:在排序算法中,发布时间是一个决定性的特征。时间越近,权值越高。进而要求,设计索引的时候要以“按时间顺序从新到旧遍历”为中心优化。

    Earlybird还有一个特点,每个用户得到的搜索结果都是“个性化”的。结果排序会根据用户的本地关系图等特征进行调整。总的来看,earlybird需要处理如下3种数据(Twitter的工程师称之为信号,signal):

        静态信号(Static Signals):在初次建立索引的时候被建入,例如:tweet的语言
        共鸣信号(Resonance Signals):随着时间的推移,需要不断动态更新的信息,例如:tweet被转发的次数
        搜索用户的信息:和用户相关的信息,在发起搜索时被用来进行个性化排序。例如,本地关系图等

    2 总体架构

    Earlybird的架构图1如下:

        图的上半部分是tweet的索引构建阶段,下半部分是检索阶段。Earlybird服务是整个系统的核心,它负责建立、检索并维护着倒排索引数据。
        索引构建过程如下:
            用户发布的新tweet会被发送到一个队列(Ingestion Pipeline)里面。在这里,tweet的文本被分词,并被加上静态信号。
            按照hash分割,tweet被分发到各个Earlybird服务上。Earlybird将tweet实时地建立索引
            同时,另外有个一个Updater服务,它推送共鸣信号到Earlybird服务,动态地更新索引。
        查询过程如下:
            用户搜索请求搜先到达Blender服务(搜索前端服务器),Blender解析请求,并将搜索用户的本地社交图谱(Local Social Graph)合并到搜索请求中,往下发送到Earlybird服务。
            Earlybird服务执行相关性计算并排序。并将排序好的tweet列表返回给Blender。
            Blender合并各个Earlybird返回的列表,并执行一些重排序(Reranking),然后返回给用户

    3 索引结构

    简要来说,一个典型的搜索引擎查询过程是:

        对query分词成term
        对每个term查询term字典,获得倒排索引拉链的指针
        获取倒排索引拉链
        合并每个term的倒排索引拉链 5)返回给用户

    当然,这中间省略了可能穿插在各个环节的不同赋权和排序的操作。这一节,我们来一起看看Earlybird索引结构中主要的2个部分: Term字典 和 倒排索引 。
    3.1 Term字典

    Term字典一般是Hash-based或者Tree-based的。Earlybird不支持需要计算query中term顺序 以及term区间 等复杂查询,所以hash-based就足够了。简单来讲,它的term字典就是一个大的hashtable。特别地,Java默认的Hashmap使用的是拉链法,不是GC友好的数据结构。所以Earlybird自己实现了开放地址的Hashtable。为了节约空间,每个Term被分配了一个唯一且单调递增的id做为key,value的数据包含:

        Term对应的倒排索引数据长度
        指向位置倒排索引数据末尾的指针

    下图是Earlybird索引结构的一个结构图:
    这里有2个小问题:

        Q: 保存倒排索引长度的目的?
            A: 在多Term的拉链归并的时候,能按照索引长度进行排序,使得长度小的先先被合并,减少不必要的索引扫描
        Q: 为什么保存的是倒排索引末尾的指针,而不是头部?
            A: 如初识Earlybird中提到,Earlybird有对发布时间做优化,即,新的tweet有较高权值,那么它理应被先遍历到。

    3.2 分段索引

    为了实现高吞吐、低延时地并发索引更新和检索服务,Earlybird采取了将分离索引的读和写的策略:每个实例维护了多个索引分段(目前是12个1),每个分段保存相对较少量的tweet(目前是223~840万1)。新增加的tweet首先被放到同一个索引分段中。这样,在任意时间,只有一个分段是在发生写操作的(我们称之为“活动分段”),而其它分段处于只读状态(称之为“只读分段”)。
    3.2.1 活动分段

    上图中,除了Term字典以外,其它3个区域都是活动分段中的部分。每个分段中有4个pool,每个pool里面有若干个slice,每个slice保存一个term的postinglist,每个postinglist里面存储着一个term的posting信息,每个posting是一个32-bit的整数:24-bit用作文档id;8-bit用作位置id,保存term在tweet中出现的位置。因为tweet有140个字符的限制,所以8-bit足够用了。slice的大小由所在pool的规格决定,4个pool分别对应21、24、27、211。建立索引时,先尝试填满21pool中的slice,如果填满,就转到24的pool的slice,以此类推。同时,term dict保留着term对应的最后一个pool的中postinglist胃部的指针,这样做的目的是,Twitter的检索顺序是按照时间顺序,从新到久查找。数据的新旧程度在排序算法中占了很大比重。 所以查询的步骤是这样的:

        对Query进行分词成term
        对每个term,查询Term dict中对应的postlinglist的指针
        通过指针,遍历最多4个pool中的slice数据,获得整个postinglist

    另外,为了加快处理速度,处于活动状态的分段数据是不会被压缩的。
    3.2.2 只读分段

    当一个活动分段写满之后,它就会被转换成一个只读分段,转换过程中,一系列优化会被执行,以提高效率:一份新的索引数据会被创建,原数据不会发生改变。当新数据创建完毕之后,原数据会被优化过的新数据替换掉。 postinglist会以1000为阈值划分为“长”和“短”的两类。短的,posting保持原样(24-bit文档id加上8-bit的位置信息),但是posting会按照时间逆序排列。对于长的postinglist,引入基于块(block-based)的来源于PForDelta2和Simple93的压缩算法。postings数据被存放在256字节定长的block中。block的最开头4个字节是未压缩的,接下来的4个字节存储了block的头信息,剩下248字节(1984bits)用来存储编码过后的变长的posting数据。原始的posting数据被转换成{d-gap, position}的对。头信息中保存着,后面的变长数据中存储了多少个{d-gap, position}对。这个n的计算方法如下:
    n(⌈(log2gapmax)⌉+⌈(log2posmax)⌉)≤1984
    因为上面n的取值范围在一个相对较小的区间,可以使用预先定义好的bit操作来加速编码解码。

    4 小结

    Earlybird给我们展示了实时搜索引擎开发中一系列工程实践的方法。其中2点我个人比较赞同:

        利用已有开源代码:开发一个新项目的时候,借鉴同领域成熟解决方案,对其进行定制。不到万不得已,不要自己重新写一套全新代码。Earlybird就是一个典型的在旧方案基础上,做了一系列tradeoff,最终达到目标的一个例子。
        从需求出发做设计:Earlybird需求阶段即明确了时间在排序中的决定性因素,进而在后续的设计中,围绕这一目标做优化。咋一看似乎绑定太紧,后续需求变动会牵一发而动全身。互联网产品应该坚持“小步快跑”的原则,在不断迭代中优化架构。

    Footnotes:

    1 Michael Busch, Krishna Gade, Brian Larson, Patrick Lok, Samuel Luckenbill, and Jimmy Lin. Earlybird: Real-Time Search at Twitter.

    2 梁斌. "PForDelta索引压缩算法的实现"

    3 V. N. Anh and A. Moffat. Inverted Index Compression using Word-Aligned Binary Codes.

你可能感兴趣的:(twitter)