论文笔记:Dynamo: Amazon’s Highly Available Key-value Store (上)

前言

  这个月的博客打算记录一篇论文,Dynamo:Amazon’s Highly Available Key-value Store 。这是亚马逊2007年发表的一篇论文,讲的是其dynamo数据库的一些设计原理。

做笔记如下,希望可以便人便己。

ps:这篇笔记以论文叙述为的大体框架

在这里插入图片描述

一、Abstract

  由于大规模的基础设施组件可能会不断发生failure(和google gfs论文中的假想差不多),dynamo的主要设计初衷是为了满足在这种情况下可以保持一定的可靠性(reliability)和扩展性(scalability)。为了提供这种"always-on"的高的可用性(highly availability),dynamo 牺牲了了一定程度的一致性(consistency)。它的一致性解决方案主要是靠版本控制(object version)和上层的应用辅助(application-assisted)来实现的.

  总的来说,dynamo是一个为了提高可用性而牺牲一定程度的一致性的这样一个KV存储系统。

二、Introduction

  这个部分介绍了在现实场景下确实有些应用需要一些高可用的KV存储系统(这也是为什么要设计dynamo的原因),比如说购物车购物的相关逻辑需要保证随时能读写数据。

  之后简要介绍了了dynamo使用的一些技术要点:
在这里插入图片描述
图一 dynamo中用到的一些问题和采取的技术


注:对于一个分布式存储系统来说,其研究的重点主要也就是这些,分区、副本、一致性、故障检测等等。

三、Background

  为什么需要dynamo这样高可用性的KV存储系统?文章中的观点是对于很多应用来说,它们需要更多的是KV服务(以一个primary-key作为CURD的标识),这样的服务虽然也可以用通用的mysql服务来实现,但是有点大材小用,或者说,不够完全的match。 因为mysql提供的是更结构化化的查询存储服务,同时提供了较高的事务特性,这些特点使得其在处理kv这样的服务时,性能不够高。同时可用性和可扩展性也不够完善。

  时代呼唤一个新的存储系统,dynamo应运而生。

3.1 System Assumptions and Requirements

  这里说明了dynamo设计的一些基本假设和前提,主要包括查询模型(Query Model)、事务特性(ACID)、系统效率(Efficiency)以及一些其他的假设。

  • 查询模型: 系统的设计主要是针对于简单的KV查询(典型的no sql 模式),并且基本没有跨多个key的join查询方式,因此对于关系型数据库中的那种schema是不需要的。

  • 事务特性:还是上文中叙述的,为了保证high-availablity ,系统放弃了强一致性,只保证了一种弱的一致性(consistency)。同时系统不提供隔离性(isolation)的保证,对于原子性(atomicity)来说,也只提供单key 更新操作。(对于持久性(Durability)来说,文章中没有给出明确说明,但我觉得这个崩溃恢复这个特性是系统必须要提供的吧。)

  • 效率: 系统要能保证一定的服务等级,即对于请求来说,需要保证一定的延时范围和吞吐量

  • 其他特性: dynamo 的目标是在企业内部使用,所以在设计时没有考虑过多的安全性(比如说网络认证等)。还有,dynamo对可扩展性(scale up)也有一定的需求。

3.2 Service Level Agreement (SLA)

  这部分其实没说什么实质性的内容。讨论的是在一个系统中为了保证服务质量,每个组件都应该保证一定的服务等级,这样整个系统才可以给client提供一个最终的服务保证。 文中还叙述了amazon采用SLA的标准不是通常的平均数、中位数或者期望这种,它采用的是个叫做99.9th的标准(具体是啥,我也不太清楚。(* / ω\*))

3.3 Design Considerations

  这个部分讲的是系统副本之(replication)间的一致性。对于多副本来说,其之间的数据复制是一个大问题。一般来说,有同步的方式和异步的方式。对于同步的方式来说,系统往往需要等到所有的(或者大多数或者指定多个,如quorum)副本都持有相同的数据之后才能向client返回。这样的系统一般是强一致性的,根据CAP定理,这种系统往往牺牲了一定程度的可用性(即存在分区的时候,系统可能会unavailable)。
  为了获得高可用性,一些系统采用了异步复制(也叫做乐观复制技术,optimistic replication techniques)的方式。数据的更改可以在后台中慢慢进行,系统依旧可以提供服务。只不过这时可能会有数据不一致性情况。为了获得高可靠性,dynamo采用的就是这种方式。
   对于这种异步复制的系统来说,会遇到数据冲突的情况。这时何时解决冲突以及由谁来解决冲突就是个认真要考虑的问题了。
  (1). 何时解决冲突: 文章中说有两种解决方式,一种是read期间解决(在read的时候读到了不同版本的数据进行冲突处理),一种是在write期间解决(在write的时候写入到指定数量(所有的或者大多数结点)的replication中去以此来保证read操作的简单)。dynamo为了保证“always writeable”,采用的是前者。

