[译]我们是如何分割Airbnb运行中的数据库的

来源:互联网 发布:我是淘宝黑名单买家 编辑:程序博客网 时间:2024/06/05 18:58

原文链接:How We Partitioned Airbnbs Main Database in Two Weeks


译者:杰微刊-macsokolot(@gmail.com)


“Scaling=每小时100英里的速度行驶时,替换掉这辆车的所有部件”


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

Airbnb,MySQL,Amazon,RDS

Airbnb 流量峰值以每年 3.5倍的速率增长, 呈现出夏季季节性高峰趋势.


即将进入2015年夏季旅游旺季,Airbnb 基础设施团队正努力分割我们的数据库来应对今年夏天如期而至的流量高峰。这个极其重要的项目的目标,是在数据库上使用应用函数来分割表。应用程序层的更改,数据的迁移,在最少宕机时间下保证数据一致性的鲁棒性测试,这显然需要一笔重大的工程投资。


为了尽可能的缩减工程时间,我们其中一位才华横溢的工程师提出了一个新奇的点子--利用 MySQL 复制功能来处理棘手的部分:保证数据一致性。(这种想法比较特立独行,在亚马逊 RDS的“读取复制副本提升(Read Replica Promotion)”功能有使用过这种方法)。通过在数据库升级过程中忍受短暂和有限的停机时间,我们得以执行此操作,而无需编写一行保存或迁移的代码。在这篇博客文章中,我们将分享一些我们做的工作和我们在这一过程中学到了什么。


Airbnb,MySQL,Amazon,RDS


夏季来了


首先,一些背景


我们比较同意我们的朋友在Asana和Percona指出的水平分片(horizontal sharding)不好的说法,所以我们更喜欢将应用功能垂直分区来分散负载和隔离故障。例如,我们有专门的数据库,每个运行其自己专用的RDS实例,一对一地映射到我们独立的Java和铁路服务上。然而由于历史原因,我们的很多核心应用程序数据仍然是在从原始数据库中获取的,那时Airbnb是一个简单纯铁路的应用。


使用客户端内建的查询事件探查器(由于RDS的限制只能在客户端实现) 来分析我们的数据库访问模式,我们发现,Airbnb 的收件箱功能——允许客人和主人进行沟通交流——在我们的主数据库上占近 1/3 的写操作。此外,这种写模式随着流量增长呈线性,所以将这部分功能从主数据库剥离出来,将对我们的主数据库稳定性提高是非常有利的。因为它是一个独立的应用程序功能,我们也非常自信可以消除所有的交叉表联接和事务的问题,所以我们优先考虑这一块。


在研究这一块的处理方法时,两个现实问题影响到了我们的决策。首先,我们最后一次分割数据库是三年前即2012 年,所以在当前大小的数据库上继续此操作是一个新的挑战,对我们来说,我们必须在有限计划的停机时间内尽量减少工程的复杂性。其次,2015年我们大约 130 名软件工程师,我们的团队分布于各个领域产品之中——从个性化的搜索,客户服务工具,信任和安全,全球支付、 限制连接的安全可移动应用——只留下一小部分人在这个数据基础工程之上。考虑到这些因素,我们选择使用 MySQL 复制功能以尽量减少工程的复杂性及所需的投资。


我们的方案


当决定使用MySQL自带的复制功能对数据进行迁移,意味着我们不再需要开发一个颇具挑战性的程序来保证数据的一致性,因为MySQL里的复制功能是公认的保质保量。我们在Amazon RDS上运行 MySQL,创建新副本并使这个副本独立于源,这非常容易做到。我们的设置如下:


Airbnb,MySQL,Amazon,RDS


我们从主数据库创建了一个新的副本(主消息数据),这个新副本在升级处理之后将作为独立的主数据库。然后,我们连接一个二级副本(消息副本),将作为消息主数据库。这里的重点是,整个升级过程中可能需要几分钟或更长的时间来完成,而在此期间,我们必须有意不写相互关联的数据表,这样以保持数据的一致性。因为一个网站若因数据库不堪重负而导致关停,这个时间成本是比暂时关停一个本地化且可控的消息收件箱功能的代价更高,因此小组成员必须做出这样的权衡:缩短升级的开发时间。值得一提的是,对于那些运行自己的数据库的人来说,复制过滤器可以用来避免复制不相关的表,并有可能减少升级所需的时间。


第一阶段:规划


将消息收件箱功能所涉及到的表移动到一个新的数据库,在迁移后会出现现有的跨表联接查询无效。由于数据库升级是不可逆的,这次操作成功取决于我们能否识别出所有这些情况,选择略过或用内应用(in-app)连接方式去取代它们。幸运的是,我们的内部查询分析器允许我们很容易地在主服务中确定这些查询情况,我们在剩下的服务中撤销相关的数据库权限,以获得整个的覆盖。我们在Airbnb的重构原则之一是:每个服务应该拥有属于自己的数据,这样能大大简化这个服务的工作复杂性。虽然从技术上讲,这是整个项目最耗时的部分,因为它需要沟通顺畅,来自不同团队成员间的共同努力。


接下来,我们需要一个可拓展的数据管道,这个管道既能对离线数据进行分析又能向下提供数据服务。所以计划的下一步是将我们的所有相关管道去处理从消息副本(message-replica)导出的数据,并确保这些数据在升级后是最新的。这种数据迁移方案的一个好处是新的数据库和现有的数据库名称是一样的(不要与我们的RDS实例名称混淆,如消息主数据库message-master和消息副本数据库message-replica)尽管数据在升级后是分离的,但这让我们能够在数据管道中保留原来的命名规范,因此我们并没有选择给数据库重命名。


