近日,Facebook为post搜索添加了Graph Search。我们来看几个惊人的数字:Facebook每天约产生10亿条post,post索引总数已上万亿条,数据量超700TB。为这些post建立索引和构建实时查询系统在工程上存在非常大的挑战,那么Facebook又是如何应对这一挑战的?以下为译文:
数据收集
Facebook的底层数据结构是为了满足快速迭代网络服务的需要,这却也成为构建实时查询系统所面临的最大挑战。增加新功能往往需要改动这些数据结构,而Facebook一贯的作风是变动不要给工程师平添烦恼。然而,由于wall post、photo、check-in等功能采用不同的数据存储机制,对底层数据结构进行改动增加了以时间、地点和标签进行排序的难度。当前,排序和索引的数据约有70种,其中很多种都是基于特定post类型的。此外,数据存储在一个用于生产环境的MySQL数据库中。这也就意味着,当数据库同时支撑生产传输及数据收集时,负载将大幅度增加,因此这些过程必须被严格监控。
索引建立
数据收集来后,我们将其存储在HBase集群中,然后执行Hadoop map-reduce任务,高并行地为之建立索引。为原始数据建立索引后,然后便传输给搜索的基础Unicorn。我们将数据分为两块——文档数据和反向索引(inverted index)。每条post的文档数据包含用于排序的相关信息。传统意义上搜索索引有什么,反向索引就有什么。要建反向索引需要遍历每一条post,并确定与假设中的哪种搜索过滤器相匹配。
索引更新
为了更新索引,我们使用Wormhole技术订阅MySQL数据库中的变更。一旦有新post,现有post被修改、删除或与post有关的相关数据被编辑等情况发生时,我们就会都对相关post进行更新操作。为了减少重复代码,我们使用与在“数据收集”部分提到的相同逻辑来进行更新操作。不同之处在于,我们在收集数据时有意避开缓存,因为我们想尽量避免请求没有缓存过的数据。当我们更新索引时,我们将会命中缓存,因为我们希望该数据是最近被访问过,并且还在缓存中。
索引存储
post索引要比Facebook维护的其他搜索索引大得多。在开始搜索post之前,Facebook 所有的搜索索引都存储在RAM中。对于快速查询来说,这再好不过了。对小型的搜索索引来说也是可行的。然而,将700多TB的数据存储在RAM中所带来系统开销是难以想象的,因为它需要维护分布在多台机器上的索引。协调存储索引的多台机器使它们有序地工作给系统带来了巨大的性能损耗,Unicorn团队为此不得不寻找存储post索引的新方法。我们最终敲定的解决方案是用固态闪存存储大部分的索引,用RAM存储存取最为频繁的数据结构,性能得以维持。
结果排序
由于索引了1万亿条post,绝大多数查询返回的结果数量之多是任何人都读不完的。为此,Facebook开始设计对结果进行排序。为了使对用户有价值并与用户相关的内容浮到上面,主要采用了两种主要策略:查询重写和结果动态打分。在执行前,先重写查询,灵活增加子句,以确保查询结果对用户价值更大。为搜索结果进行打分,包括基于一系列用于排序的特征进行排序和选择文档。排序特征是从文档中抽取出来的,目前一共抽取了100多项特征,结合排序模型,用于寻找最佳搜索结果。随着用户量的增长和用户反馈的增多,排序模型必将得到进一步改善。
项目简史
像 Facebook 的很多其他产品一样,post搜索功能也是诞生于一个编程马拉松项目。而在过去的一年中,Graph Search 团队的几十个人实现了post搜索的大部分功能——基础架构、排序和产品化。