注:这里所说的两种冲突解决方式有点类似于quorum机制,需要做一个treadoff, 是保证write 简单,还是read简单。文中所说的在write期间解决的方式,好像和数据冲突没啥大关系。╮(╯▽╰)╭

  (2). 由谁来解决冲突:对于整个应用来说,应用逻辑一致性要求存储系统和上层应用的(还有其他的组件)协调。某些时候可以把一致性要求放在下层的存储系统中,也有些时候可以由应用逻辑来保证,还有的时候需要它们统一协作来保证系统的逻辑正确。对于dynamo来说,在read期间发生数据冲突的时候,它是由上层的应用程序来决定该如何处理


注:对于一般由存储系统来解决数据冲突的情况,有一个选择是“last write win”.

  除了上面说的一致性要点,还有包括一些可扩展性、去中心化和对称性以及不均匀性(heterogeneity)也是系统要考虑的点。

四、Related Work

  这一节的内容相对来说不是很重要,下面只做简单介绍。

4.1 Peer to Peer System

  这里简单介绍了去中心化的p2p协议的相关系统,如pastry 和chord等。

4.2 Distributed FIle Systems and Databases

  这一部分的主要内容有:

  • 不同于P2P网络提供的是一种扁平化的命名空间(flat namespaces),分布式存储系统大都提供的是一种层次化的命名空间(hierarchical namespaces)。

  • 分布式的多副本存储虽然可以提高可用性(highly availability),但也带来了一致性(consistency)方面的问题(因为各副本之间需要保持一致性)。这往往需要进行一定的权衡(trade off),高的可用性的系统一般就意味着一致性要弱些,比如说最终一致性。

  • Dynamo提供的是最终一致性,也就是说,它允许发生网络分区(network partitions)的时候还可以进行读写操作。这就引起了一个问题,就是对于发生数据冲突(比如说updated confilcts)的时候,需要进行额外的措施解决(resolves)这个问题,以达到最终一致性的要求。

  • Dynamo 是Amazon内部使用的一个最终一致性存储系统,所以它的设计更多的关注是高可用性,对于数据的完整性(data integrity)和安全性(security) 它倒是没有关注太多。(Dynamo does not focus on the problem of data integrity and security and is built for a trusted environment.)

4.3 Discussion

  这一段主要讨论了dynamo 和其他非中心化存储系统不同的几点:

  • 它服务的上层是那些需要“always writeable”的应用,所以即使发生failure 或者concurrent writes的时候,系统也不能拒绝update操作。

  • dynamo建立在可信任的网络之中。

  • 上层应用不需要支持层次命名空间或者复杂的关系schema.(applications that use Dynamo do not require support for hierarchical namespaces (anorm in many file systems) or complex relational schema (supported by traditional databases))

  • dynamo的设计目标是提供对延时敏感的应用(latency sensitive ),它可以在99.9%的情况下提供少于几百ms的读写延时


在这里插入图片描述

五、System Architecture

  一个存储系统的设计包括很多方面,对于这篇论文来说主要讨论的有数据分区(partition),副本处理(replication),版本控制(versioning),成员管理(membership),failure handing 和scaling。 概要如上图一所示。

5.1 System Interface

dynamo 主要对外界提供了两种接口:

  • get(key) 返回的是key所对应的object 或者一组有冲突的不同版本的object(a list of objects with conflictiong versions)
  • put(key,context,object): 向存储系统内部写入key-object值,context用来携带系统的metadat和版本的相关信息(The context encodes system metadata about the object that is opaque to the caller and includes information such as the version of the object.)

5.2 Partitioning Algorithm

  dynamo 对于数据存储分布或者说data partition法采用的是一致性hash算法,更确切的说采用的是改进版的一致性hash算法。
对于基本的一致性hash算法,简要来说就是把数据空间看成一个环(ring),每个node 在其中占据一个位置(hash成ring中的一个位置),然后每个node只负责在两个node之间的范围内的数据(key经过hash映射后的位置)如下图所示。

在这里插入图片描述
  对于一致性hash来说,其最大的优点就是:在node的加入或者移除的时候只影响其临近的其他node结点(需要进行数据迁移)。

  对于普通的一致性hash算法来说,文章中还说明了其两个缺点:
