微信号:ArchNotes

介绍:高可用架构公众号.

Airbnb个性化搜索服务架构

2018-08-21 10:21 Charles He等

导语:业务快速增长给搜索带来什么样的挑战?针对类似场景如何设计通用的平台?本文详细讲述Airbnb大型搜索服务的演进之路。


去年,Airbnb到了需要可扩展、分布式存储系统的时候了。例如,搜索个性化数据超过了单机的承载能力。当我们提升了个性化服务的纵向扩展能力的时候,意识到其他服务也有同样的需求,因此决定设计一个通用平台,简化其他服务需要做的事情。


除了通常的请求/响应模式,其他服务有不同的需求,例如,从数据源(例如,MySQL数据库)做周期性批量同步,引入新的数据源(例如,新的搜索特性),从数据流中消费增量更新(我们的场景中是Kafka),或者提供数据分析能力,并且为网站流量提供低延迟的数据服务。随着公司的持续增长,我们有很多应用积累了越来越多的数据。如果我们能挖掘出有用的信息并且反馈给应用,那么这些数据可以为我们的产品提供巨大价值。

摘要

让我们从个性化搜索开始。这需要保留我们的用户行为历史。要求能记录实时用户行为,并且能立即获得记录,以优化个性化搜索结果(并且改善其他产品)。提供其他应用能用的数据快照(例如分析或者验证)。需要周期性的聚合并且截断历史数据,批量导入一批特征(离线计算)到系统中。

这些需求贯穿公司很多应用。因此我们决定设计一个通用存储平台,以支持这些需求,并帮助其他服务负责人聚焦他们特殊的业务逻辑。我们计划在这个系统中满足下面这些需求:

  1. 为网站流量提供低延迟操作(毫秒级别)

  2. 实时数据流提供增量更新

  3. 便捷高效的数据批量操作

  4. 保证公司增长的数据和流量可扩展

  5. 维护成本小


注意,我们将“批量操作”定义为快照和压缩完整存储库的操作,用新快照替换现有快照以进行服务,将完整的新信号合并到存储库中,用新集合替换现有信号数据以及完整数据集上的任何其他操作Nebula是一个平台,我们这个存储上满足了所有的这些需求。

什么是Nebula?

Nebula是一个无模式版本化的数据存储服务,提供实时随机数据访问和离线批量数据管理。它包含一个支持增量数据(最近一段时间的更新)的动态数据存储的独立服务,和一个支持批量操作的静态数据存储的快照数据存储。我们选择DynamoDB作为动态数据存储(主要原因是它有很低的读延迟,使用AWS的维护成本低),和HFileService(Airbnb内部使用的可扩展的静态存储,支持分区和本地硬盘到HFile格式的预处理)存储静态的快照。

随机数据访问的抽象存储

Nebula为底层物理存储提供了统一的API。API为应用提供了通用K-V存储API,增量和静态数据内部合并,这样,应用不需要为实时数据和批量数据分别部署。所以,它能灵活的迁移到不同的物理存储,上层应用不需要修改API。

Nebula使用版本化列式存储,类似于BigTable和HBase。版本化列式存储比起原始K-V存储,能让服务负责人更便捷地定义他们的数据模型。必要的时候,版本能解决冲突和跟踪数据变更时间。应用每行和列能存多少个版本没有任何限制。

Nebula支持<行,列,版本>级别的原子操作。并发写同样的<行,列>会有不同的版本,这样数据能合理的存储。每列都有自己的版本,并且所有的写直接追加到各自的列上(通过版本存储)。用户随机访问需要通过给定<行,列>获取一个或者多个版本的数据。获取多列或者多行的多次请求可以合并到一个单独的多请求中。

使用个性化数据的一个例子,数据模型如下:

每行表示一个用户的数据,每列表示一个用户交互类型(又叫用户事件),比如前面提到的搜索,每个版本是用户事件发生时候的时间戳。在产品中,我们有很多用户事件,每个事件列积累了大量的不同时间戳的事件。

为了支持一个搜索请求,搜索服务查找给定用户对应的事件数据,用这些个性化数据在排序模型中决定向用户展示的有序列表。因为这是搜索请求路径,所以我们有很严格的延迟和可用性要求。

