分享

分布式数据库设计中的一致性权衡

 openlabzeng 2016-10-12

本文部分内容取材于耶鲁大学Abadi的一些博客和文章。Abadi是一些分布式数据库的缔造者,如Calvin,HadoopDB等,同时是著名的列式数据库 C-Store和高性能事务性数据库H-Store的缔造者之一, C-Store已经被商业化为高性能列存引擎Vertica,H-Store也被VoltDB商业化,而Abadi自己则基于HadoopDB创建了Hadapt公司,因此是位不折不扣的数据库和分布式系统专家。


CAP理论自从2000年提出,迄今已经是分布式系统构建的基础之一。CAP理论提出任何基于网络的数据共享系统,都最多只能拥有以下三条中的两条:

  1. 数据一致性(C),等同于所有节点访问同一份最新的数据副本;

  2. 高可用性(A);

  3. 分区容错性(P)。


CAP理论对于分布式数据库的设计者们来说是极其重要的,因为在它存在之前,设计者们为了实现分布式数据库绞尽脑汁却常常感觉面对各种复杂分布式场景无所适从,CAP理论提供了一个简单的工具来帮助他们做简单的选择题。然而,从CAP出现后的十多年里,该理论因为过于简单而容易引起误解,性能延迟跟一致性的权衡相比CAP权衡来说对于分布式数据库的设计可能会更明显。例如对于分布式数据库来说,分区容错性P几乎是必备的,因此设计者们会认为需要在一致性C和高可用性A之间选择,通常情况下设计者们都会选择牺牲一致性来获得高可用性(AP)。然而这种选择跟CAP理论是有出入的,因为分区容错性不仅仅意味着需要在C和A之间做出权衡,还要考虑网络分区本身,例如网络分区是基于跨机房,还是仅仅单机房?网络硬件质量如何?冗余度如何?网络参数更改所需要的开销如何?等等,尽管网络分区通常出错的的可能远远小于其他种类的问题,但是由于CAP理论本身对这方面并没有做出任何假设,所以说设计分布式数据库时用牺牲一致性获取高可用并不全面,更准确的说法是:在没有网络分区的情况下,CAP理论能够确保ACID和高可用都可以获得。


为了深入理解现代分布式数据库,弄清楚它们每一种的设计上下文是十分重要的。Amazon推出Dynamo是为了支撑其电商平台的数据服务;Facebook的Cassandra则是为了支撑收件箱及其查询特性;Linkedin开发Voldemort用于处理巨量的写入请求数据;Yahoo开发PNUTS则是存储用户档案以及社交网络相关信息;Riak跟Dynamo的应用场景类似。在以上的每种场景里,应用服务器都需要从数据库中获取相应数据然后生成Web页面,访问延迟对于用户体验可以说是第一重要的因素,每增加100毫秒的延迟,都会让用户继续等待Web页面出现的概率降低,很不幸,延迟,一致性,和高可用性之间存在着不可避免的权衡——当然,延迟和可用性之间本身也是存在密切关系的,不具备可用性的服务延迟当然可能无限长,所以此处讨论的延迟是正常情况下的返回。以上三者之间存在矛盾,因为高可用性意味着必然引入数据副本,数据复制在正常操作下同样会产生。为了能够做到最高可用性,分布式数据库甚至需要做到跨数据中心的数据同步。同时,由于数据复制的产生,也就意味着需要在延迟跟一致性之间做出权衡,因为只有三种方法进行数据复制:1) 系统向所有副本发出数据更新请求;2) 系统向选举出来的master主节点发送数据更新请求;3) 系统向任意节点发送数据更新请求。下边分别描述一下每种手段都会面临哪些权衡。


当数据更新请求发送到所有副本时,如果没有约定的协议而让所有副本分别处理,那么就无法保证数据更新的时序在所有副本保持一致;如果有这样的协议存在,那就意味着需要增加额外的协商而导致延迟增加。