最后,因为我们AirbnbRails应用独立的对这些表进行写操作,我们就可以切换所有相关的服务流量到新的消息数据库中去,以减少主数据库访问的复杂性。


第二阶段:执行


Airbnb,MySQL,Amazon,RDS


Production Infrastructure团队成员的重要日子


规划工作做完之后,实际的操作如下:


1. 与我们的客户服务团队沟通10分钟暂停消息收件功能的计划。我们很清楚,暂停任一功能会让滞留在异国他乡的游客不停的刷新他的Airbnb,因此让所有相关的功能能够正常运行非常重要,而我们选择在一周流量最低峰的时候执行升级操作。


2. 让消息接收功能使用新消息数据库中的用户授权和数据连接服务。在这个阶段,我们仍然让写操作在主数据中进行,而读操作在数据副本中进行,这样就不会有外在的影响。但我们推迟了这一步直到升级操作开始,因为这样会使主数据的连接加倍,所以我们想让这个阶段的时间尽可能的短。接下来就是切换数据库地址,这不需要部署,因为我们在配置工具Zookeeper中更新数据库地址,当然你也可以用SmartStack.


3. 切换所有的信息收发写操作流量到主消息数据库。因为它还并没有升级完全,所有在新数据库中进行写操作会失败,因此我们开始计时暂停服务时间。当所有读操作都成功后,这意味着这段时间内所有的消息都已经在新数据库中记录好了,因为将所有这些消息标记成已读状态需要执行数据的写操作。


4. 杀掉在步骤二中所有消息数据用户与主数据库的连接。由于是直接杀掉连接,这会导致重连或者集群重启。我们极力缩短将写操作重定向新的主数据库时间,这是将从数据库转换成主数据库的先决条件。


5. 可以通过以检测这几个方面来得知这个副本真正有效:


① 消息收发数据表的新入口在主信息数据库和它的副本上。


② 所有消息收发连接在主数据库中荡然无存。


③ 新的消息连接能够连上主消息数据库


6. 升级消息主数据库。以我们的经验,在RDS中升级过程中,数据库完全宕掉持续了30s,在这30s中在主数据中进行读操作是全部失败的。但是,写操作持续了将近4分钟,因为在数据库初始化后,整个升级真正起效的时间花了将近3分30秒。


7. 在下一次的RDS自动备份窗口期间之前,新升级的消息主数据库上启用Multi-AZ部署方式,这是为了提升故障切换支持,Multi-AZ最大程度地缩短了RDS快照和备份延迟高峰。


8. 一旦所有指标看上去非常好并且数据库也很稳定,这时候就可以在各自的数据库中删掉一些不相关的数据表。做好最后的这一步是很有必要的,因为这能确保各个服务消费各自产生的数据。


如果操作失败了,我们将用Zookeeper去恢复数据库的主机地址。并且将消息收发功能即刻恢复到原来的状态。然而,我们将丢失任何在独立消息数据库中的任何写入数据。从理论上讲,这可以通过回填方式来恢复数据,但从用户角度上来说这将是一种非常糟糕的处理方式。因此,我们卖力地测试各个环节,直到真正开始进行升级操作。


结果


Airbnb,MySQL,Amazon,RDS


在主数据中写入操作明显下降


从开始到结束,这个项目花费了2周时间,其中使消息收发功能暂时关停了7分半钟,并是我们的主数据库大小缩减了20%。最重要的是,这个升级项目让我们的数据库的稳定性有了显著的提升,因为其减少了主数据33%的写入操作。这些过载的查询操作在未来的数月中将新增50%,这将对于我们现有的主数据库将是难堪重负的,因此此次升级操作让我们在宝贵有限的时间内,完成了对数据库长期稳定性和可拓展性的投资。


意外:RDS快照显著提高了响应时间


根据RDS官方文档:


不同于单AZ部署,当你用MySQL,Oracle或PostgreSQL引擎使用AZ部署进行备份的时候,你的主数据库中I/O活动不会被中断。因为这个备份是从持续活动的数据库中拿数据的。然而,请注意,在你使用多AZ部署备份的时候,你仍将承受几分钟的读写延迟。


我们一般RDS所有主实例中启用多AZ部署以充分利用RDS的高可用性和故障转移支持。在这个项目中,我们注意到,给定一个足够大的数据库负载,在RDS快照甚至是使用了多AZ部署中都将遇到读写延迟,这将显著产生巨大的查询条目的积压,最终导致我们的数据库升级失败。的确,我们知道这种快照会导致读写延迟,但是在此之前,我们却忽视了这样一个问题:数据库宕机的可能性在读写延迟中是非线性增加的,相较于数据库负载。


鉴于RDS快照是一个RDS的核心功能,这是我们依赖它每天进行自动备份的重要原因。以前我们不知道的是,随着我们的数据库的负载增长,这种RDS快照也会引起数据库的不稳定性。因此,对于这个项目,它已经比我们原先预计的更为棘手。


Airbnb,MySQL,Amazon,RDS


Xinyao, 整个项目的leader,为项目完成庆功.


致谢:Xinyao Hu在Ben Hughes和Sonic Wang的指导下,我完成了这个项目的初步一个方案,随后Xinyao Hu就主导了整个项目。Brian Morearty 和 Eric Levine重构代码以消除交叉表连接问题。Production Infrastructure团队在完成整个升级项目之后度过了一个愉快的午后时光。


0 0
原创粉丝点击