数据能通过增量数据流(包含个人用户事件)以单元格为单位和离线管道批量(压缩历史数据或者按列引导/合并/替换数据)的方式被更新。

内置批量数据处理

Nebula使用离线管道为每个仓库的增量数据做快照,与前面的快照合并,然后使用新的快照提供服务。这些任务跟在线服务分开执行,对网站流量的影响非常小。

管道可以根据要求进行高级配置。例如,每个应用可以定义他们自己的策略,如何合并新老数据(例如,新数据覆盖老数据,使用版本聚合,丢弃老数据等等),如何压缩历史数据(保留N个版本,删除某段时间之前的数据等等),如何调度管道,等等。

Nebula给用户提供定义良好的接口,用于他们定制数据并且自动加载到系统中。用户可以将他们的数据放在公共的地方,修改一些Nebula配置,然后管道将选择并且合并数据到系统中以供使用。

应用负责人能在数据快照上,将他们的特殊需求定制的逻辑放到管道上。他们的逻辑将在最新的快照数据上执行,所以,这是一个处理逻辑的有效方式。

在个性化搜索场景中,我们在下列情况下使用管道:

  1. 定期生成快照,合并增量数据和静态数据

  2. 按列进行压缩和过滤过期事件,保证数据大小可控

  3. 离线特征计算以创建新的特征,批量导入新特征到存储中

  4. 定制验证用户事件的逻辑,合理的状态检查


所有的个性化数据都版本化,不管什么时候发现数据问题,Nebula可以回滚到以前的好快照,并在版本(时间戳)之前丢弃任何不良数据。回滚逻辑根据应用决定,但是Nebula的批量接口让回滚逻辑实现很简单。

架构

这是整个系统的架构(如下)和设计选型。

一个Nebula读请求查询两个数据源。增量数据存储仅仅包含最新的数据,快照存储包含完整的数据。两个存储都支持读查询,但是只有动态存储接收写请求。快照存储的更新通过切换底层快照。数据存储通过Zookeeper协作。

动态数据存储DynamoDB

我们选择DynamoDB是因为低延迟的要求,但是也可以根据其他要求使用其他的物理存储(例如HBase)替换。作为Nebula的底层存储,物理存储只需要支持主键和排序二级索引。尽管底层实现不同上层接口却是一致的,对于系统上的任何应用(和用户)来说,替换物理存储是透明的。

我们不准备去设计另外的物理存储,使用DynamoDB作为底层的存储能让我们非常快速的组建一个系统。

数据输入流被写入动态存储中;它允许随机更新,并且支持很高的QPS。DynamoDB的读延迟很低,所以能很好的满足我们平均10毫秒的延迟要求。我们做了一个优化,为了保证DynamoDB表的大小容易管理,每天将数据分区到新的表。所以,我们的每个表仅仅占据一部分DynamoDB的分区以保证有高的QPS。

批量数据存储HFileService

Nebula根据动态更新合并起来的实时查看的最新快照存储在HFileService集群中。

HFileService以低延迟高吞吐量从本地磁盘中提供静态的HFiles(快照格式)。而且,数据加载过程对读请求几乎没有影响,所以离线数据合并操作不影响对数据的实时访问。

HFileService通过动态分片机制对数据分区,所以水平扩展能力依赖数据的总大小。尽管是静态数据,复制策略非常简单并且能随着流量的增长去调整。

使用离线管道做快照、压缩和定制逻辑

Nebula支持在线随机数据访问和批量操作。批量操作不影响在线访问。下图描述Nebula的离线架构:

定期从增量存储导出批量更新数据到分布式文件系统(Amazon S3)。数据导出之后,启动一个离线Spark任务将批量更新和历史数据合并。我们经常有其他的离线产生数据的情况,例如机器学习特征,需要批量上传到系统中。合并阶段经常有这样的情况,新快照通过合并批量更新、历史数据和定制离线数据进行创建。我们在合并过程中添加合理的检查,避免坏数据进入到我们的系统。

最新的快照存在S3上,等待下一轮合并。它也会被保存到我们的历史数据存储中。贯穿整个导出-合并-加载过程,实时存储一直保留这些导出到S3的增量数据,直到新的快照生成成功并且保存到历史数据存储中才会删除。这保证了读请求总通过实时和历史存储能获取到完整数据。

