分布式键值系统Amazon Dynamo简介
- Dynamo采用的技术
- 虚拟节点
- Gossip协议
- NRW
- Vector Clock
- 读写流程
- 参考链接
Dynamo采用的技术
问题 | 采用的技术 |
数据分布 | 改进的一致性哈希(DHT),采用了虚拟节点技术 |
复制协议 | 复制写协议(Replicated-write protocal,NRW参数可调) |
数据冲突处理 | 向量时钟 |
临时故障处理 | 数据回传机制(Hinted handoff) |
永久故障的恢复 | Merkle哈希树,简单讲就是二叉树的复制 |
成员资格及错误检测 | 基于Gossip的成员资格和错误检测协议 |
虚拟节点
虚拟节点的分配有如下两种方式:
- 随机分配
- 该方法可以保证负载分配的比较平均,但是这个方法的问题是可控性较差,新节点加入、离开系统时,急群众的原有节点都需要扫描所有的数据从而找出新节点的书绝,Merkle树也要全部更新;另外增量归档、备份变得几乎不可能。
- 数据范围等分+随机分配
- 将数据的哈希空间等分为份(N=机器个数,S=每台机器的虚拟节点数),然后每台机器随机选择S个分割点作为Token,每台机器对各个数据范围分别维护一颗Merkle树。新节点的加入和离开只需要对涉及到的部分进行数据同步。
Gossip协议
执行过程:
Gossip 过程是由种子节点发起,当一个种子节点有状态需要更新到网络中的其他节点时,它会随机的选择周围几个节点散播消息,收到消息的节点也会重复该过程,直至最终网络中所有的节点都收到了消息。这个过程可能需要一定的时间,由于不能保证某个时刻所有节点都收到消息,但是理论上最终所有节点都会收到消息,因此它是一个最终一致性协议。
Dynamo通过Gossip干了如下几件事情:
- 系统中的每个节点都会定期(比如1s)通过Gossip协议同其他节点交换集群信息,并相互更新各自落后的节点版本信息,如果发现某个节点很长时间都没有状态更新,则认为该节点已经下线了。
- 如果某节点a重启,其他持有节点a数据副本的节点能通过Gossip协议发现,并将副本数据归还节点a,这种机制叫做数据回传(Hinted Handoff)
NRW
类似Quorum中的,只不过这三个参数的取值可以根据实际的需求调整,并不一定要满足大于。由此导致的问题是,在Dynamo这样的P2P集群中,由于每个节点存储的集群信息有所不同,可能出现同一条记录被多个节点同时更新的情况,无法保证多节点之间的更新顺序。因此Dynamo引入了向量时钟(Vector Clock)的技术手段尝试解决冲突。
NRW对节点失效的保证性也和一般的一致性协议如Raft、paxos不同。仅保证了当存在不超过一台机器故障时,至少能够读到一份有效数据。
NRW还导致了一个问题,假设N=3,W=2,R=2,如果部分写操作已经返回客户端成功了但是没用完全同步到所有的副本,会导致三个副本之间的数据都不一致。因此要Dynamo启动修复任务,该任务会合并各个副本中的数据并更新过期的副本,从而使得副本间保持一致。
Vector Clock
每个节点对每个对象的操作用向量时钟[nodes, counter]
表示,出现冲突之后需要解决,最常见的冲突解决方式有两种:
- 通过客户端逻辑解决
- 通过时钟“last write wins”解决,这种方式依赖集群内节点之间的时钟同步算法,不保证完全准确。
Dynamo只能保证最终一致性,如果多个节点之间的更新顺序不一致,客户端可能读不到期望结的结果。多客户端并发操作也很难预测操作结果。
Dynamo最大的问题应该就是,牺牲了一致性,但是没有换来什么好处。
读写流程
Amazon Dynamo采用P2P架构,因此各Replica间不存在主节点,读写操作的时候:
- 客户端首先通过DHT找到所有的副本,其中一个副本作为协调者
- 客户端把IO请求发往协调者,协调者并发地向所有副本发送读写请求。同时协调者也将IO向本地发起请求
- 其他副本成功响应的个数满足W/R之后,协调者回复客户端成功,但是仍会继续等待或者重试,直到所有的副本都响应成功。
- 读过程中,如果发生冲突,需要自己解决冲突或将冲突返回给客户端。
- 读操作成功之后,对应的状态机不会立即销毁(可能只有R个节点返回了),协调者会等待一小段时间,这段时间内可能还有一些节点会返回过期的数据,协调者将更新这些节点的数据到最新版本,这个过程称为读取修复。