Airbnb是如何在两周内完成数据库切分的

【编者的话】
Airbnb是一家联系旅游人士和家有空房出租的房主的服务型网站,其总部位于美国旧金山。Airbnb可以为用户提供各式各样的住宿信息。2011年,Airbnb服务难以置信地增长了800%。用户数量的不断增加必将导致Airbnb用户数据量的激增,从而为数据的存储带来了巨大的压力。

数据量的猛增首先必然要求增加数据存储空间,同时还对数据存储的可扩展性和稳定性提出很高的要求。如何来解决这个问题呢?针对这个问题,Airbnb提出了一个非常有影响力的项目,旨在通过应用函数将特定的表格进行划分,存储在不同的数据库中。这通常需要做很多工程上的投资,主要涉及到应用层的改变、数据迁移和可靠性测试,目的是通过最少的停机时间确保数据的一致性。

具体来说,Airbnb又是如何实现的?Airbnb的工程师Willie Yao在其博客中分享了他们的具体工作,以及他们在这个过程中所学习到的一些东西。本文是一篇翻译稿,原文题目为“How We Partitioned Airbnb’s Main Database in Two Weeks”。

正文

“扩展等于给运行在100km/h的高速汽车更换所有部件。”

——Mike Krieger,Instagram的联合创始人@ Airbnb OpenAir 2015

Airbnb是如何在两周内完成数据库切分的_第1张图片

Airbnb住宿高峰以每年3.5倍的速度增长,而且都在夏季达到最高峰。

每当进入夏季,Airbnb都会迎来一个住宿的旺季,正如上图所示,而且住宿高峰还以每年3.5倍的速度在增长。开始进入2015年夏季旅游旺季的时候,Airbnb的基础架构团队就一直在努力扩展我们的数据库,以应对夏季高峰期高的访问流量。一个尤其有影响力的项目旨在通过应用函数将特定的表格进行划分,存储在不同的数据库中。这通常需要做很多工程上的投资,主要涉及到应用层的改变、数据迁移和可靠性测试,从而通过最少的停机时间确保数据的一致性。在整个项目的攻坚过程中,我们中一位非常优秀的工程师提出了一种非常有趣的想法,即利用MySQL复制来应对数据一致性中的最困难的那一部分。 (这个想法来自于亚马逊RDS的“Read Replica Promotion”功能的一个明确用例)在数据库升级过程中通过容忍一个短暂和有限的停机时间,我们能够执行此操作,而无需编写任何一行bookkeeping或migration代码。在这篇博客中,我们将分享我们的一些工作,以及在这个过程中所学习到的一些东西。

首先,一些背景

我们在Asana和Percona的朋友认为水平分片并不是很好的解决方法,我们倾向于同意他们的观点,所以我们选择通过应用函数进行垂直划分以传播负载和隔离故障。例如,我们有专门的数据库,每个都运行着他们各自的RDS实例,它们与我们独立的Java和Rails服务形成一对一的映射。但是由于历史原因,很多我们的核心应用数据仍然存储在原始的数据库中,因为早前Airbnb只是一个单一的单片Rails应用程序。

利用我们内置的客户端查询分析器来分析我们的数据库访问模式,我们发现Airbnb让客人与主机进行通信的消息收件箱功能,在我们的主数据库中占据了将近1/3的写入。此外,这种写入模式还会与流量线性增长,所以将其划分出来对于提高我们的主数据库的稳定性将是一个巨大的成功。因为它是一个独立的应用功能。同时,我们也有信心,所有跨表的连接和事物都可以被排除,所以我们优先开始了这个项目。

在研究这个项目的过程中,两个现实问题影响到了我们的决策。首先,我们最后一次划分数据库已经是三年前,所以在当前的规模下,继续采取这种操作对我们来说是一个新的挑战,我们付出了计划停机时间的代价,只为最大限度地减少工程的复杂性。其次,当我们进入2015年之后,公司大约有130名软件工程师,我们的团队在很多领域拓宽了自己的产品,从个性化搜索、客户服务工具、信任和安全、全球支付,到可靠的移动应用程序,这些工作占用了公司很大一部分工程力量,相反只留下很小部分工程用于基础架构的建设。考虑到这些因素,我们选择使用MySQL复制,以尽量减少工程的复杂性和需要的投资。

我们的计划

对我们来说,决定使用MySQL内置的复制功能进行数据迁移意味着我们不需要再单独打造最具挑战性的部分来确保数据的一致性,因为复制已经被证明是一种成熟的方式。我们在Amazon RDS上运行MySQL,因此创建新的读出副本以及转移一个副本到standalone主机上是容易的。我们的环境集成了下面这些部分:

Airbnb是如何在两周内完成数据库切分的_第2张图片

我们从我们的main master数据库中创建了一个新的副本(消息master),消息master在升级之后可以作为一个新的独立的主机工作。然后,我们连接一个第二层副本(消息副本,message-replica),其可以作为消息master的副本工作。美中不足的是,升级过程可能需要几分钟或更长的时间才能完成,在此期间,我们必须放弃对相关表格的写入操作,以保持数据的一致性。鉴于一个不堪重负的数据库带来的超长的停机时间会比一个本地化的、受控的消息收件箱的停机时间要长得多,我们的团队愿意作出妥协。值得一提的是,对于那些运行自己的数据库的人,复制过滤器能被用来避免复制不相关的表格,潜在地减少了升级的时间。

第一阶段:预规划

