在分布式系统中,为了保证数据的高可用,一般会将数据保留多个副本(replica),这些副本会放置在不一样的物理的机器上。html
1.什么是数据一致性git
在数据有多份副本的状况下,若是网络、服务器或者软件出现故障,会致使部分副本写入成功,部分副本写入失败。这就形成各个副本之间的数据不一致,数据内容冲突。github
形成事实上的数据不一致。算法
2.CAP定理数据库
CAP理论认为在分布式的环境下设计和部署系统时,有3个核心的需求:服务器
Consistency,Availability和Partition Tolerance,即CAP。
Consistency:一致性,这个和数据库ACID的一致性相似,但这里关注的全部数据节点上的数据一致性和正确性,而数据库的ACID关注的是在在一个事务内,对数据的一些约束。系统在执行过某项操做后仍然处于一致的状态。在分布式系统中,更新操做执行成功后全部的用户都应该读取到最新值。网络
Availability:可用性,每个操做老是可以在必定时间内返回结果。须要注意“必定时间”和“返回结果”。“必定时间”是指,系统结果必须在给定时间内返回。“返回结果”是指系统返回操做成功或失败的结果。架构
Partition Tolerance:分区容忍性,是否能够对数据进行分区。这是考虑到性能和可伸缩性。分布式
3.数据一致性模型微服务
一些分布式系统经过复制数据来提升系统的可靠性和容错性,而且将数据的不一样的副本存放在不一样的机器。
强一致性:
当更新操做完成以后,任何多个后续进程或者线程的访问都会返回最新的更新过的值。这种是对用户最友好的,就是用户上一次写什么,下一次就保证能读到什么。根据 CAP 理论,这种实现须要牺牲可用性。
弱一致性:
系统并不保证续进程或者线程的访问都会返回最新的更新过的值。用户读到某一操做对系统特定数据的更新须要一段时间,咱们称这段时间为“不一致性窗口”。系统在数据写入成功以后,不承诺当即能够读到最新写入的值,也不会具体的承诺多久以后能够读到。
最终一致性:
是弱一致性的一种特例。系统保证在没有后续更新的前提下,系统最终返回上一次更新操做的值。在没有故障发生的前提下,不一致窗口的时间主要受通讯延迟,系统负载和复制副本的个数影响。DNS 是一个典型的最终一致性系统。
跨行转帐问题是一个典型的分布式事务,用户A向B的一个转帐1000,要进行A的余额-1000,B的余额+1000,显然必须保证这两个操做的事务性。
相似的还有,电商系统中,当有用户下单后,除了在订单表插入记,还要在商品表更新库存等,特别是随着微服务架构的流行,分布式事务的场景更变得更广泛。
两阶段提交协议是协调全部分布式原子事务参与者,并决定提交或取消(回滚)的分布式算法。
1.协议参与者
在两阶段提交协议中,系统通常包含两类机器(或节点):一类为协调者(coordinator),一般一个系统中只有一个;另外一类为事务参与者(participants,cohorts或workers),通常包含多个,在数据存储系统中能够理解为数据副本的个数。协议中假设每一个节点都会记录写前日志(write-ahead log)并持久性存储,即便节点发生故障日志也不会丢失。协议中同时假设节点不会发生永久性故障并且任意两个节点均可以互相通讯。
2.两个阶段的执行
1.请求阶段(commit-request phase,或称表决阶段,voting phase)
在请求阶段,协调者将通知事务参与者准备提交或取消事务,而后进入表决过程。
在表决过程当中,参与者将告知协调者本身的决策:赞成(事务参与者本地做业执行成功)或取消(本地做业执行故障)。
2.提交阶段(commit phase)
在该阶段,协调者将基于第一个阶段的投票结果进行决策:提交或取消。
当且仅当全部的参与者赞成提交事务协调者才通知全部的参与者提交事务,不然协调者将通知全部的参与者取消事务。
参与者在接收到协调者发来的消息后将执行响应的操做。
(3)两阶段提交的缺点
1.同步阻塞问题。执行过程当中,全部参与节点都是事务阻塞型的。
当参与者占有公共资源时,其余第三方节点访问公共资源不得不处于阻塞状态。
2.单点故障。因为协调者的重要性,一旦协调者发生故障。
参与者会一直阻塞下去。尤为在第二阶段,协调者发生故障,那么全部的参与者还都处于锁定事务资源的状态中,而没法继续完成事务操做。(若是是协调者挂掉,能够从新选举一个协调者,可是没法解决由于协调者宕机致使的参与者处于阻塞状态的问题)
3.数据不一致。在二阶段提交的阶段二中,当协调者向参与者发送commit请求以后,发生了局部网络异常或者在发送commit请求过程当中协调者发生了故障,这回致使只有一部分参与者接受到了commit请求。
而在这部分参与者接到commit请求以后就会执行commit操做。可是其余部分未接到commit请求的机器则没法执行事务提交。因而整个分布式系统便出现了数据部一致性的现象。
(4)两阶段提交没法解决的问题
当协调者出错,同时参与者也出错时,两阶段没法保证事务执行的完整性。
考虑协调者再发出commit消息以后宕机,而惟一接收到这条消息的参与者同时也宕机了。
那么即便协调者经过选举协议产生了新的协调者,这条事务的状态也是不肯定的,没人知道事务是否被已经提交。
三阶段提交协议在协调者和参与者中都引入超时机制,而且把两阶段提交协议的第一个阶段拆分红了两步:询问,而后再锁资源,最后真正提交。
(1)三个阶段的执行
1.CanCommit阶段
3PC的CanCommit阶段其实和2PC的准备阶段很像。
协调者向参与者发送commit请求,参与者若是能够提交就返回Yes响应,不然返回No响应。
2.PreCommit阶段
Coordinator根据Cohort的反应状况来决定是否能够继续事务的PreCommit操做。
根据响应状况,有如下两种可能。
A.假如Coordinator从全部的Cohort得到的反馈都是Yes响应,那么就会进行事务的预执行:
发送预提交请求。Coordinator向Cohort发送PreCommit请求,并进入Prepared阶段。
事务预提交。Cohort接收到PreCommit请求后,会执行事务操做,并将undo和redo信息记录到事务日志中。
响应反馈。若是Cohort成功的执行了事务操做,则返回ACK响应,同时开始等待最终指令。
B.假若有任何一个Cohort向Coordinator发送了No响应,或者等待超时以后,Coordinator都没有接到Cohort的响应,那么就中断事务:
发送中断请求。Coordinator向全部Cohort发送abort请求。
中断事务。Cohort收到来自Coordinator的abort请求以后(或超时以后,仍未收到Cohort的请求),执行事务的中断。
3.DoCommit阶段
该阶段进行真正的事务提交,也能够分为如下两种状况:
执行提交
A.发送提交请求。Coordinator接收到Cohort发送的ACK响应,那么他将从预提交状态进入到提交状态。并向全部Cohort发送doCommit请求。
B.事务提交。Cohort接收到doCommit请求以后,执行正式的事务提交。并在完成事务提交以后释放全部事务资源。
C.响应反馈。事务提交完以后,向Coordinator发送ACK响应。
D.完成事务。Coordinator接收到全部Cohort的ACK响应以后,完成事务。
中断事务
Coordinator没有接收到Cohort发送的ACK响应(多是接受者发送的不是ACK响应,也可能响应超时),那么就会执行中断事务。
(2)三阶段提交协议和两阶段提交协议的不一样
对于协调者(Coordinator)和参与者(Cohort)都设置了超时机制(在2PC中,只有协调者拥有超时机制,即若是在必定时间内没有收到cohort的消息则默认失败)。
在2PC的准备阶段和提交阶段之间,插入预提交阶段,使3PC拥有CanCommit、PreCommit、DoCommit三个阶段。
PreCommit是一个缓冲,保证了在最后提交阶段以前各参与节点的状态是一致的。
(2)三阶段提交协议的缺点
若是进入PreCommit后,Coordinator发出的是abort请求,假设只有一个Cohort收到并进行了abort操做,
而其余对于系统状态未知的Cohort会根据3PC选择继续Commit,此时系统状态发生不一致性。
参考资料: