如何使用多数据中心提供数据

原帖:http://highscalability.com/blog/2009/8/24/how-google-serves-data-from-multiple-datacenters.html

觉得这篇文章很好,尝试翻译了一下,翻得很烂,大家随便看看。

数据同步方案设计

  为了避免数据中心成为单点错误节点,当一个数据中心出了严重问题的时候,能很快恢复提供正常服务,双数据中心或者多数据中心是显而易见的解决思路。这种模式一般称为 multihoming (多宿主)。多数据中心有下面一些模式:

1.         Backup

2.         Master-Slave

3.         Multi-Master

4.         2PC ( 两阶段提交 )

5.         Paxos

 

我们将由下面几个方面来分析这些模式之间的差异:

  • 一致性
  • 事务
  • 延迟
  • 吞吐量
  • 数据丢失
  • 故障转移

 

 

Google App Engine 采取是多数据中心间的 Master-slave replication 。他们采取这种模式,主要是为了达成以下的目标:

  • 最低的写入延迟
  • 数据中心灾难生存能力
  • 强一致性保证

 

 

没有一个方案能够面面俱到,折中不可避免,你需要根据你认为最重要的需求来选择合适的方案。

 

仍然有很多概念需要理解,下面我们需要一个一个的来讨论:

 

一致性 当你写入一个值以后尝试读取那个值会怎么样?

在多数据中心上读写数据是最困难的问题之一。用户会期待某种程度的可靠性和一致性。

  • Weak (弱一致性) --- 尽量使数据一致,但是数据可能会一致,可能会不一致。就像 MemoryCached 做的那样。如果应用类型对数据一致性要求并不高的话,比如 VOIP 应用, RTP 应用,在线视屏,多人网络游戏等,这个程度的一致性就已足够。

 

 

  • Eventual( 最终一致性 )- -- 你不会马上就能看到你写入的东西,但最终会看到你写入的东西。 Email 就是一个很好的例子,人家发给你的邮件你不会马上就能收到,但过会儿你终将会收到。 DNS 改动传播, SMTP Amazon S3, SimpleDB, 搜索引擎建立索引都是这类。这类一致性对数据存储还不是很理想。

 

 

  • Strong (强一致性) --- 这个级别的一致性非常适合存储结构化的数据。你能马上读到你刚写入的数据。这也能减低程序开发的难度。 Google App Engine, 文件系统, Microsoft Azure, 大多数关系型数据库都以这种方式工作。

 

 

当我们跨越多个数据中心读写数据时,应该提供哪个级别的一致性保证呢?我们可以放弃一些保证,但是我们应当知道在放弃这些保证的同时我们得到了什么。

 

Transactions (事务)一种扩展 形式的一致性,特指在一个行为包含多步操作时的一致性

 

事务的四种属性:正确性,一致性,有效性,原子性

但你一个行为跨越多个数据中心时,你要保证你行为支持事务是很困难的,因为太多的地方可能出错,而且延迟会大大提高。

 

为什么我们需要多数据中心呢?

  • 一是以为数据中心可能会以多种原因出错而不可用

 

  • 性能 --- 我们可以让用户的操作发生在地理上更接近他的数据中心上。光的速度限制着数据传输的速度,一旦一个用户的操作跨越世界时,让数据中心接近用户的效果就会显著出来了。数据传输时经过太多的路由器也会增加延迟。让用户的操作路由到地理上更接近他的数据中心上无疑是种好的做法。 CDN (内容分发网络)就是这么干的,特别在处理静态数据的时候。它们把数据放得到处都是。

 

 

为什么我们会选择单数据中心呢?

  • 在单数据中心上操作更简单:更低的带宽消耗,更低的延迟,更容易操作,更容易编码。

 

  • 在多数据中心上操作更困难:更高的成本,更高的延迟,操作更困难,编码更困难

 

  • 特别困难情形是你读写的是结构化的数据,而且你允许在多个点写入。这会带来数据一致性的问题。在面对距离和错误的同时,保持数据的一致性并非易事。

 

 