(1) node的hash 位置可能导致数据的不均匀分配,进而导致每个结点的负载不均衡。
(2) 这种算法没有考虑到node性能的差异性。也就是说,对于性能较高的node,可以让其负责更多的数据。

  为了解决这个问题,文章中采取了优化后的一致性hash算法,即在hash换中引入了虚拟节点(virtual node),每个物理node对应多个虚拟节点,然后虚拟节点映射到ring中。这 从某种程度上来说算是加了一个分层,从key->node 变成了 key->virtual node-> physical node。

  采用了优化后的一致性hash具有以下优点:
(1) 当添加或者删除node之时,数据的变动变得更为平均。即添加nodeA时,nodeA能够从其他多个node中较为均匀的获取数据; 删除nodeA时,nodeA的数据也能较为平均的转移给其他的结点。
(2) 可以根据每个node的capacity 来进行区别对待,比如说性能高的node分配更多的virtual node,这样其就可以负责更多的数据。

  
注:

  1. partition算法还有很多,如按照时间、按照范围、按照数据size进行分区; 系统的论述可以参见【2】

5.3 Replication

  副本的作用在分布式存储中主要是用来备份和提高系统性能(主要是读性能)。在dynamo中为了获得high availability 和 durability, 也采用了replication的机制。即每个key-value在整个分布式系统中要有N个副本,而且这N个副本要保存在N个独立的physical node上,这样一个node 宕机了,其他的N-1 个node才可以继续提供服务。如下图二所示。

在这里插入图片描述
图二 数据分区示意图

5.4 Data Versioning

  因为提供了“always writable”的保证,dynamo 允许存在网络分区的时候进行数据update操作。这个时候如果出现了数据更新冲突怎么办? dynamo采用的机制是使用数据多版本控制,即每个object维护一个一个向量时钟(version clock),根据这个向量时钟来进行冲突的解决,主要有syntactic reconciliation(主要是新版本正常覆盖老版本,由系统自动覆盖) 和 semantic reconciliation (同一个状态由于并发更新引发的多冲突分支)。如下图三所示。

在这里插入图片描述
图三 版本并发控制示意图

注:

  1. 按照我的理解,这里dynamo系统使用的其实是一种乐观的多版本并发控制技术-MVCC,通过保存数据的多个版本(只需要保存在并发过程中的版本,非并发过程的数据可以保存一份,也就是paper中说的synatactic reconciliation )来进行分布式的并发冲突处理,这有点类似于SVN或者git这种版本控制系统。

  2. 具体的判断版本之间的因果关系(causality),即一个版本是否从另一个版本中演化而来。不同的系统可能使用不同的方案。这里dynamo使用的的版本向量(version version),这是分布式的开山鼻祖Lamport的一篇paper。具体的内容可以参见【3】

5.5 Execution of get() and put() operations

  这部分主要说了dynamo正常的读写操作。 其中重点有如下内容:

(1) 在client进行路由寻址的时候主要有两种方式,一种是使用一个负载均衡器进行request分发;第二种是client端程序链接到一个partition-aware的库程序(一般由开发者提供),通过调用库程序来选择request的请求node.

(2) 为了保持副本间的一致性要求,系统采用了类似quorum 协议。在写入保证写入W个副本才算写入成功,在读取时需要读取R个副本。其中R+W>N 。(实际实现的时候并没有采取这样严格的quorum机制,而是使用的一种类似a sloppy quorum机制)

(3) 在put()时,系统会为新数据产生一个vector clock ,然后将新数据和其相关的vector clock同步到N个结点中。 只有至少W个结点正确write,put操作才算成功。

(4)在read()操作时,系统会从N个结点中读取至少R个reolication的数据, 然后把数据返回给client。当然,这些数据有可能是有冲突的,需要client端来reconcile。

5.6 Handling Failures: Hinted Handoff

  上面所说的a sloppy quorum机制就是使用基础的quorum加上hinted handoff 实现的。也就是说如果待写入的W个副本中有一个(或者一个以上)node(假设一个node中存储一个replication) 发生宕机或者其他的failure, 那么就会采用hinted handoff的机制。

  所谓hinted handoff 也就是采用一个缓冲队列,如果一开始向目标node发送数据时发生错误,那么就会把待发送的replication 缓存在这个队列中,然后在后台的任务中周期的向目标结点进行重试,直到发送成功(或者超期删除)。在这个过程中可以执行此节点已经成功写入(实际上不一定能够成功写入)的相关逻辑(比如说如果凑齐了W个副本,则向client端成功返回)。

  本质上是把一个同步传输的任务变成了异步传输的任务。

