数据副本协议 (史上最全)
文章很长,而且持续更新,建议收藏起来,慢慢读!疯狂创客圈总目录 博客园版 为您奉上珍贵的学习资源 :
免费赠送 :《尼恩Java面试宝典》 持续更新+ 史上最全 + 面试必备 2000页+ 面试必备 + 大厂必备 +涨薪必备
免费赠送 经典图书:《Java高并发核心编程(卷1)加强版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领
免费赠送 经典图书:《Java高并发核心编程(卷2)加强版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领
免费赠送 经典图书:《Java高并发核心编程(卷3)加强版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领
免费赠送 经典图书:《尼恩Java面试宝典 最新版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领
免费赠送 资源宝库: Java 必备 百度网盘资源大合集 价值>10000元 加尼恩领取
数据副本协议
在分布式系统中有个CAP理论,对于P(分区容忍性)而言,是实际存在 从而无法避免的。因为,分布系统中的处理不是在本机,而是网络中的许多机器相互通信,故网络分区、网络通信故障问题无法避免。因此,只能尽量地在C 和 A 之间寻求平衡。对于数据存储而言,为了提高可用性(Availability),采用了副本备份,比如对于HDFS,默认每块数据存三份。某数据块所在的机器宕机了,就去该数据块副本所在的机器上读取(从这可以看出,数据分布方式是按“数据块”为单位分布的)
但是,问题来了,当需要修改数据时,就需要更新所有的副本数据,这样才能保证数据的一致性(Consistency)。因此,就需要在 C(Consistency) 和 A(Availability) 之间权衡。
副本协议的定义:
副本按特定的协议流程控制副本数据的读写行为,使得副本满足一定的可用性和一致性要求的分布式协议。
副本协议分为两类:中心化副本控制协议和去中心化副本控制协议
一:中心化副本控制协议
由一个中心节点协调副本数据的更新、维护副本之间的一致性。
优点:协议相对简单,所有副本相关的控制交由中心节点完成。
缺点:系统的可用性依赖于中心化节点,当中心节点异常或者中心节点通信中断时,系统将失去某些服务。
中心化副本控制协议主要有primary-secondary协议、2PC、MVCC。
primary-secondary协议
primary-secondary又称为primary-backup,在协议中副本被分为两类,有且只有一个primary副本,其他的全部是secondary副本。
primary-secondary包括以下四大内容:
数据更新流程
数据读取方式
Primary副本的确定和切换
数据同步
a:数据更新流程
(1):数据更新都有primary节点协调完成。
(2):外部节点将更新操作发送给primary节点。
(3):primary节点进行并发控制即确定并发更新操作的先后顺序。
(4):primary节点将更新操作发送给secondary节点。
(5):primary根据secondary节点的完成情况决定更新是否成功并将结果返回给外部节点。
b: 数据读取
如果只需要最终一致性,则读取任何副本都可以满足需求。如果需要会话一致性,则可以为副本设置版本号,每次更新后递增版本号,则用户读取副本时验证版本号,从而保证用户读到的数据都在会话范围内递增。想要最终一致性比较困难,由一下几种思路参考.
(1):只读primary节点。
(2):由primary控制节点secondary的可用性,更新成功为可用,更新不成功为不可用。
(3): 基于Quorum机制。
c: primary副本的确定与切换
通常在primary-secondary类型的分布式系统中,哪个副本是Primary是属于元信息,由专门的元数据服务器维护。执行更新操作前先查询元数据管理服务器获取primary信息,再进行操作。
切换副本的难度在于两个方面:
(1):如何确定节点的状态以及发现原primary节点异常时一个较为复杂的问题。
基于Lease机制(心跳机制)、基于Quorum机制(过半机制)。
(2):在原primary以及死机,如何确定一个secondary副本使得该副本上面的数据与原primary一致成为新的问题。
由于分布式系统中可靠的发现节点异常需要一点的探测时间的,探测时间通常在10秒级别。 primary-secondary类型的分布式系统的最大缺点就是primary切换带来的一定时间的停服务时间。
d: 数据同步
通常的不一致情况:
(1): 由于网络分化等异常,secondary上面的数据落后primary上的数据。
(2):在某些协议下面可能有脏数据,需要被丢弃。
(3):secondary是新增的副本,完全没有数据,需要从其他副本拷贝数据。
解决方法
对于(1),常用的方法是回放primary上面的操作日志(通常是redo日志),从而追上primary的更新进度。
对于(2),较好的方法就是在设计分布式协议不产生脏数据。也可以基于undo日志的方法删除脏数据。
对于(3),直接拷贝primary数据,或者使用快照。
二:去中心化副本控制协议
去中心化协议没有因为中心节点异常带来的停服务问题,缺点是协议过程通常比较复杂。
去中心化副本控制协议主要有:Paxos
三:读写控制规则
约定:
更新操作是一系列顺序的过程,每次更新记录操作记为Wi,i为更新操作单调递增的序号,每个Wi执行成功后副本数据都变化,称为不同数据版本,记为Vi。
WARO
WARO是一种简单的副本控制协议,当 Client 请求向某副本写数据时(更新数据),只有当所有的副本都更新成功之后,这次写操作才算成功,否则视为失败。这样的话,只需要读任何一个副本上的数据即可。但是WARO带来的影响是写服务的可用性较低,因为只要有一个副本更新失败,此次写操作就视为失败了。
需要一种机制当某次更新操作Wi一旦所有副本都成功,全局都能知道这个消息,此后读取操作指定读取数据版本为Vi的数据。
写服务而言,记录更新成功的版本号Vi的操作将成为关键操作,通常方法就是将版本号信息存放在某个或某组元数据服务器上,容易成为瓶颈。因为更新需要全部副本成功才能成功,所以当任意一个副本不可用的情况,更新服务就不可用。在工程实践中,这种机制往往比较难实现或者效率较低。
读服务而言系统可以容忍N-1个副本异常。
从这里可以看出两点:①写操作很脆弱,因为只要有一个副本更新失败,此次写操作就视为失败了。②读操作很简单,因为,所有的副本更新成功,才视为更新成功,从而保证所有的副本一致。这样,只需要读任何一个副本上的数据即可。假设有N个副本,N-1个都宕机了,剩下的那个副本仍能提供读服务;但是只要有一个副本宕机了,写服务就不会成功。
WARO牺牲了更新服务的可用性,最大程度地增强了读服务的可用性。
Quorum
Quorum在WARO之上做了限制而来。
Quorum是一种简单的副本管理机制。WARO牺牲了更新服务的可用性,最大程度的增强了读服务的可用性。
在Quorum机制下面,当某次更新操作Wi一旦在所有的N个副本都成功,则认为更新操作为“成功提交的更新操作”,对于的数据为“成功提交的数据”。
Quorum机制是“抽屉原理”的一个应用。定义如下:假设有N个副本,更新操作wi 在W个副本中更新成功之后,才认为此次更新操作wi 成功。称成功提交的更新操作对应的数据为:“成功提交的数据”。对于读操作而言,至少需要读R个副本才能读到此次更新的数据。其中,W+R>N ,即W和R有重叠。一般,W+R=N+1
W+R>N ,得到 R>N-W
由于更新在W个副本上面是成功的,所有最多读取R个副本则一定能读到Wi更新后的数据Vi。
当W=N,R=1,W+R=N+R>N , 就得到WARO。
Quorum要点:
• 写入数据的时候,要求W个节点确认收到
• 读取数据的时候,读取R个节点,获取最新数据版本
• W + R > N(总节点数)则称为Quorum
例子
假设系统中有5个副本,W=3,R=3。初始时数据为(V1,V1,V1,V1,V1)--成功提交的版本号为1
当某次更新操作在3个副本上成功后,就认为此次更新操作成功。数据变成:(V2,V2,V2,V1,V1)--成功提交后,版本号变成2
因此,最多只需要读3个副本,一定能够读到V2(此次更新成功的数据)。而在后台,可对剩余的V1 同步到V2,而不需要让Client知道。
如何读取最新成功提交的数据:
对于强一致性系统,应该始终读取返回最新的成功提交的数据,在Quorum机制下面条件需要进一步限制。
1:限制提交的更新操作必须严格递增,即只有在前一个更新操作成功提交之后才可以提交后一个更新操作,从而保证提交的数据版本是连续增加的。
2: 读取R个副本,对于R个副本中版本号最高的数据,
2.1 若已经存在W个,则该数据是最新的成功提交的数据
2.2 若存在个数少于W个,为X个,继续读取其他副本,直到读取到W个该版本的副本,则认为是最新提交的数据。
四,Quorum机制应用实例
国内多个厂商发生数据灾难,导致服务中断,关于异地多活的容灾讨论重新被激活,引起业界的广泛关注。所以数据往往有多个副本,从而,使用Quorum机制保障多副本数据的一致性,变得非常普遍。
微信的Quorum_KV
腾讯的NoSQL存储系统经过精心设计,已经能够应对海量数据的高并发、高可用、低延迟的应用场景,由几百台存储机组成的存储集群可以轻松扛起数百万的读写并发。集群的性能具有线性扩展,集群最大可以容纳到几千台存储服务器。当然如此庞大的集群,相应地也会带来管理上的复杂,因此的一个集群不会超过1千台服务器
微信使用的NoSQL存储名称为Quorum_KV,是基于LSMTree研发的强一致性、持久化KV分布式存储,支持key-table 和 string-value两种存储模型。
Quorum_KV通过Quorum协议实现双向可写功能,一个最小的存储单元由二台存储机和一台仲裁机组成,写存储机时经过仲裁决定被写的机器。
数据的读写都在内存中进行,内存的Key采用多阶HASH来索引,读写性能极高,但受数据容量受内存的限制。数据在主、备上都落盘,保证了数据的持久性。一个存储集群可以支持业务混合存储,业务Key采用一致性HASH方式分布。
写数据时直接写内存的Memtable表。Memtable写满后转换成Immutable。Immutable定期Dump到本地磁盘变成数据文件,数据文件不断递增形成不同level级别数据文件,不同level级别的数据文件会定期合并。
内存表使用Skiplist来做内存的Key索引。
读数据时先读内存表,如果找不到记录再寻找Immutable,然后是level 0磁盘文件、level 1……直至寻找到记录返回结果。所以可以看到,Quorum_KV的设计是面向写量大,读量小的业务场景,很好地适应了微信消息写量大的特点。
一个最小的存储单元由二台或多台存储主、备组成,主备可组成一主多备的链状结构。数据写入主后同步到备。客户端可同时读取主备,因此读到备的客户端会有一定的数据延迟,适合对数据一致性要求不高的业务。
Zookeeper
Zookeeper的选举机制是遵循了Quorum的,这也是为什么我们部署Zookeeper必须要求有奇数个Cluster可用的原因。这样一是能保证Leader选举时不会出现平票的情况,避免出现脑裂。二是Leader在向Follower同步数据的时候,必须要超过半数的Follower同步成功,才会认为数据写入成功。
elasticsearch
ES 在 Master 被选举之前是一个 P2P 的系统,但是当 Master 被选取后,它的管理本质上是 Master 和 slave的模式。
Elasticsearch 看名字就能大概了解下它是一个弹性的搜索引擎。首先弹性隐含的意思是分布式,单机系统是没法弹起来的,然后加上灵活的伸缩机制,就是这里的 Elastic 包含的意思。它的搜索存储功能主要是 Lucene 提供的,Lucene 相当于其存储引擎,它在之上封装了索引,查询,以及分布式相关的接口。
Elasticsearch是一个实时(索引数据到能被搜索大概1s左右)的分布式搜索和分析引擎,主要用于全文搜索,结构化搜索以及分析。Elasticsearch使用Lucene作为内部引擎,但是在使用它做全文搜索时,只需要使用统一开发好的API即可,而不需要了解其背后复杂的Lucene的运行原理,可以说是一个开箱即用的分布式实现,其内部定义了大量的默认值。Elasticsearch并不仅仅是Lucene这么简单,它不但包括了全文搜索功能,还可以进行以下工作:
- 分布式实时文件存储,并将每一个字段都编入索引,使其可以被搜索。
- 实时分析的分布式搜索引擎。
- 可以扩展到上百台服务器,处理PB级别的结构化或非结构化数据。
为什么ES使用主从模式
除主从(Leader/Follower)模式外,另一种选择是分布式哈希表(DHT),可以支持每小时数千个节点的离开和加入,其可以在不了解底层网络拓扑的异构网络中工作,查询响应时间大约为4到10跳(中转次数)。例如,Cassandra就是使用这种方案。但是在相对稳定的对等网络中,主从模式会更好。
ES的典型场景中的另一个简化是集群中没有那么多节点。通常,节点的数量远远小于单个节点能够维护的连接数,并且网络环境不必经常处理节点的加入和离开。这就是为什么主从模式更适合ES。
elasticsearch 写一致性原理以及quorum机制
一、例子
我们在发送任何一个增删改操作的时候,比如说: put /index/type/id
,都可以带上一个consistency参数,指明我们想要的写一致性是什么,例:
put /index/type/id?consistency=quorum1
二、consistency
consistency的三个值:one(primary shard)、all(all shard)、quorum(default)。
- one:要求我们这个写操作,只要有一个primary shard是active活跃可用的,就可以执行
- all:要求我们这个写操作,必须所有的primary shard和replica shard都是活跃的,才可以执行这个写操作
- quorum:默认的值,要求所有的shard中,必须是大部分的shard都是活跃的,可用的,才可以执行这个写操作
三、quorum机制
写之前必须确保大多数shard都可用,保证shard数量大于int( (primary + number_of_replicas) / 2 ) + 1
才生效,所以number_of_replicas
必须大于1(根据前面的式子)。
quroum = int( (primary + number_of_replicas) / 2 ) + 11
例:3个primary shard,number_of_replicas=1,总共有3 + 3 * 1 = 6个shard
quorum = int( (3 + 1) / 2 ) + 1 = 3
所以,要求6个shard中至少有3个shard是active状态的,才可以执行这个写操作
如果节点数少于quorum数量,可能导致quorum不齐全,进而导致无法执行任何写操作
例:3个primary shard,replica=1,要求至少3个shard是active,3个shard,必须在不同的节点上,如果说只有2台机器的话,是不是有可能出现说,3个shard都没法分配齐全,此时就可能会出现写操作无法执行的情况
es提供了一种特殊的处理场景,就是说当number_of_replicas>1时才生效,因为假如说,你就一个primary shard,replica=1,此时就2个shard,(1 + 1 / 2) + 1 = 2,要求必须有2个shard是活跃的,但是可能就1个node,此时就1个shard是活跃的,如果你不特殊处理的话,导致我们的单节点集群就无法工作
四、quorum wait
quorum不齐全时,会wait,默认1分钟,timeout,100,30s。wait等待期间,期望活跃的shard数量可以增加,最后实在不行,就会timeout。
其实可以在写操作的时候,加一个timeout参数,这个就是说自己去设定quorum不齐全的时候,es的timeout时长,可以缩短,也可以增长,如:
put /index/type/id?timeout=30 # 默认毫秒
put /index/type/id?timeout=30s # 指定单位为30秒
HDFS高可用性实现
HDFS的运行依赖于NameNode,如果NameNode挂了,那么整个HDFS就用不了了,因此就存在单点故障(single point of failure);其次,如果需要升级或者维护停止NameNode,整个HDFS也用不了。为了解决这个问题,采用了QJM机制(Quorum Journal Manager)实现HDFS的HA(High Availability)。
为了实现HA,需要两台NameNode机器,一台是Active NameNode,负责Client请求。另一台是StandBy NameNode,负责与Active NameNode 同步数据,从而快速 failover。
那么,这里就有个问题,StandBy NameNode是如何同步Active NameNode上的数据的呢?主要同步是哪些数据呢?
数据同步就用到了Quorum机制。同步的数据 主要是EditLog。
数据同步用到了一个第三方”集群“:Journal Nodes。Active NameNode 和 StandBy NameNode 都与JournalNodes通信,从而实现同步。
为解决NameNode的单点问题,在Hadoop 2.0对HDFS的高可用进行了改进,使得系统中可以同时启动多个NameNode,一个Active,一个Standby,并使用ZKFC(ZKFailoverController)对两者进行监控,当发现Active的NameNode服务中断,Standby的NameNode的状态会自动变为Active,接替原ActiveNameNode对外提供服务。
要想实现上面的功能,那就必然需要一个机制来确保Active和Standby这两个NameNode中的数据一致,所以在该系统中还引入了一个QJM模块,全称为Quorum Journal Manager。该模块一般由奇数个结点构成,每个QJM结点对外有一个RPC接口,以供Active NameNode向QJM写入EditLog(操作日志),此时会要求半数以上的QJM都写入成功,才算此次操作成功。Standby的NameNode也会定期从QJM上获取最新的EditLog来更新自身的数据。
每次 NameNode 写 EditLog 的时候,除了向本地磁盘写入 EditLog 之外,也会并行地向 JournalNode 集群之中的每一个 JournalNode 发送写请求,只要大多数 (majority) 的 JournalNode 节点返回成功就认为向 JournalNode 集群写入 EditLog 成功。如果有 2N+1 台 JournalNode,那么根据大多数的原则,最多可以容忍有 N 台 JournalNode 节点挂掉。
这就是:Quorum机制。每次写入JournalNode的机器数目达到大多数(W)时,就认为本次写操作成功了。
这样,每次对Active NameNode中的元数据进行修改时,都会将该修改写入JournalNode集群的大多数机器中,才认为此次修改成功。
当Active NameNode宕机时,StandBy NameNode 向 JournalNode 同步EditLog,从而保证了HA。
Active NameNode 向 JournalNode 集群提交 EditLog 是同步的
但 Standby NameNode 采用的是定时从 JournalNode 集群上同步 EditLog 的方式,那么 Standby NameNode 内存中文件系统镜像有很大的可能是落后于 Active NameNode 的,
所以 Standby NameNode 在转换为 Active NameNode 的时候需要把落后的 EditLog 补上来。
具体的同步过程可参考: Hadoop NameNode 高可用 (High Availability) 实现解析
因为:每个数据块的地址信息不属于“元信息”,并没有保存在 FsImage、CheckPoint...,这是因为地址信息变化比较大。比如说,一台DataNode下线了,其上面的数据块地址信息就全无效了,而且为了达到指定的数据块“复制因子”,还需要在其他机器上复制该数据块。
而快速failover,是指Active NameNode宕机后,StandBy NameNode立即就能提供服务。因此,DataNode也需要实时向 StandBy NameNode 发送 block report
常见容错机制:
failover、failfast、failback、failsafe
1.failover:失效转移
Fail-Over的含义为“失效转移”,是一种备份操作模式,当主要组件异常时,其功能转移到备份组件。其要点在于有主有备,且主故障时备可启用,并设置为主。如Mysql的双Master模式,当正在使用的Master出现故障时,可以拿备Master做主使用
2.failfast:快速失败
从字面含义看就是“快速失败”,尽可能的发现系统中的错误,使系统能够按照事先设定好的错误的流程执行,对应的方式是“fault-tolerant(错误容忍)”。以JAVA集合(Collection)的快速失败为例,当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件。例如:当某一个线程A通过iterator去遍历某集合的过程中,若该集合的内容被其他线程所改变了;那么线程A访问集合时,就会抛出ConcurrentModificationException异常(发现错误执行设定好的错误的流程),产生fail-fast事件。
3.failback:失效自动恢复
Fail-over之后的自动恢复,在簇网络系统(有两台或多台服务器互联的网络)中,由于要某台服务器进行维修,需要网络资源和服务暂时重定向到备用系统。在此之后将网络资源和服务器恢复为由原始主机提供的过程,称为自动恢复
4.failsafe:失效安全
Fail-Safe的含义为“失效安全”,即使在故障的情况下也不会造成伤害或者尽量减少伤害。维基百科上一个形象的例子是红绿灯的“冲突监测模块”当监测到错误或者冲突的信号时会将十字路口的红绿灯变为闪烁错误模式,而不是全部显示为绿灯。
参考文献:
https://blog.csdn.net/lu1005287365/article/details/52678400
http://www.voidcn.com/article/p-krmrvvfh-bnc.html