你不同的架构选择

  • Single Datacenter( 单数据中心 ) 。不再为在多数据中心上操作烦神。这是最简单的选项,大多数人都是这么干的。但是如果你的数据中心挂了,你会丢失数据,而且你的网站也不能提供服务了。

 

  • Bunkerize( 单数据中心强化 ) 。为你终极的数据中心加固一条马奇诺防线。保证你的数据中心无论如何也不会挂掉。 SimpleDB Azure 就是使用这种策略。

 

  • Single Master (单主节点) 。选择一个数据中心为主节点,主节点支持写操作,别的节点(从节点)会复制主节点的数据。从节点仅仅支持读操作。

 

  1. 看起来不错,但不是伟大的
  2. 数据复制是异步的,所以仍然有数据遗失的窗口期(数据复制没完成, master 挂掉)。
  3. 在出错时,从数据中心可能与 master 数据中心并不一致。
  4. 这种方式在金融机构比较流行
  5. 可以根据数据中心的地理位置选择比较近的读取。数据中心的一致性取决于实现的技术。写操作还是限定在 master 数据中心上。

 

  • Multi-Master( 多主数据中心 ) 。真正的多数据中心,圣杯级的。所有的数据中心都同时支持读 / 写操作。事务真正需要起效。这个真的非常难。

 

 

  1. 有人仅仅在两个数据中心上实现这种模式。纳斯达克有两个位置非常接近的数据中心(低延迟),所有的事务都会以 2 阶段提交的方式执行,而且它需要很低的延迟。
  2. 这个模式在超过 2 个数据中心时是非常难以实现的。因为你将为排队延迟,路由延迟,光速(电磁波的速度)付出代价。你不得不在多个数据中心间对话。你会提高你的数据处理能力和吞吐量,但是你毫无疑问地会在延迟上付出代价。

 

你要选择如何做呢?

下面是这些方案的权衡比较:

 

Backup

Master/slave

Multi-Master

2PC

Paxos

一致性

最终一致性

最终一致性

强一致性

强一致性

事务

支持

本地支持(限于一个数据中心内)

支持

支持

延迟

吞吐量

中等

数据丢失

一些

一些

故障转移

不能

只读

/

/

/

 

选择一个特定的方案我们将得到何种类型的一致性,事务支持,延迟,吞吐量?是否会在出错的时候丢失数据?会丢失多少?我们何时可以故障转移,比如子在维护或者移动东西的时候,或者让一个数据中心废弃?我们怎么可以做到故障转移?这个方案是如何支持故障转移的?

 

  • Backup - 给你的数据制作一份拷贝,这种做法是隐秘和安全的。一般来说它只能提供弱一致性。通常不能提供事务支持。这种方案可以用来为系统第一次内部运行时数据备份,生产环境你最好别这么干。一旦出错它会丢失你自上次备份以来的新数据。而且你不得不停止你的服务直到你在另外一个数据中心上使用备份复原好数据。

 

 

  • Master/Slave 复制

 

数据写入 Master 导致数据也会被复制到一个或者多个 Slave

由于复制是异步的,对降低写延迟和提高吞吐量有利。

除非你非常小心,这种方案只能提高弱一致性或者最终一致性。

由于你的数据在多个数据中心中有多份拷贝,所以当出错时,你会丢失一些数据,但不会太多。故障转移仅仅支持读操作直到你把 Master 角色转移到另一个数据中心上。

Datastore 当前就是使用了这种机制。真正的多数据中心 (Multi-Master ) 会增加延迟,因为你不得不在各个数据中心间增加一些额外的通讯消耗。 App Engine 已经在写操作上比较慢了,所以这种额外的负担更显得痛苦。 Master/Slave 模式会让你得到大多数的好处的同时,还会让写操作延迟较低。

 

  • Multi-Master 复制

 

在多个数据中心上同时支持写操作。

你必须设计好如何在写入数据冲突的时候合并结果。这种做法跟异步复制很相像,但是你支持多点写入。你最好选择实现最终一致性。写入数据不会立刻影响到所有的数据中心。但这样的话又貌似回到了原点。我们追求 Backup Master/Slave 所不能提供的强一致性。这种系统运行有了显而易见的变化,因为我们必须考虑写入合并的问题。

为了合并你必须找一个一种方法在写入操作上加入时序性。这里没有全局时钟,而且写操作是并行发生的。你甚至不知道那个动作是最先发生的。因此你需要使用时间戳,本地的时间戳 + 偏斜,本地的版本号,分布式的共识协议( chubby zookeeper )等手段。