迁移消息收件箱中的表格到一个新的数据库中可能使使用跨表连接的现有查询在迁移之后失效。因为数据库升级后无法恢复。幸运的是,我们的内部查询分析器允许我们很容易地识别对大部分主要的服务的查询,我们能够撤销对剩余服务的相关数据库的权限授予,以获得完全的覆盖。在Airbnb工作的其中一个原则是服务应该拥有自己的数据,这将极大地简化了这里的工作。虽然在技术上简单明了,这却是工程中最耗时的一个阶段,因为它需要良好的跨团队沟通能力。

接下来,我们有一个非常广的数据通道能够提高离线数据的分析能力和下游产品的服务能力。所以预规划的下一步是迁移我们所有相关的通道来占用消息副本的数据出口,以确保升级之后使用的是最新的数据。这个迁移计划也存在一个不足,即使升级之后数据分开了,新的数据库与我们现有的数据库还是具有相同的名字(使用我们的RDS实例的名称并不会混淆,如message-master和message-replica))。然而,这实际上允许我们在数据通道中保持命名规则的一致,所以我们选择不对数据库进行重命名。

最后,因为我们主要的Airbnb Rails应用程序采用独占的方法对这些表格进行写入访问,我们能够交换所有相关的服务流量到新的消息数据库副本中,用来减少主操作的复杂性。

第二阶段:操作

一旦所有预规划工作已经完成,实际的操作按以下步骤执行:

  1. 与我们的客户服务团队沟通,计划收件箱中的停机时间在10分钟以内。对一个事实我们非常敏感,即当客户正试图check in到自己的Airbnb账号的时候,任何停机都可能让客人滞留在国外,所以让所有的相关功能一直运行,然后执行这些操作在访问并不繁忙的时候是非常重要的。

  2. 对消息收件箱中的查询变更为使用新的消息数据库用户授权和数据库连接进行部署。在这个阶段,在读出数据到消息副本的时候,我们仍然可以对main master进行写入,同时读取到的信息复制,所以这对外应该没有影响。然而,我们推迟这一步,直到操作开始,因为它是需要两倍于对主机的连接,所以我们希望这个阶段尽量简单。下一步,交换数据库主机不需要这个部署,因为我们在Zookeeper上有配置工具来更新数据库主机条目,在那里它们可以通过SmartStack被发现。

  3. 交换所有消息收件箱写入流量到消息主机。因为它还没有被升级,在新主机上的所有的写入操作都会失败,此时也开始对停机时间进行计时。然而读出请求是成功的,实际上几乎所有的消息传输在这个阶段都会慢下来,因为读出时标记一个消息需要一个数据库写入操作。

  4. 切断所有main master与消息数据库的数据库连接。不需要执行一个部署或进行集群重启,只需要直接切断连接,我们尽量减少所有写入副本的时间,这个副本将作为一个新的主机工作。

  5. 通过检查下面各项验证复制已经被赶上:

    • 在消息master和消息副本上所有消息收件箱中的表格的最新条目
    • main master上的所有消息连接都没有了
    • 消息master上的新连接都建立起来了
  6. 升级消息master。根据我们的经验,在RDS上进行升级的过程中,该数据库完全停下来大约需要30秒,在这段时间里,master上的读出操作失效。然而,写入将失效将近4分钟,因为在升级计时之前,需要花费大约3.5分钟,然后它才启动。

  7. 在下一次RDS自动备份窗口之前,在最新升级的消息master上启用Multi-AZ部署。除了提升对失效转移的支持,Multi-AZ最大程度地缩短了RDS快照和备份的延迟高峰。

  8. 一旦所有的指标看起来不错,以及数据库也稳定,丢弃各自的数据库中不相关的表格。这最后一步很重要,因为可以确保没有服务使用了陈旧的数据。

如若操作失败,我们将恢复Zookeeper上的数据库主机条目,消息收件箱功能也将被立即恢复。然而,任何写入的东西将丢失。理论上,恢复并回填丢失的消息是可能的,但是这将耗费很大的力气和并让用户感觉混乱。因此,在继续操作之前我们会非常仔细地检测了上述各步骤。

结果

Airbnb是如何在两周内完成数据库切分的_第3张图片

主数据库的主机写入明显下降。

端对端的部署,这个项目花了大约两个星期才完成,消息收件箱中的停机时间仅仅只有7分半钟,我们的主要数据库的大小减小了20%。最显著的是,通过将我们的主要的主机数据库上的写入查询降低33%,这个项目显著提高了数据库的稳定性。这些迁移的查询预计在未来数月内将另有50%的增长,这肯定会给我们的主数据库造成非常的负担,所以这个项目花了我们很多的宝贵时间,主要为了追求数据库长期的稳定性和可扩展性。

一个惊喜:RDS快照会显著提高延迟

按照RDS文档:

不同于Single-AZ部署,在为MySQL、Oracle和PostgreSQL引擎进行Multi-AZ 部署期间,I / O活动并不会暂停,因为备份处于待机状态。但是,请注意,您仍然可以在为Multi-AZ部署的备份过程中体验到几分钟的延迟增加。

我们通常启动RDS所有主机实例上的Multi-AZ部署,以充分利用RDS的高可用性和对失效转移的支持。在这个项目中,我们观察到,给予一个足够的数据库负载,即使使用Multi-AZ部署,一个RDS快照所经历的延迟可能足够给我们造成查询的大量积压,并搞垮我们的数据库。我们意识到快照会导致延迟增加,但在此之前的项目中,我们没有意识到,延迟的非线性增加导致完全停机的概率是与数据库负载相关的。

显然,RDS快照是RDS功能的核心,我们依赖于它每日进行自动备份。以前我们不知道的是,当我们的主数据库的负载增加时,RDS快照引起站点不稳定的可能性也随之增加。因此,在完成这个项目中,我们认识到,项目比我们原先预计的更为迫切。

你可能感兴趣的:(Airbnb是如何在两周内完成数据库切分的)