S3上的完整快照被用于其他的离线数据分析。

流式更新输出

除了对快照的随机访问和批量处理,Nebula还支持流式更新,以保证应用及时感知数据的更新。通过DynamoDB的流API来支持流式更新。一个单独的组件使用Kinesis消费者中的流,并将其发布到特定的Kafka流中,因此任何感兴趣的服务都可以订阅它。

其他场景:搜索索引基础设施

说完了Nebula,接下来讲讲我们如何使用Nebula重构Airbnb的搜索索引。我们先聊一下为何要重构。

由于Airbnb大部分使用Rails / MySQL作为前端,因此搜索索引会监听(并且仍然)对数据库表进行更改,维护当前搜索索引文档的缓存,并使用新文档更新搜索实例(如果有任何更改)。由于使用轮询加载器加载,以及从数据一致性的数据源定期同步,因此性能不确定。新的搜索机器可以通过从缓存中缓慢流式传输来引导其索引。

下面是我们决定使用这个系统的原因:

  1. 端到端的低延迟操作(平均时间小于1秒)

  2. 能够通过批量任务离线处理并且合并消费的特征到索引中

  3. 能够使用实时特征

  4. 离线生成索引(能够共享索引到离线分片)

  5. 快速回滚有问题的分片

  6. 快速扩展新的搜索实例

  7. 审核搜索索引文档的更新

  8. 索引数据增长的时候可扩展


Nebula系统上面的这些特性很完美的解决了我们所有的需求。版本化列式存储意味着我们能审核搜索文档,支持批量任务意味着我们能离线生成索引(以及合并列表特征)并且直接部署到搜索中。因为这些索引基于快照构建和部署,出现坏的索引数据我们能快速的回滚。新生成的索引被用于新的搜索实例快速启动(仅仅通过下载索引)。

上图展示了基于Nebula的搜索架构。数据快照作为离线数据合并的一部分每天生成。索引构建器的作业对此快照进行操作以构建分片索引,然后像普通的二进制部署一样定期部署搜索。这个系统使用了Nebula的特性,只需要实现定制逻辑关联搜索索引。

展望

我们在Nebula之上构建了很多服务,包括刚刚提到的搜索索引管道,个性化基础设施,Airbnb的价格服务数据仓库。为每个应用提供了很多TB的数据,平均延迟在10毫秒。我们想鼓励其他的团队使用Nebula构建更多的应用。

我们也计划把我们的系统跟我们的数仓深度结合,即,存储历史快照到Hive,共享更多数据流消费逻辑,等等。为分析功能提高数据的可用性和一致性,让系统管理和操作更便捷,这样对开发者来说才能更容易构建他们的应用。

鸣谢

很多人的付出才把这个系统做起来。我们想感谢Alex Guziel为这个项目所做的突出贡献,感谢Jun He, Liyin Tang, Jingwei Lu等人的慷慨相助,还有很多人通过搜索,应用基础设施,数据基础设施,产品基础设施和其他团队所有以各种方式帮助的人。


原文地址:

https://medium.com/airbnb-engineering/nebula-as-a-storage-platform-to-build-airbnbs-search-backends-ecc577b05f06

相关阅读:


本文作者 Charles He, Soumyadip Banerjee, Tao Tao, Krishna Puttaswamy,邓启明翻译。转载本文请注明出处,欢迎更多小伙伴加入翻译及投稿文章的行列,详情请戳公众号菜单「联系我们」。

高可用架构

改变互联网的构建方式

长按二维码 关注「高可用架构」公众号




 
高可用架构 更多文章 深度 | 从Go高性能日志库zap看如何实现高性能Go组件 Cloudflare Nginx优化成果:每天为互联网节约54年 重磅!GitHub发布开源负载均衡组件GLB Redis深度历险——核心原理与应用实践 如何在短时间内成为DevOps专家?
猜您喜欢 如何编写一个独立的 PHP 扩展 【连载干货】中国人民大学统计数据挖掘中心专题报告资料之回归分析与Lasso 微博平台分布式存储系列--Kick Off FEX 技术周刊 - 2018\/05\/07 腾讯WeTest第一届移动沙龙参会感