这种方案无法支持全局事务。因为在多点同时写的情况下,你是无法保证事务支持的。你不得不设想接下来怎么做。

App Engine 想要强一致性,因为这会使编写应用更简单,因为他们没有采取这个方案。

 

  • Two Phase Commit 2PC - 两阶段提交是在分布式系统上实现事务的一种协议。

 

半分布式 - 因为一定存在一个主仲裁者( master coordinator .

它是同步的。所有的事务会串行的进入 master ,这会减低吞吐量和增加延迟。

如果你很在乎吞吐量的话,你绝对不能考虑这种方案。这种方案没有单点错误。由于额外的协调通讯,它会提高延迟。一个写操作的时间要达到 200 毫秒的级别。

然而这种方案能工作。你将写入所有的数据中心或者什么都没写入。你会得到强一致性和事务支持。

 

  • Paxos 一组独立节点为一个提案达成大多数共识的协议。

 

协议:有提案提出和达成共识 2 个步骤。只要有多数的节点同意提案通过,那么提案将会被视为通过。

2PC 不同的是它是完全分布式的。不存在单个的主协调者。

很多事务可以并行执行。

写操作会有高延迟因为这种协议需要 2 轮额外的协调。

要做到如此,不得不为一次写操作付出 150 毫秒延迟,相对关系型数据库一此写入操作大概 5 毫秒来说,这未免有点高。

他们倾向使用物理上很接近的数据中心,然而一些天生的多数据中心开销(路由器延迟等)也会显得太高。就算是在一个数据中心内部也还是显得太慢了。

Paxos 仍然在 goolge 内部使用得很多。特别是在锁服务方面( chubby ),用来协调所有的跨数据中心的一切操作。特别用来协调状态在数据中心间转移。如果你的应用为一个数据中心提供数据,当它需要把数据转移到另外一个数据中心时,这些协调的工作就需要通过 Paxos

Paxos 也会用来管理 memcache 和离线处理。

 

杂项

 

Entity Groups AppEgine 的一致性单元。 Entity Groups 上的操作都是串行化的。同时所有在 Entity Group 上的提交的操作的日志都会有备份。这样做是为了维持一致性和提供事务支持。 Entity Groups 本质上就是分片( shards . 分片( sharding )是为了提供伸缩性因为它使你可以处理很多写操作。 Datastore 就是按照 entity group 块大小来分片的。 BuddyPoke 4 千万的用户,每个用户都有一个 entity group ,也就是有 4 千万个不同的分片。

 

Eating your own dog food( 自食其力 ) google 内部是一种常用战略。先让人们在内部反复的使用一个新的功能。反复使用不断改进直到真正发布出去。

 

AppEngine 把关系型数据库看成竞争者,就像 Azure SimpleDB 一样。往关系型数据库插入一条数据只需要几毫秒。 AppEngine 写入一条数据需要 30 40 毫秒 , 读却很快。他们喜欢这种权衡折中,因为对于 web 应用来说,读往往比写多得多。

 

讨论

我尚有一些事不明。他们是否考虑过 MVCC Multi-version concurrent control--- 多版本同步控制)的实现模式?这可能很有趣,但是它未被列为一种选项。显然目前 Google 对于内存中的数据伸缩性实现还不是很适当。

 

对于强一致性的偏好,以至它被一再列为主要设计目标,主要是让程序员编程更容易。就算如此在 google app engine 上编程已经非常困难了。由于它的局限性和缺乏关系型数据库的编程特性,在编写一个可伸缩的应用的时候,程序员被迫承接了太多的责任。我很好奇,如果放弃了强一致性,那相比而言又会如何呢?

 

我真的很欣赏那个特性评价矩阵,以及 google app engine 做技术选择的讨论。

对于 Google app engine 来说,写已经相当慢了,因此它没有太多的动态余量用在太多层次之间的协调通讯。这类东西是 Google 的开发人员在设计评审会议上讨论的东西,但是他们通常很少让这类东西流传出来。我貌似听见 Ryan 说道:为什么我们不能得到一切?但是事实就是如此,我们不能得到一切,总得有所取舍。感谢 Ryan 的分享。

 

猜你喜欢

转载自tangay.iteye.com/blog/1158925