当数据更新发送到master节点时,意味着master节点会处理所有更新请求,同时也意味着所有其它slave副本节点也会按照master的时序来处理写入请求。有3种方式做到master和其余slave节点之间的数据同步:1) 同步复制。master节点会等待所有复制请求都已经被发送到slave节点并成功返回。这可以确保一致性,但会增加延迟。2) 异步复制。系统假定数据在同步之前就已经完成,在这种情况下,一致性和延迟的权衡取决于系统是如何处理读请求的:如果系统把所有读请求都路由到master节点,那么就不存在一致性问题,但却存在延迟或者可用性问题:一旦master节点过载或者崩溃,前者导致延迟过高,后者则直接不可用。如果系统把读请求路由到任意节点,那么就不会出现过载,但不同节点可能会返回数据的不同版本,导致一致性问题。3) 第三种处理数据同步的方式是组合同步跟异步:系统选择把数据更新请求同步发送到部分副本,然后其余的副本则采用异步复制。在这种情况下,一致性和延迟的权衡仍然取决于系统设计:如果读请求被路由到至少一个接收同步更新的节点,例如根据Quorum选举协议,当R + W > N时可以保证一致性,其中R是接收读请求的同步节点数, W 是同步写请求的节点数,N是副本数量,当然,前述的延迟问题都存在,只不过有所缓解。如果读请求会被路由到异步复制节点,那么就会存在一致性问题。

如果数据更新请求被发送到任意节点,情况跟上述类似,取决于同步复制还是异步复制,以及读请求被路由到哪些节点。


为了更好理解上边的描述,这里可以选择一些分布式数据库为例来看看它们是如何设计的:Dynamo,Cassandra,Riak选择了以上第二种数据复制方式跟第三种方式的组合:系统首先提交更新请求到某一节点,然后由该节点扩散数据同步复制到W个节点;系统同时路由读请求到R个同步复制节点,这里R+W不小于副本的数量。 与此同时,系统并不是把数据更新请求总发到某一个节点,以此做到可用性和低延迟,当然是牺牲一定的一致性为代价。BigTable和HBase选择了第一种数据复制方式,既数据更新请求提交到所有副本,相比之下一致性更强而延迟有损失。Megastore则做到了更强的一致性,因为它确保同一个组内的数据的ACID,而组之间通过Paxos做到跨区域同步复制。MongoDB在分区存在的情况下,如果主master发生错误,它会存储所有还未复制的写入请求数据到本机rollback目录下,同时其余系统会重新选举出新的master节点,因此直到rollback目录的数据全部同步完成之后才能让新的master节点达到出错节点的状态。Yahoo的PNUTS系统以异步的方式维护远程副本而带来数据一致性问题,但好处是主副本就放在本地,减小操作的等待时间。这个策略在实际中很实用,因为一般来讲,用户数据大都会根据用户的地理位置做分区。最理想的状况是每一位用户都在他的数据主副本附近。因此有时候在跨数据中心的系统,牺牲一致性来避免高延迟是有意义的。


一致性和延迟的权衡只在需要数据复制的前提下存在。回到分布式数据库设计考虑的其他方面,我们接下来顺便谈及一下为什么ACID那么困难:如果一个事务需要跨机器进行,ACID因为原子性就需要引入分布式提交协议,比如二阶段提交(2PC),隔离性需要事务本身在提交协议持续时间内一直持有锁。由于OLTP负载是有大量轻量级事务构成的,处理网络请求就会在相当长的时间内持有锁,这会严重影响性能。另一方面,副本的引入使得ACID在副本之间面临更大的挑战。所以一般分布式数据库都会分成以下几类:1) 牺牲ACID获得可伸缩性,例如绝大多数No-SQL系统;2) 降低事务的灵活性来获得可伸缩性,例如部分所谓的New-SQL系统,确保单机事务性而避免分布式事务;3) 放弃可伸缩性,采用专有硬件Scale Up,比如Oracle,DB2。有不少方案试图提出第四种手段,例如引入预先分配的时间戳确保多节点都按照确定顺序执行,避免引入2PC,以及修改数据复制流程,使之发生在数据更新请求被应用到系统任何节点之前,Google Spanner就是这样的系统。由此可以看出,分布式数据库设计,本质上就是日志的管理。由于设计的复杂性,除了学术界,很多公司(除了Google)都是在绕过问题而非面对问题,比如阿里主推的OceanBase,为绕过分布式ACID采用了单机更新,也就是所有的写入行为发生在一台内存很大的机器来进行。


    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多