从AWS迁移到Facebook基础架构

机构:郑州北大青鸟 时间:2015-12-05 点击:602

  Mike Krieger, Instagram的联合创始人和CTO,近期写了一篇文章,文章中提到了一个故事,大约在2012年的时候,弗吉尼亚州的一场飓风瘫痪了将近一半的(服务器)实例。

  在接下来的36小时里,这个小团队重建了几乎我们全部的基础架构,这种体验是他们永远不想重复的。

  像这样的自然灾害有可能对数据中心造成临时的和永久的伤害——我们需要保证在用户体验上有最小的损失。

  其他的在地理上扩容的动机包括:

  区域故障的恢复: 比自然灾害更加常见的是网络短线、电力问题,等等。例如在我们扩展我们的服务到俄勒冈州不久,我们的一个基础构件,包括memecache和异步层服务器,被关机了,导致了用户请求的大规模一场。

  在我们的新架构下,我们能够将流量从该区域转移走,以减轻我们在从电力故障中恢复时的问题。

  弹性容量扩展: Facebook有不少数据中心。当我们的基础架构准备好扩展到一个区域甚至当网络上有不小的延迟时,可以非常容易的将Instagram的容量扩展到所有可用的容量中。这帮助我们快速决定为用户准备好新的功能而不用Scramble for基础架构资源来支持他们。

  从一到二

  所以我们怎么开始这件事情的? 首先让我们来看一下Instagram的整体基础架构栈。

  扩展到多数据中心的关键是区分全局数据和局部数据。全局数据需要在不同的数据中心间复制,而局部数据在每个区域可能不同(例如web服务器创建的异步任务应该只在所在的区域被看到)。

  下一个要考虑的是硬件资源。这个可以粗略的氛围三中:存储,计算和缓存。

  存储

  Instagram主要是用两种后端数据库系统:PostgreSQL和Cassandra。他们都有成熟的复制框架来很好的作为全局的一致数据存储。

  全局数据整齐地映射到这些服务器上存储的数据。目标是在不同的数据中心间保持这些数据的最终一致性,每一个区域有一个读复制,来避免web服务器的跨数据中心读。

  但是,对PostgreSQL的写入仍然夸数据中心,因为他们总是要写到主服务集群上。

  CPU处理

  Web服务器,异步服务器都是无状态的容易分布的计算资源,并且只需要访问本地数据。Web服务器可以创建异步工作,这些异步工作被异步消息代理加入队列,然后被异步服务器消费,全都在一个区域。

  冗余计数器

  最常见的缓存键是计数器。例如,我们使用一个计数器来确定喜欢Justin Bieber的一个具体的帖子的人数。

  当只有一个区域时,我们可以从web服务器增加memcache的计数器,所以避免一个“select count(*)”的数据库调用,这回节省几百毫秒。

  但是在有两个区域和PgQ失效时,每一个新的喜欢对计数器创建了一个缓存失效事件。这会创建大量的“select count(*)”,尤其是在热点对象上。

  为了减少这些操作每一个需要的资源,我们对这个帖子的喜欢数量的计数器进行冗余(译注:即在post的字段中加上likes的计数器,虽然是反范式的但带来了性能提升)。当一个新的喜欢来到时,这个计数在数据库中增加,因此,每个对这个计数的读会变成一个更有效的简单的select。

  另一个在存储喜欢这个帖子的人的同一个数据库中进行冗余计数的好处是,更新可以被包含在一个事务中,似的这个更新总是原子的和一致的。虽然在改变前,缓存的计数器可能和数据库中存储的不一致,因为超时或重试等等原因。

  Memcache租约

  在上面来自Justin Bieber的新的帖子的例子中,在这个帖子的最初的几分钟,浏览和点赞的都会达到峰值。对每一个赞,计数器都从缓存中删去。非常常见的情况是web服务器都尝试从缓存中获取同一个

  计数器,但是会有“缓存未命中”发生。如果所有的这些web服务器都去数据库服务器来获取数据,将会导致惊群问题。

  我们使用memcache租约机制来解决这个问题。它像这样工作:

  Web服务器发起一个“租约get”请求,不是通常的“get”请求到memcache服务器。

  Memcache服务器在命中时返回命中的缓存值。在这种情况下和一个通常的“get”请求没有区别。

  如果memcache服务器找不到对应的key,它在n秒内返回一个“首次未命中”给这段时间内请求的一个web服务器;这段时间内任何其他的“租约get”请求会得到一个“热未命中”。在“热未命中”的情况下,这个key最近从cache中删除,它会返回过期的值。如果这个缓存的key在n秒内没有被挺冲,它再次对一个“租约get”请求返回“首次未命中”。

  当一个web server收到“首次未命中”时,它进到数据库中获取数据并且填充缓存。

  当一个web server收到“热未命中”和一个过期的值时,它可以使用这个值。如果它收到一个没有值的“热未命中”,它可以选择等待缓存被“首次未命中”的web server填充。

  总之,在以上的实现中,我们可以通过减少访问数据库的次数和每次访问的资源来减少数据库的负载。

  这也提高了我们后端在一些热计数器调出缓存时的可靠性,这种情形在Instagram的早期并非不常见。每次这种情形发生都会使得工程师赶忙手动修复缓存。在这样的改变下,这些事故成为了老工程师的回忆。

  更多知识:郑州网络工程师培训学校整理。

返回顶部