注:
有一个疑问点,如下图所示,根据论文中所述,如果一个副本本该写入A,B,C三个node,但是如果A fail 了,那么系统会把本该发送给A的数据副本顺延到D,由D来进行将数据周期发送给A的任务执行? 我的疑问是,为什么不直接把数据放在B的缓存队列中,由B进行周期传送呢?难道是D 接受了本该由A的数据后还可以进行提供这个副本数据的读取服务?

在这里插入图片描述
图四 数据分区示意图

5.7 Handling permanent failures:Replica synchronization

  对于暂时性的网络分区或者结点故障,使用上面的hinted handoff 可以取得较好的效果。但是如果遇到长时间的网络分区或者结点完全宕机,那么这会造成hinted handoff的缓冲队列溢出(或者超出设定的最大时限),进而导致无法把数据异步发送到最终的node上去。

  这个时候往往就需要进行额外的数据同步操作。dynamo为了处理这种情况采取了一种叫做反熵(anti-entropy)的副本同步机制
。为了快速对比replication之间的数据不一致(减少传输的数据量),dynamo采用了merkle tree(也叫做hash 树)的一种数据结构。这种hash树简单来说就是构造一个hash树,这棵树的叶子结点是所有的数据块,然后根据数据块算出一个hash值,每两个(或多个)hash值在共同算出一个hash值,一直向上递推,直到根结点。如下图所示。
在这里插入图片描述
图五 hash tree 示意图

  在进行具体的比较时只需要从不同node的根结点进行比较,如果根结点相同,则说明两棵hash树是相同的,进而表明tree所对应的数据块是相同的;如果根结点不相同,则进行递归比较其左右子树,知道确定最后不同的数据块。如下图六所示。
在这里插入图片描述
图六 不同node上的hash 树


  具体的有关hash tree的原理可以参照【4】。

  关于具体dynamo的实施细节,小生还不是非常理解,从相关资料【5】里找到了以下一段文字。
在这里插入图片描述
图七 dynamo有关hash tree的一段文字


注:

  1. 对于anti-entropy,也就是反熵来说,其更像一种概念。熵代表的是系统的混乱程度,反熵说的是降低系统的混乱程度,使其变得更有秩序。在这里也就是让两个replication之间的数据变得更加一致。从这种概念上来说,实现数据的一致有很多种方式,dynamo使用了hash树加数据同步。【6】中也介绍了一些其他的方式。

  2. 对于dynamo使用hash树来检测replication的数据不一致还有一些小疑问: 如果两个待比较的node之间的key数目不一样怎么办?又或者说key1在node1中存在,但是在node2中不存在(本来应该存在),这样怎么构造hash树呢?

5.8 Membership and Failure Detection

  这个部分主要讲的是成员变更和成员的failure 检测。 由于dynamo采用的是去中心化的架构。所以在添加成员node时,其他的结点在开始的一段时间内是无法了解这个信息的。dynamo为了快速把消息传递给其他node结点,采用了gossip(谣言) 算法来吧消息传递出去。在dynamo系统中,gossip不仅用来传递成员变更的信息,还用来传递哪些node负责哪些key range 等元数据信息。 关于成员peer的failure detection, 系统也是使用gossip来传递的,其基本原理就是发送心跳消息,如果对方在指定的时间内没有回复,则判断其为failure, 然后当前结点把这个消息传递出去。

  具体的gossip协议,网上有很多资料,在此就不赘述了(其实我也没学过,^ _ ^)。


注:

  1. 对于成员信息、数据分区等这些元数据信息一般的系统是有一个中心化的结点(或者集群)来管理的。比如说gfs、bigtable等。这里采用非中心化的架构,不能说其好坏,马克思告诉我们,各有利弊而已。**( /ω\ *)
  2. gossip是一种简单高效的peer to peer 信息交换系统, 它在DynamoDB里面起到很重要的作用。

5.9 Adding/Removing Storage Nodes

  这一部分没讲什么太多的内容,主要具体介绍了一个结点加入(或者移除)member ring 中时,会对周围的node造成一些影响,比如说数据的Rebalance、迁移等。

后记

  论文内容比较多,这里先介绍到这儿,剩下的部分不是重点,放在【下】中介绍了。


参考

【1】、通俗易懂 强一致性、弱一致性、最终一致性、读写一致性、单调读、因果一致性 的区别与联系
【2】、《分布式系统原理介绍》,刘杰
【3】、time-clocks
【4】、Merkle Tree概念
【5】、Dynamo基础之Merkle Tree
【6】、Gossip协议:流言蜚语,原来也可以实现一致性
【7】、Dynamo: Amazon’s Highly Available Key-value Store

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值