精华内容
下载资源
问答
  • 数据一致性

    千次阅读 2018-12-13 10:52:37
    在高并发的场景里,如何保证一个业务事务的数据一致性非常重要 在高并发的业务场景中通常出现这样的现象 T1读数据B(sql1) T2读数据B(sql2) T1修改数据B(sql3)(数据库会自动对事务加上排他锁) T2修改...

    在高并发的场景里,如何保证一个业务事务的数据一致性非常重要

    在高并发的业务场景中通常出现这样的现象

    T1读数据B(sql1)

    T2读数据B(sql2)

    T1修改数据B(sql3)(数据库会自动对事务加上排他锁)

    T2修改数据B(sql4)(数据库会自动对事务加上排他锁)

    在这样的场景中通常会出现几种情况:

    1、不加锁的条件下,T2修改产生脏数据。或者当T2修改以查询的结果为条件则修改失败(分布式系统容易导致数据不一致)

    2、在数据库层面(sql1、sql2)加上共享锁时,通常容易致使sql3无法执行,由于sql1、sql2,都对统一数据加上共享锁,但是在修改时都无法对数据加上排他锁导致业务产生死锁情况,导致程序崩溃

    3、对于第二种情况的,解决方案有,在程序业务处理的过程中T1在读取数据的时候手都对数据B加上排他锁,因此程序当业务T2进入处理程序时,也会对数据B加排他锁,但是T1业务还在进行没有是否锁,T2进出便会挂起等待T1的事务进行完成,(但是在分布式的系统中由于一个业务由多服务,多数据库事务组成,无法做到对事物的统一管理)

    4、在单机的业务环境下,通常可以通过代码业务层面解决,高并发产生的数据问题。具体解决方案为对可能产生高并发的业务处理代码快加上程序锁,使其在做业务处理的时候,由多线程的业务转化成单线程的业务

     但是这种情况,仅限于再请求量处理及时的情况下才能使用,一旦有大量的请求涌进,由于单线程的业务处理无法及时,系统拥堵量过大导致系统崩溃,

    5,鉴于4的解决方案在系统处理效率的牺牲比较大不可取,于是可以采用将单线程业务,使用mq机制将所有的请求统一放入一个一队列中,使其业务在处理的时候通过mq的排队机制进行处理,当mq队列过大的时候,可以在处理mq里面的业务时,通过多线程的业务方式提高系统的效率,但对统一数据操作时需要,保证,该数据不存在正在处理的线程(存在并发风险)

    6、使用redis分布式锁,来提高单线程处理效率低下的问题,具体解决方案为。当一个业务处理进来时,程序会对业务处理的主体表数据,获取一个锁,并保存在redis中,(当redis中已存在该数据的锁时,程序会进入一个不断获取锁的机制,直至获取到锁,并获取对数据的操作权限),从而提高系统的处理效率,以及业务数据的一致性

     redis锁实现方式:

    JedisPoolConfig 继承Java通用对象池GenericObjectPool

     

     

     

     

    展开全文
  • 目录 一、分布式事物:本地事务和分布式事务(2PC+3PC)+传统分布式事务的问题 (一)本地事务和分布式事务(2PC+3PC) ...一致性Consistency: 可用性Availability: 分区容错性PartitionToler...

    目录

    一、分布式事物:本地事务和分布式事务(2PC+3PC)+传统分布式事务的问题

    (一)本地事务和分布式事务(2PC+3PC)

    (1)两阶段提交协议2PC

    (2)三阶段提交协议3PC

    (二)对于微服务,传统分布式事务存在的问题

    二、CAP理论和BASE思想

    1.CAP理论

    一致性Consistency:

    可用性Availability:

    分区容错性PartitionTolerance:

    2.BASE思想

    BasicallyAvailiable(基本可用):

    SoftState(软状态):

    EventualConsistency(最终一致性):

    3.CAP理论和BASE思想的关联性

    三、可靠事件模式

    1.基本思路

    (1)用户下单

    (2)交易支付

    (3)订单更新

    2.关键点

    3.解决方案

    4.实现策略

    (1)事件确认组件

    (2)事件恢复组件

    (3)实时消息传递组件

    四、补偿模式

    1.基本思路

    2.关键点

    3.解决方案

    五、Sagas长事务模式--错误管理模式,同时用于控制复杂事务的执行和回滚

    1.基本思路

    2.解决方案

    六、TCC模式

    1.基本思路

    2.解决方案

    3.实现策略

    七、最大努力通知模式

    1.基本思路

    2.解决方案

    八、人工干预模式

    1.基本思路

    2.解决方案

    九、数据一致性模式总结

    参考书籍、文献和资料:


    一、分布式事物:本地事务和分布式事务(2PC+3PC)+传统分布式事务的问题

    (一)本地事务和分布式事务(2PC+3PC)

    传统单体应用一般都会使用一个关系型数据库,好处是使用ACID事务特性,保证数据一致性只需要开启一个事务,然后执行更新操作,最后提交事务或回滚事务。更方便的是可以以借助于Spring等数据访问技术和框架后只需要关注引起数据改变的业务本身即可。

    但是随着组织规模不断扩大、业务量不断增加,单块应用不足以支持庞大业务量和数据量,就需要对服务和数据进行拆分,拆分后就会出现两个或两个以上的数据库的情况,此时不能依靠本地事务得以解决,需要借助于分布式事务来保证一致性,常说的就是保持强一致性的两阶段提交协议和三阶段提交协议。

    (1)两阶段提交协议2PC

    准备阶段:由协调者提议并收集其他节点参与者的反馈(提议的节点为协调者,参与决议的节点为参与者)。

    执行阶段:根据反馈决定提交或中止事务。

    如图,协调者发起一个提议分别询问各参与者是否接受场景,然后协调者根据参与者的反馈,提交或中止事务。

    注意:只有当所有参与者都同意才提交,否则只有有一个不同意就中止。            

    但是,2PC有其固有的三大问题:

    问题一:同步阻塞问题

    执行过程中,所有参与者都是事务阻塞型的。当参与者占用公共资源时,其他第三方节点访问公共资源就不得不处于阻塞状态。

    问题二:单点故障

    一旦协调者发生故障,参与者会一直阻塞下去。特别是在第二阶段,协调者发生故障,则所有参与者还处于锁定资源的状态中,但是无法完成后续的事务操作。

    问题三:数据不一致

    当协调者向参与者发送提交请求后发生了局部网络异常,或者在发送提交请求过程中协调者发生了故障,就会导致只有一部分参与者接收到了提交请求,这部分参与者接到请求后就会执行提交操作,而未接收到提交请求的机器就无法执行事务提交,于是就出现了数据不一致的问题。

    (2)三阶段提交协议3PC

    与2PC相比,3PC主要有两个改动点:在协调者和参与者之间都引入了超时机制+把准备阶段一分为二

    3PC:CanCommit + PreCommit + DoCommit,具体操作如下

    CanCommit阶段:协调者向参与者发送提交请求,参与者如果可以提交就返回Yes响应,否则返回No响应。

    PreCommit阶段:协调者根据参与者的响应情况来决定是否可以进行事务的PreCommit操作。

    如果协调者从所有参与者获得反馈都是Yes响应,那么就执行事务的预执行;

    如果有任何一个参与者向协调者发送了No响应,或者等待超时后没有收到参与者的响应,那么就执行事务的中断。

    DoCommit阶段:执行提交或中断事务。当协调者没有收到参与者发送的ACK响应,就会执行中断事务。

    可见,3PC主要解决了2PC的单点问题和同步阻塞问题。

    (二)对于微服务,传统分布式事务存在的问题

    在微服务架构中,传统分布式事务并不是实现数据一致性的最佳选择,主要有三大问题:

    问题一:对于微服务架构来说,数据访问变得更加复杂,为达到微服务间的松耦合和高度独立,数据是微服务私有的,唯一可访问的方式就是通过API,采用2PC/3PC难度太大。

    问题二:不同的微服务经常使用不同的数据库,但是在微服务架构中,服务会产生不同类型的数据,关系数据库不一定是最佳选择,很多微服务会采用SQL和NoSQL结合模式,如搜索引擎、图数据库等NoSQL数据库大多数都不支持2PC/3PC。

    问题三:当数据被拆分了或者在不同的数据库存在重复数据的时候,锁定资源和序列化数据来保证一致性就会变成一个非常昂贵的操作,会给系统的吞吐量以及扩展性带来巨大的挑战。

    对于微服务架构,建议采用一种更为松散的方式来维护一致性,也就是所谓的最终一致性,对于开发者而言,实现最终一致性的方案可以根据不同的业务场景做不同的选择。

    二、CAP理论和BASE思想

    1.CAP理论

    指的是在一个分布式系统中,无法同时实现一致性Consistency、可用性Availability和分区容错性PartitionTolerance。

    对于典型分布式系统而言,如图所以,这三个概念可以做以下解释:

    一致性Consistency:

    指分布式系统中的所有数据备份在同一时刻是否拥有同样的值。

    可用性Availability:

    指在集群中一部分节点故障后,集群整体是否还能正常响应请求。

    分区容错性PartitionTolerance:

    相当于通信的时限要求,系统如果不能在一定时限内达到数据一致性,就意味着发生了分区情况,也就是说整个分布式系统就不在互联了。

    由于当前网络硬件肯定会出现延迟丢包等通信异常问题,三态性不可避免,所以分区容错性必须实现。

    2.BASE思想

    BASE=BasicallyAvailiable(基本可用)+SoftState(软状态)+EventualConsistency(最终一致性)

    BASE理论是对CAP理论的延伸,基本思想是即使无法做到强一致性,应用可以采用适合的方式达到最终一致性。

    BasicallyAvailiable(基本可用):

    指分布式系统在出现故障的时候,可以损失部分可用性,需要保证和核心可用。服务限流和降级就是其基本表现。

    SoftState(软状态):

    指允许系统存在中间状态,而中间状态不会影响整体可用状态。分布式存储中一般一份数据都有有若干个副本,允许不同节点间副本同步的延时就是软状态的体现。

    EventualConsistency(最终一致性):

    指系统中所有的数据副本经过一定时间后,最终能够达到一致的状态。

    CAP的一致性就是强一致性,这种一致性级别是最符合用户直觉的,用户体验好,但实现起来对系统性能影响较大弱一致性正好相反。BASE中的最终一致性可以看做是弱一致性的一种特殊情况。

    3.CAP理论和BASE思想的关联性

    CAP理论和BASE思想实际上是有关联的,基本关系如图所示:                                                          

    CAP理论和BASE思想讨论的意义在于即使无法做到强一致性,我们也可以采用合适的方式达到最终一致性,这点对微服务架构很重要。最终一致性的方法重要有以下六种分别讲解。

    在微服务架构中,比如,对于一个完整的下单操作而言,订单服务和支付服务都是业务闭环中的一部分,在一个完整的业务操作流程中需要保证各自数据的正确性和一致性。

    三、可靠事件模式

    1.基本思路

    尝试将订单和支付两个微服务进行分别管理,并需要一个媒介用于这两个微服务之间进行数据传递,一般而言,消息中间件MOM适合扮演数据传递媒介的角色。引入消息中间件后下单操作流程可以拆分为如下三个步骤:

    (1)用户下单

    当用户使用订单服务下单时,一方面订单服务需要对所产生的订单数据进行持久化操作,另一方面,也需要同时发送一条创建订单的消息到消息中间件。

    (2)交易支付

    当消息中间件接收到订单创建消息,就会把消息发送到支付服务。

    支付服务接收到订单创建消息后,同样对该消息进行业务处理并持久化。当所有关于支付相关的业务逻辑执行完成以后,支付服务需要向消息中间件发送一条支付成功的消息。

    (3)订单更新

    支付成功消息通过消息中间件传递到订单服务时,订单服务根据支付的结果处理后续业务流程,一般涉及订单状态更新、向用户发送通知内容等内容。

    2.关键点

    对于以上三个闭环管理,仔细分析整个过程,不难发现存在如下三个基本问题:

    (1)某个服务在更新了业务实体后发布消息失败;

    (2)服务发布事件成功,但是消息中间件未能正确推送事件到订阅的服务;

    (3)接受事件的服务重复消费事件。

    针对第三个问题,是最为容易解决的,一般处理方法是由业务代码控制幂等性,例如支付服务传入一个订单时,可以通过判断该订单所对应的唯一ID是否已经处理方式来避免对其再次处理。

    但是前两个问题概括起来就是要解决消息传递的可靠性问题,这个是可靠性事件模式实现数据最终一致性的关键点。要做到可靠消息传递,需要消息中间件确保至少投递一次消息,目前主流的消息中间件都支持消息持久化和至少一次投递的功能。所以,我们需要考虑的是,如何原子性的完成业务操作和发布消息。

    订单服务同时需要处理数据持久化和消息发送两个操作,这就需要考虑两个场景:

    (1)如果数据持久化操作失败,显然消息不该被发送;

    (2)如果数据持久化操作成功,但消息发送失败,那么已经被持久化的数据需要被回滚以还原到初始状态。

    对应这两个场景基本实现流程图如下:

    在逻辑上是可行的,但在运行中,需要考虑很多意想不到的场景,主要有以下两个实际问题:

    (1)实际问题一:典型的依旧是分布式环境下所固有的网络通信异常问题,消息中间件返回通信发生故障,如下图分析:              

    (2)实际问题二:订单服务投递消息后等待消息返回,当消息返回时,订单服务挂了,也会导致数据不一致,如下图分析:

    3.解决方案

    解决上面的问题可以使用一个本地事件表。

    微服务在进行业务操作时需要将业务数据和事件保存在同一个本地事务中,由本地事务保证更新业务和发布事件的原子性。

    发布的事件被保存在本地事务表中,然后该服务实时发布一个事件通知关联的业务服务,如果事件发布成功则立即删除本地事件表中的事件。      

    由于事件消息发布可能会失败或无法获取返回结果,我们需要使用一个额外的“事件恢复”服务来恢复事件,该事件恢复服务定时从事件表中恢复未发布成功的事件并重新发布,只有重新发布才删除保存在本地事件表中的事件。

    注意,事件恢复服务保证了事件一定会被发布,从而确保数据的最终一致性。

    4.实现策略

    在实现上,首先考虑在可靠事件模式中存在一个事件生产者。该事件生产者处于操作的主导地位, 并根据业务操作通过事件的方式发送业务操作的结果(在上例中,订单服务就是事件的生产者),其次,事件消费者是被动方,负责根据事件来处理自身业务逻辑(上例中的支付服务属于事件消费者)。

    有了事件生产者和事件消费者后,我们关注事件服务,事件服务的主要作用就是管理本地事件表,它能存储、确认并发送事件,同时根据不同状态查询事件信息并确定事件已被事件消费者成功消费。事件服务有三大组件:       

    (1)事件确认组件

    表现为一种定时机制,用于处理事件没有被成功发送的场景。

    例如,订单服务在完成业务操作之后需要发送事件到本地事件表,如果这个过程中事件没有发送成功,我们就需要对这些事件重新发送,这个过程为事件确认。

    (2)事件恢复组件

    同样表现为一种定时机制,根据本地事件表中的事件状态,专门处理状态为已确认但还没有成功消费且已超时的事件。

    基本的事件恢复策略就是向消费者重新发送事件,并在消费成功后更新事件状态,并在本地事件表中进行逻辑删除。

    (3)实时消息传递组件

    基于特定的消息中间件工具和框架将事件作为消息进行发送的组件。

    目前可供选择的主流消息中间件包括RabbitMQ、ActiveMQ、RocketMQ、Kafka等。

    四、补偿模式

    1.基本思路

    基本思路在于使用一个额外的补偿服务来协调各个需要保证一致性的微服务,补偿服务按顺序依次调用各个微服务,如果某个微服务调用失败就撤销之前所有已经完成的微服务,补偿服务对需要保证一致性的微服务提供补偿操作。

    举例当中涉及两个微服务,订单微服务和支付微服务,为其提供补偿操作,如果支付服务失败,就需要取消之前的下单服务。

    为降低开发的复杂性和提高效率,补偿服务通常实现为一个通用的补偿框架,补偿框架提供服务编排和自动完成补偿的能力。

    2.关键点

    对于补偿服务而言,所有服务的操作记录是一个关键点,操作记录是执行取消操作的前提。

    举例中,订单服务与支付服务需要保存详细的操作记录和日志,这些日志和记录有助于确定失败的步骤和状态,进而明确需要补偿的范围,然后获取所需补偿的业务数据。

    如果只是订单服务失败,那么只需要补偿一个服务就可以,如果支付服务也失败了,对两个服务进行回滚。

    补偿操作要求业务数据包括支付时的业务流水号、账号和金额。理论上可以根据唯一的业务流水号就能完成补偿操作,但提供更多的数据有益于微服务健壮性。

    3.解决方案

    实现补偿模式的关键要素就是记录完整的业务流水,可以通过业务流水为补偿操作提供需要的业务数据。

    补偿服务可以从业务流水的状态中知道补偿的范围,补偿过程中需要的业务数据同样可以从记录的业务流水中获取。

    补偿服务作为一个服务调用过程同样存在调用不成功的情况,需要通过一定的健壮性机制来保证补偿的成功率,补偿的相关操作本身需要具有幂等性。

    补偿服务健壮性策略:需要根据服务执行失败的原因来选择不同的重试策略,如图所示:                                         

    (1)服务重启:如果失败的原因不是暂时的,而是由业务因素导致的业务错误,需要对问题进行修正后重新执行。

    (2)立即重试:对于网络失败或数据库锁等瞬时异常,重试在很大程度上能够确保任务正常执行。

    (3)定时调用:一般会指定调用的次数上限,如果调用次数达到上限也就不再进行重试。

    如果通过服务重启、立即重试、定时调用等策略依旧不能解决问题,则需要通知相关人员进行处理,即人工干预模式。

    五、Sagas长事务模式--错误管理模式,同时用于控制复杂事务的执行和回滚

    1.基本思路

    长时间持续的事务无法简单地通过一些典型的ACID模型以及使用多段提交配合持有锁的方式来实现。Sagas用于解决这个问题,和多段式分布式事务处理不同,Sagas会将工作分成单独的事务,包含正常额操作和回滚的操作。

    对于开发者而言,不是将所有微服务都定义为分布式的ACID事务,以下单行为为例,将其定义为一个整体,其中包含如何去生成订单以及如何去取消订单,对于支付而言也需要提供同样的逻辑。

    可以将订单和支付服务组合在一起构成一个服务链,然后将整个服务链加密,这样只有该服务链的接收者才能够操控这个服务链。

    当一个服务完成后,会将完成的信息记录到一个集合中,之后可以通过这个集合访问到对应的服务。

    当一个服务失败时,服务本身将本地清理完毕并将消息发送给该集合,从而路由到之前执行成功的服务,然后回滚所有的事务。

    2.解决方案

    在Sagas事务模型中,一个长事务是由一个预定义好执行顺序的子事务集合和他们对应的补偿子事务集合所组成。

    典型的一个完整的交易由T1、T2、......、Tn等多个业务活动组成,每个业务活动可以是本地操作或者是远程操作,而每个业务活动都有对应的取消活动C1、C2、......、Cn。

    所有的业务活动在Sagas事务下要么全部成功,要不全部回滚,不存在中间状态。对于一个Sagas链路而言,各个业务活动执行过程中都会依赖上下文,每个业务活动都是一个原子操作,并提供执行和取消两个入口。

    需要设计一个存储模型来保存执行上下文并通过该存储模型来索引到对应的服务。

    存储模型中包含两个内部结构,一个是完成的任务,一个是等待执行的任务。如果成功就会将任务向前执行,如果失败就向后执行。

    实现上的一种思路可以采用队列和栈数据结构,一方面使用队列来向前执行,另一方面使用栈来向后执行。

    注意,当执行取消操作进行事务操作失败时需要记录失败事务日志,通过重试策略进行重试,对重试失败的执行定时重试,在有问题时则进行人工干预。

    六、TCC模式

    1.基本思路

    一个完整的TCC业务由一个主服务和若干个从服务组成,主服务发起并完成整个业务流程。

    从服务提供三个接口:Try、Confirm、Cancel:

    Try接口:完成所有业务规则检查,预留业务资源。

    Confirm接口:真正执行业务,其自身不做任何业务检查,只使用Try阶段预留的业务资源,同时该操作需要满足幂等性。

    Cancel接口:释放Try阶段预留的业务资源,同样也需要满足幂等性。

    举例来看,订单系统拆分成订单下单和订单支付两个场景,使用TCC模式后执行效果如下:

    (1)Try阶段:尝试执行业务。

    一方面完成所有业务检查,如针对该次订单下单操作,需要验证商品的可用性以及用户账户金额是否够。

    另一方面需要预留业务资源,如把用户账户余额进行冻结用于支付该订单,确保不会出现其他并发进程扣减账户余额导致后续支付无法进行。

    (2)Confirm阶段:执行业务。

    Try阶段一切正常,则执行下单操作并扣除用户账号中的支付金额。

    (3)Cancel阶段:取消执行业务。

    释放Try阶段预留的业务资源,如果Try阶段部分成功,如商品可用且正常下单,但账户余额不够而冻结失败,则需要对产品下单做取消操作,释放被占用的该商品。

    2.解决方案

    TCC服务框架不需要记录详细的业务流水,完成Confirm和Cancel操作的业务由业务服务提供。

    TCC模式同样有两个阶段组成

    第一阶段:

    主业务服务分别调用所有从业务的Try操作,并在活动管理器中登记所有从业务服务。当所有从业务服务的Try操作都调用成功或者某个从业务的Try操作失败,进入第二个阶段。

    第二阶段:

    主业务根据第一阶段的执行结果来执行Comfirm或Cancel操作。

    如果第一阶段所有Try操作都成功,则主业务服务调用所有从业务活动的Confirm操作。

    如果第一阶段中失败,则调用Cancel操作。

    整体上,一个完整的TCC事务参与方包括三个部分:

    (1)主业务服务

    整个业务的发起方,在订单处理场景中,订单应用系统即是主业务服务。

    (2)从业务服务

    负责提供TCC业务操作,是整个业务活动的操作方。

    从业务必须实现Try、Confirm和Cancel三个接口,供主业务服务调用。Confirm和Cancel接口需要具备幂等性,订单的下单服务与支付服务即是从业务服务。

    (3)业务活动管理

    管理控制整个业务活动,包括记录维护TCC全局事务状态和每个从业务服务的子事务状态,并在业务活动提交时确认所有从业务服务Confirm操作,在业务活动取消时调用所有从业务服务的Cancel操作。

    3.实现策略

    在实现TCC模式上,最重要的工作是设计一个稳定的、高可用的、扩展性强的TCC事务管理器。

    在一个跨服务的业务操作中,首先通过Try锁住服务中业务资源进行资源预留,只有资源预留成功了,后续的操作才能正常进行。Confirm操作是在Try之后进行的对Try阶段锁定的资源进行业务操作,Cancel在所有操作失败时用于回滚。

    TCC的操作都需要业务方提供对应的功能,在开发成本上比较高,推介TCC框架有:

    (1)http://github.com/protera/spring-cloud-rest-tcc;

    (2)http://github.com/changmingxie/tcc-transaction;

    (3)http://github.com/liuyangming/ByteTCC;

    (4)http://github.com/QNJR-GROUUP/EasyTransaction;

    (5)http://github.com/yu199195/happylifeplat-tcc;

    (6)https://www.atomikos.om/Blog/TCCForTrasationMangementAcrossMicroservices。

    七、最大努力通知模式

    1.基本思路

    本质上是一种通知类的实现方案。

    基本思路是通知发送方在完成业务处理后向通知接收方发送通知消息。

    当消息接收方没有成功消费消息时,消息的发送方还需要进行重复发送直到消费者消费成功或达到某种发送总之条件。

    消息发送方可以设置复杂的通知规则,利于采用阶梯式事件通知方式。

    通知接收方也可以使用发送方所提供的查询和对账接口获取数据,用于恢复通知失败所导致的业务数据。

    以支付宝为例,通知回调商户提供的回调接口,通过多次通知、查询对账等手段完成交易业务平台间的商户通知。

    2.解决方案

    实现上比较简单,基本系统结构中的通知服务包括三大组件:

    (1)查询组件

    通发送方处理业务并把业务记录保存起来,查询组件提供查询入口供通知接收方主动查询业务数据,避免数据丢失。

    (2)确认组件

    当通知接收方成功接收到通知时,需要与通知发送方确认通知已被正常接收。确认组件接收到确认消息之后就会更新业务记录中的状态,通知组件根据状态就不需要再发送通知。

    (3)通知组件

    通知组件根据业务记录向通知接收方发送通知,在发送通知的过程中需要保存通知记录,并根据业务记录的状态以及现有的通知记录确定下一次发送通知的策略。

    注:最大努力通知模式适合于业务最终一致性的时间敏感度比较低的场景,一般用于类似支付宝与商户集成类跨企业的业务活动。

    八、人工干预模式

    1.基本思路

    严格意义上并不是一种数据一致性的实现机制,当前面讲的各种模式都无法满足需要时,人工干预模式更多的是一种替代方案。

    对于一些重要的业务场景下,由于前面几种模式中因为网络三态性无法解决问题的情况,需要人工干预来保证真正的一致性。常用手段为定期对账等。

    2.解决方案

    实施前提是需要一个后台管理系统,提供操作不一致的基本入口。

    周期性的对账机制需求,对账机制基于业务数据,业务双方根据各自系统内所产生的订单或支付记录,相互对比发现数据不一致的情况,然后通过线下付款等形式形成一致的操作结果。

    九、数据一致性模式总结

    对于金融、支付等业务体系,数据一致性要求极高,需要保证严格的实时一致性要求。

    对于基于社交类的应用场景,可以采用局部实时一致、最终全局一致的实现思路。

    微服务架构里面建议“兜底”思维,即不管实现方案是否完美,最后都要有一个备选方案,备选方案不一定满足日常业务场景,但当出现异常情况时,可通过备选完成正常业务的闭环。

    参考书籍、文献和资料:

    【1】郑天民. 微服务设计原理与架构. 北京:人民邮电出版社,2018.05

    【2】尹吉欢. Spring Cloud微服务全栈技术与案例分析. 北京:机械工业出版社,2018.08

    展开全文
  • 浅析数据一致性

    万次阅读 2016-02-19 15:27:38
    什么是数据一致性?  在数据有多分副本的情况下,如果网络、服务器或者软件出现故障,会导致部分副本写入成功,部分副本写入失败。这就造成各个副本之间的数据不一致,数据内容冲突。 实践中,导致数据不一致的情况...

    欢迎支持笔者新作:《深入理解Kafka:核心设计与实践原理》和《RabbitMQ实战指南》,同时欢迎关注笔者的微信公众号:朱小厮的博客。


    什么是数据一致性?

      在数据有多分副本的情况下,如果网络、服务器或者软件出现故障,会导致部分副本写入成功,部分副本写入失败。这就造成各个副本之间的数据不一致,数据内容冲突。 实践中,导致数据不一致的情况有很多种,表现样式也多种多样,比如数据更新返回操作失败,事实上数据在存储服务器已经更新成功。


    CAP定理

      CAP定理是2000年,由 Eric Brewer 提出来的。Brewer认为在分布式的环境下设计和部署系统时,有3个核心的需求,以一种特殊的关系存在。这里的分布式系统说的是在物理上分布的系统,比如我们常见的web系统。
      这3个核心的需求是:Consistency,Availability和Partition Tolerance,赋予了该理论另外一个名字 - CAP。
      Consistency:一致性,这个和数据库ACID的一致性类似,但这里关注的所有数据节点上的数据一致性和正确性,而数据库的ACID关注的是在在一个事务内,对数据的一些约束。系统在执行过某项操作后仍然处于一致的状态。在分布式系统中,更新操作执行成功后所有的用户都应该读取到最新值。
      Availability:可用性,每一个操作总是能够在一定时间内返回结果。需要注意“一定时间”和“返回结果”。“一定时间”是指,系统结果必须在给定时间内返回。“返回结果”是指系统返回操作成功或失败的结果。
      Partition Tolerance:分区容忍性,是否可以对数据进行分区。这是考虑到性能和可伸缩性。
      CAP定理认为,一个提供数据服务的存储系统无法同事满足数据一致性、数据可用性、分区容忍性。
      为什么不能完全保证这个三点了,个人觉得主要是因为一旦进行分区了,就说明了必须节点之间必须进行通信,涉及到通信,就无法确保在有限的时间内完成指定的行文,如果要求两个操作之间要完整的进行,因为涉及到通信,肯定存在某一个时刻只完成一部分的业务操作,在通信完成的这一段时间内,数据就是不一致性的。如果要求保证一致性,那么就必须在通信完成这一段时间内保护数据,使得任何访问这些数据的操作不可用。
      如果想保证一致性和可用性,那么数据就不能够分区。一个简单的理解就是所有的数据就必须存放在一个数据库里面,不能进行数据库拆分。这个对于大数据量,高并发的互联网应用来说,是不可接受的。
      在大型网站应用中,数据规模总是快速扩张的,因此可伸缩性即分区容忍性必不可少,规模变大以后,机器数量也会变得庞大,这是网络和服务器故障会频繁出现,要想保证应用可用,就必须保证分布式处理系统的高可用性。所以在大型网站中,通常会选择强化分布式存储系统的可用性(A)和伸缩性§,在某种程度上放弃一致性©。一般来说,数据不一致通常出现在系统高并发写操作或者集群状态不稳(故障恢复、集群扩容等)的情况下,应用系统需要对分布式数据处理系统的数据不一致性有所了解并进行某种意义上的补偿和纠错,以避免出现应用系统数据不正确。


    数据一致性模型

      一些分布式系统通过复制数据来提高系统的可靠性和容错性,并且将数据的不同的副本存放在不同的机器,由于维护数据副本的一致性代价高,因此许多系统采用弱一致性来提高性能,一些不同的一致性模型也相继被提出。

    1. 强一致性: 要求无论更新操作实在哪一个副本执行,之后所有的读操作都要能获得最新的数据。
    2. 弱一致性:用户读到某一操作对系统特定数据的更新需要一段时间,我们称这段时间为“不一致性窗口”。
    3. 最终一致性:是弱一致性的一种特例,保证用户最终能够读取到某操作对系统特定数据的更新。

    数据一致性实现技术

    Quorum系统NRW策略

      这个协议有三个关键字N、R、W。

    • N代表数据所具有的副本数。
    • R表示完成读操作所需要读取的最小副本数,即一次读操作所需要参与的最小节点数目。
    • W表示完成写操作所需要写入的最小副本数,即一次写操作所需要参与的最小节点数目。

      该策略中,只需要保证R+W>N,就可以保证强一致性。
      例如:N=3,W=2,R=2,那么表示系统中数据有3个不同的副本,当进行写操作时,需要等待至少有2个副本完成了该写操作系统才会返回执行成功的状态,对于读操作,系统有同样的特性。由于R + W > N,因此该系统是可以保证强一致性的。
      R + W> N会产生类似Quorum的效果。该模型中的读(写)延迟由最慢的R(W)副本决定,有时为了获得较高的性能和较小的延迟,R和W的和可能小于N,这时系统不能保证读操作能获取最新的数据。
      如果R + W > N,那么分布式系统就会提供强一致性的保证,因为读取数据的节点和被同步写入的节点是有重叠的。在关系型数据管理系统中,如果N=2,可以设置为W=2,R=1,这是比较强的一致性约束,写操作的性能比较低,因为系统需要2个节点上的数据都完成更新后才将确认结果返回给用户。
      如果R + W ≤ N,这时读取和写入操作是不重叠的,系统只能保证最终一致性,而副本达到一致的时间则依赖于系统异步更新的实现方式,不一致性的时间段也就等于从更新开始到所有的节点都异步完成更新之间的时间。
    R和W的设置直接影响系统的性能、扩展性与一致性。如果W设置为1,则一个副本完成更改就可以返回给用户,然后通过异步的机制更新剩余的N-W的副本;如果R设置为1,只要有一个副本被读取就可以完成读操作,R和W的值如较小会影响一致性,较大则会影响性能,因此对这两个值的设置需要权衡。

    下面为不同设置的几种特殊情况:
    1. 当W=1,R=N时,系统对写操作有较高的要求,但读操作会比较慢,若N个节点中有节点发生故障,那么读操作将不能完成。
    2. 当R=1,W=N时,系统对读操作有较高性能、高可用,但写操作性能较低,用于需要大量读操作的系统,若N个节点中有节点发生故障,那么些操作将不能完成。
    3. 当R=Q,W=Q(Q=N/2+1)时,系统在读写性能之间取得平衡,兼顾了性能和可用性。

    两阶段提交算法

      在两阶段提交协议中,系统一般包含两类机器(或节点):一类为协调者(coordinator),通常一个系统中只有一个;另一类为事务参与者(participants,cohorts或workers),一般包含多个,在数据存储系统中可以理解为数据副本的个数。两阶段提交协议由两个阶段组成,在正常的执行下,这两个阶段的执行过程如下所述:

    • 阶段1:请求阶段(commit-request phase,或称表决阶段,voting phase)。
      在请求阶段,协调者将通知事务参与者准备提交或取消事务,然后进入表决过程。在表决过程中,参与者将告知协调者自己的决策:同意(事务参与者本地作业执行成功)或取消(本地作业执行故障)。
    • 阶段2:提交阶段(commit phase)。
      在该阶段,协调者将基于第一个阶段的投票结果进行决策:提交或取消。当且仅当所有的参与者同意提交事务协调者才通知所有的参与者提交事务,否则协调者将通知所有的参与者取消事务。参与者在接收到协调者发来的消息后将执行响应的操作。

      举个例子:A组织B、C和D三个人去爬长城:如果所有人都同意去爬长城,那么活动将举行;如果有一人不同意去爬长城,那么活动将取消。用2PC算法解决该问题的过程如下:

    1. 首先A将成为该活动的协调者,B、C和D将成为该活动的参与者。
    2. 阶段1:A发邮件给B、C和D,提出下周三去爬山,问是否同意。那么此时A需要等待B、C和D的邮件。B、C和D分别查看自己的日程安排表。B、C发现自己在当日没有活动安排,则发邮件告诉A它们同意下周三去爬长城。由于某种原因,D白天没有查看邮件。那么此时A、B和C均需要等待。到晚上的时候,D发现了A的邮件,然后查看日程安排,发现周三当天已经有别的安排,那么D回复A说活动取消吧。
    3. 阶段2:此时A收到了所有活动参与者的邮件,并且A发现D下周三不能去爬山。那么A将发邮件通知B、C和D,下周三爬长城活动取消。此时B、C回复A“太可惜了”,D回复A“不好意思”。至此该事务终止。

      两阶段提交算法在分布式系统结合,可实现单用户对文件(对象)多个副本的修改,多副本数据的同步。其结合的原理如下:

    1. 客户端(协调者)向所有的数据副本的存储主机(参与者)发送:修改具体的文件名、偏移量、数据和长度信息,请求修改数据,该消息是1阶段的请求消息。
    2. 存储主机接收到请求后,备份修改前的数据以备回滚,修改文件数据后,向客户端回应修改成功的消息。如果存储主机由于某些原因(磁盘损坏、空间不足等)不能修改数据,回应修改失败的消息。
    3. 客户端接收发送出去的每一个消息回应,如果存储主机全部回应都修改成功,向每存储主机发送确认修改的提交消息;如果存在存储主机回应修改失败,或者超时未回应,客户端向所有存储主机发送取消修改的提交消息。该消息是2阶段的提交消息。
    4. 存储主机接收到客户端的提交消息,如果是确认修改,则直接回应该提交OK消息;如果是取消修改,则将修改数据还原为修改前,然后回应取消修改OK的消息。
    5. 客户端接收全部存储主机的回应,整个操作成功。

      在该过程中可能存在通信失败,例如网络中断、主机宕机等诸多的原因,对于未在算法中定义的其它异常,都认为是提交失败,都需要回滚,这是该算法基于确定的通信回复实现的,在参与者的确定回复(无论是回复失败还是回复成功)之上执行逻辑处理,符合确定性的条件当然能够获得确定性的结果哲学原理。
      缺点:单个A是个严重问题:没有热备机制,A节点宕机了或者链接它的网络坏了会阻塞该事务;吞吐量不行,没有充分发动更多A的力量,一旦某个A第一阶段投了赞成票就得在它上面加独占锁,其他事务不得接入,直到当前事务提交or回滚。

    分布式锁服务

      分布式锁是对数据被外界修改持保守态度,在整个数据处理过程中将数据处于锁定状态,在用户修改数据的同时,其它用户不允许修改。
      采用分布式锁服务实现数据一致性,是在操作目标之前先获取操作许可,然后再执行操作,如果其他用户同时尝试操作该目标将被阻止,直到前一个用户释放许可后,其他用户才能够操作目标。分析这个过程,如果只有一个用户操作目标,没有多个用户并发冲突,也申请了操作许可,造成了由于申请操作许可所带来的资源使用消耗,浪费网络通信和增加了延时。
      采用分布式锁实现多副本内容修改的一致性问题, 选择控制内容颗粒度实现申请锁服务。例如我们要保证一个文件的多个副本修改一致, 可以对整个文件修改设置一把锁,修改时申请锁,修改这个文件的多个副本,确保多个副本修改的一致,修改完成后释放锁;也可以对文件分段,或者是文件中的单个字节设置锁, 实现更细颗粒度的锁操作,减少冲突。
      常用的锁实现算法有Lamport bakery algorithm (俗称面包店算法), 还有Paxos算法以及乐观锁。下面对其原理做简单概述。

    1. Lamport面包店算法

      是解决多个线程并发访问一个共享的单用户资源的互斥问题的算法。 由Leslie Lamport(英语:Leslie Lamport)发明。
      这个算法也可以称为时间戳策略,或者叫做Lamport逻辑时钟。
      这里先陈述一下这个逻辑时钟的内容:
      我们用分布式系统中的事件的先后关系,用“->”符号来表示,例如:若事件a发生在事件b之前,那么a->b.
      该关系需要满足下列三个条件:

    1. 如果a和b是同一进程中的事件,a在b之前发生,则a->b
    2. 如果事件a是消息发送方,b是接收方,则a->b
    3. 对于事件a、b、c,如果有a->b,b->c,则有a->c

      注意,对于任何一个事件a,a -> a都是不成立的,也就是说,关系->是反自反的。有了上面的定义,我们也可以定义出“并发”(concurrent)的概念了:

    对于事件a、b,如果a -> b,b -> a两个都不成立,那么a和b就是并发的。

      直观上,上面的->关系非常好理解,即“xxx在xxx之前发生”。也就是说,一个系统在输入I1下,如果有a->b,那么对于这个系统的同一个输入I1,无论重复运行多少次,a也始终发生在b之前;如果在输入I1下a和b是并发的,则表示在同一个输入I1下的不同运行中,a可能在b之前,也可能在b之后,也可能恰好同时发生。也就是,并发并不是指一定同时发生,而是表示一种不确定性。->和并发的概念,就是我们理解一个系统时最基础的概念之一了。
      有了上面的概念,我们可以给系统引入时钟了。这里的时钟就是lamport逻辑时钟。一个时钟,本质上是一个事件到实数(假设时间是连续的)的函数。这个函数将每个事件映射到一个数字,代表这个事件发生的时间。形式一点来说,对于每个进程Pi,都有一个时钟Ci,这个时钟将该进程中的事件a映射到Ci(a)。而整个系统的时钟C=< C0, C1, …, Cn>,对于一个事件b,假设b属于进程Pj,那么C(b) =Cj(b)。

      这里插一句,从这个定义也可以看到大师对分布式系统的理解。分布式系统中不存在一个“全局”的实体。在该系统中,每个进程都是一个相对独立的实体,它们有自己的本地信息(本地Knowledge)。而整个系统的信息则是各个进程的信息的一个聚合。
      有了时钟的一个“本质定义”还不够,我们需要考虑,什么样的时钟是一个有意义的,或者说正确的时钟。其实,有了前文的->关系的定义,正确的时钟应满足的条件已经十分明显了:
      时钟条件:对于任意两个事件a,b,如果a -> b,那么C(a) < C(b)。
      注意,反过来讲这个条件可不成立。如果我们要求反过来也成立,即“如果a -> b为假,那么C(a) < C(b)也为假”,那就等于要求并发事件必须同时发生,这显然是不合理的。
      结合前文->关系的定义,我们可以把上面的条件细化成如下两条:

    1. 如果a和b是进程Pi中的两个事件,并且在Pi中,a在b之前发生,那么Ci(a) < Ci(b);
    2. 如果a是Pi发送消息m,b是Pj接收消息m,那么Ci(a) < Cj(b);

      上面就定义了合理的逻辑时钟。显然,一个系统可以有无数个合理的逻辑时钟。实现逻辑时钟也相对简单,只要遵守两条实现规则就可以了:

    1. 每个进程Pi在自己的任何两个连续的事件之间增加Ci值;
    2. 如果事件a是Pi发送消息m,那么在m中应该带上时间戳Tm=Ci(a);如果b是进程Pj接收到消息m,那么,进程Pj应该设置Cj为大于max(Tm,Cj(b))。

      有了上面逻辑时钟的定义,我们现在可以为一个系统中所有的事件排一个全序,就是使用事件发生时的逻辑时钟读数进行排序,读数小的在先。当然,此时可能会存在两个事件同时发生的情况。如果要去除这种情况,方法也非常简单:如果a在进程Pi中,b在进程Pj中,Ci(a) = Cj(b)且i < j,那么a在b之前。形式化一点,我们可以把系统事件E上的全序关系“=>”定义为:
      假设a是Pi中的事件,b是Pj中的事件,那么:a => b当且仅当以下两个条件之一成立:

    1. Ci(a) < Cj(b);
    2. Ci(a) = Cj(b) 且 i < j;

      Lamport把上面这些数理逻辑时钟的概念以非常直观地类比为顾客去面包店采购。面包店只能接待一位顾客的采购。已知有n位顾客要进入面包店采购,安排他们按照次序在前台登记一个签到号码。该签到号码逐次加1。根据签到号码的由小到大的顺序依次入店购货。完成购买的顾客在前台把其签到号码归0. 如果完成购买的顾客要再次进店购买,就必须重新排队。
      这个类比中的顾客就相当于线程,而入店购货就是进入临界区独占访问该共享资源。由于计算机实现的特点,存在两个线程获得相同的签到号码的情况,这是因为两个线程几乎同时申请排队的签到号码,读取已经发出去的签到号码情况,这两个线程读到的数据是完全一样的,然后各自在读到的数据上找到最大值,再加1作为自己的排队签到号码。为此,该算法规定如果两个线程的排队签到号码相等,则线程id号较小的具有优先权。
      把该算法原理与分布式系统相结合,即可实现分步锁。
      注意这个系统中需要引入时钟同步,博主的意见是可以采用SNTP实现时钟同步(非权威,仅供参考)。

    2.Paxos算法

      该算法比较热门,类似2pc算法的升级版,在此不做赘述,可以自行搜索相关资料。(博主会在之后整理列出)
      需要注意的是这个算法也是Leslie Lamport提出的,由此可见这位大师之牛逼!
      Paxos算法解决的问题是一个分布式系统如何就某个值(决议)达成一致。一个典型的场景是,在一个分布式数据库系统中,如果各节点的初始状态一致,每个节点都执行相同的操作序列,那么他们最后能得到一个一致的状态。为保证每个节点执行相同的命令序列,需要在每一条指令上执行一个“一致性算法”以保证每个节点看到的指令一致。一个通用的一致性算法可以应用在许多场景中,是分布式计算中的重要问题。节点通信存在两种模型:共享内存(Shared memory)和消息传递(Messages passing)。Paxos算法就是一种基于消息传递模型的一致性算法。BigTable使用一个分布式数据锁服务Chubby,而Chubby使用Paxos算法来保证备份的一致性。
      不仅只用在分布式系统,凡是多个过程需要达成某种一致性的都可以用到Paxos 算法。一致性方法可以通过共享内存(需要锁)或者消息传递实现,Paxos 算法采用的是后者。下面是Paxos 算法适用的几种情况:一台机器中多个进程/线程达成数据一致;分布式文件系统或者分布式数据库中多客户端并发读写数据;分布式存储中多个副本响应读写请求的一致性。

    3. 采用乐观锁原理实现的同步

      我们举个例子说明该算法的实现原理。如一个金融系统,当某个操作员读取用户的数据,并在读出的用户数据的基础上进行修改时(如更改用户帐户余额),如果采用前面的分布式锁服务机制,也就意味着整个操作过程中(从操作员读出数据、开始修改直至提交修改结果的全过程,甚至还包括操作员中途去煮咖啡的时间),数据库记录始终处于加锁状态,可以想见,如果面对几百上千个并发,这样的情况将导致怎样的后果。
      乐观锁机制在一定程度上解决了这个问题。乐观锁,大多是基于数据版本( Version)记录机制实现。何谓数据版本?即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个 “version” 字段来实现。读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。
      对于上面修改用户帐户信息的例子而言,假设数据库中帐户信息表中有一个 version 字段,当前值为 1 ;而当前帐户余额字段( balance )为 $100 。

    1. 操作员 A 此时将其读出(version=1 ),并从其帐户余额中扣除 $50($100-$50 )。
    2. 在操作员 A 操作的过程中,操作员B也读入此用户信息( version=1 ),并从其帐户余额中扣除 $20 ( $100-$20 )。
    3. 操作员 A 完成了修改工作,将数据版本号加一( version=2 ),连同帐户扣除后余额( balance=$50),提交至数据库更新,此时由于提交数据版本大于数据库记录当前版本,数据被更新,数据库记录 version 更新为 2 。
    4. 操作员 B 完成了操作,也将版本号加一( version=2 )试图向数据库提交数据( balance=$80),但此时比对数据库记录版本时发现,操作员 B 提交的数据版本号为 2 ,数据库记录当前版本也为 2 ,不满足 “提交版本必须大于记录当前版本才能执行更新 “ 的乐观锁策略,因此,操作员 B 的提交被驳回。这样,就避免了操作员 B 用基于version=1 的旧数据修改的结果覆盖操作员A 的操作结果的可能。

    欢迎支持笔者新作:《深入理解Kafka:核心设计与实践原理》和《RabbitMQ实战指南》,同时欢迎关注笔者的微信公众号:朱小厮的博客。


    展开全文
  • 在分布式技术发展下,数据一致性的解决方法和技术也在不断的演进,本文就以分布式数据库作为案例,介绍分布式数据库数据一致性的原理以及实际实现。 1、数据一致性 1.1 数据一致性是什么 大部份使用传统关系型数据库...

    前言

    分布式数据库的数据一致性管理是其最重要的内核技术之一,也是保证分布式数据库满足数据库最基本的ACID特性中的 “一致性”(Consistency)的保障。在分布式技术发展下,数据一致性的解决方法和技术也在不断的演进,本文就以分布式数据库作为案例,介绍分布式数据库数据一致性的原理以及实际实现。

    1、数据一致性

    1.1 数据一致性是什么

    大部份使用传统关系型数据库的DBA在看到“数据一致性”时,第一反应可能都是数据在跨表事务中的数据一致性场景。但是本文介绍的“数据一致性”,指的是**“数据在多份副本中存储时,如何保障数据的一致性”**场景。

    由于在大数据领域,数据的安全不再由硬件来保证,而是通过软件手段,通过同时将数据写入到多个副本中,来确保数据的安全。数据库在同时向多个副本写入记录时,如何确保每个副本数据一致,称为“数据一致性”。

    1.2 关系型数据库如何保障数据一致性

    传统的关系型数据库对于运行环境–硬件要求都比较高,例如Oracle会建议用户使用小型机+共享存储作为数据库的运行环境,DB2 DPF也同样建议用户采用更好的服务器+高端存储来搭建数据库的运行环境。所以在数据存储安全的技术要求下,传统关系型数据库更多是依赖硬件的技术来保障数据的安全性。

    在这里插入图片描述
    因为关系型数据库的数据安全是基于硬件来保障,并且数据也不会通过同时存储多份来保障数据的安全,所以关系型数据库的用户默认认为数据存储是一致的。

    1.3 分布式存储如何保障数据一致性

    本文在讨论分布式存储时,主要指的是大数据产品中的分布式文件系统和分布式数据库,例如:SequoiaDB和HDFS。

    用户在搞明白分布式存储的数据一致性原理时,必须要先明白为什么他们就需要数据一致性,和分布式存储的数据存储与关系型数据库的数据存储又有什么区别。

    大数据技术的诞生,确确实实让系统的性能有新的突破,并且支持硬件以水平扩展的方式来获得线性增长的性能和存储。这些都是过去传统关系型数据库所无法提供的。另外,大数据技术也抛弃了运行环境必须足够好的硬性要求,而是允许用户通过批量廉价X86服务器+本地磁盘的方式搭建规模集群,从而获得比过去依赖硬件垂直扩展所提供的更强的计算能力和更多的存储空间。

    大数据技术的核心思想就是分布式,将一个大的工作任务分解成多个小任务,然后通过分布式并发操作的方式将其完成,从而提高整个系统的计算效率或者是存储能力。而在分布式环境下,由于硬件的要求降低,必然需要大数据产品提供另外一个重要的功能–数据安全

    在这里插入图片描述
    大数据产品在解决数据安全的方式上,都比较接近,简单来说,就是让一份数据通过异步或者同步的方式保存在多台机器上,从而保障数据的安全。

    分布式存储在解决数据安全的技术难点后,又引入了一个新的技术问题,就是如何保障多个副本中的数据一致性。目前SequoiaDB是使用Raft算法来保证数据在多个副本中一致性。

    2、Raft算法

    2.1 Raft算法背景

    在分布式环境下,最著名的一致性算法应该是Paxos算法,但是由于它实在过于晦涩难懂,并且实现起来极度困难,所以在2013年,Diego Ongaro、John Ousterhout两个人以易懂(Understandability)为目标设计了一套一致性算法Raft。Raft算法最大的特点在于简单易懂,并且实现起来简单

    2.2 Raft算法概述

    与Paxos不同,Raft强调的是易懂,Raft和Paxos一样只要保证n/2+1节点正常就能够提供服务。

    众所周知当问题较为复杂时可以把问题分解为几个小问题来处理,Raft也使用了分而治之的思想。Raft算法重点解决三个子问题:选举(Leader election)、日志复制(Log replication)、安全性(Safety)。

    Raft算法强化了Leader节点的功能,Follower节点的数据只能够从Leader中获取,所以Follower节点的实现就变得简单,只要负责和Leader保持通信,并且接受Leader推送的数据即可。

    2.3 Raft算法原理

    2.3.1 节点角色

    Raft算法中,对节点的状态分为3种角色,分别是Leader(领导者)、Follower(追随者)和Candidate(候选者)。

    Leader,负责处理来自客户端的请求,负责将日志同步到Follower中,并且保证与Follower之间的heartBeat联系;

    Follower,当集群刚刚启动时,所有节点均为Follower状态,它的工作主要为响应Leader的日志同步请求,响应Candidate的请求,以及把请求到Follower的事务请求转发给Leader;

    Candidate,选举Leader时负责投票,选举出来Leader后,节点将从Candidate状态变为Leader状态。

    在这里插入图片描述

    2.3.2 Terms

    在分布式环境下,“时间同步”一直都是老大难的技术难题。Raft为了解决这个问题,将时间划分为一个一个的Term(可以理解为“逻辑时间”)来处理在不同时间段里的数据一致性。

    Terms有以下原则

    • 每个Term中,至多存在一个Leader
    • 某些Term中,有可能存在由于选举失败,没有Leader的情况
    • 每个节点自己维护本地的currentTerm
    • 每个Term都是一个连续递增的编号
    • 如果Follower的Term编号比别的Follower Term编号小时,该Follower Term编号将更新Term编号,以保持与其他Follower Term编号一致

    2.3.3 选举

    Raft的选举由定时器触发,每个节点的触发时间都不相同。

    所有的节点在开始时状态都为Follower,当定时器触发选举后Term编号递增,该节点的状态由Follower转为Candidate,并且向其他节点发起RequestVote RPC请求,这时选举有3种情况可能发生:

    发起RequestVote的节点收到n/2+1(过半数)个节点的投票,该节点将从Candidate状态变为Leader状态,开始向其他节点发送HeartBeat以保持Leader的正常状态

    如果收到投票请求后,该节点发现发起投票的节点Term大于自己,则该节点状态从Candidate转为Follower,否则保持Candidate状态,并且拒绝该投票请求

    选举期间发生了超时,则Term编号递增,重新发起选举
    在这里插入图片描述

    2.3.4 日志复制

    日志复制主要的作用就是用来保证节点的数据一致性与高可用性。

    当Leader被选举出来后,所有的事务操作都必须要经过Leader处理。这些事务操作成功后,将会被按顺序写入到LOG中,每个LOG都包含一个index编号。

    Leader在LOG发生变化后,通过HeartBeat将新的LOG同步到Follower上,Follower在接收到LOG后,再向Leader发送ACK信息,当Leader接到大多数(2/n+1)Follower的ACK信息后,将该LOG设置为已提交,并且Leader将LOG追加到本地磁盘中。

    同时Leader将在下一个HeartBeat中,通知所有的Follower将该LOG存储在各自的本地磁盘中。

    2.3.5 安全性

    安全性是用于确保每个节点都是按照相同的日志序列进行执行的安全机制。

    如果当某个Follower在同步Leader的日志时失败,但是未来该Follower又可能被选举为Leader时,就有可能导致前一个Leader已经commit的日志发生覆盖,这样就导致了节点执行不同序列的日志。

    Raft的安全性就是用于保证选举出来的Leader一定包含先前已经commit LOG 的机制,主要遵循的原则如下:

    每个Term 只能选举一个Leader;

    Leader的日志完整性,则当Candidate重新选举Leader时,新的Leader必须要包含先前已经commit的LOG;

    Candidate在选举新的Leader时,使用Term来保证LOG的完整性;

    3、分布式数据库数据一致性技术实现

    以国产原厂的分布式数据库SequoiaDB为例,SequoiaDB在多副本的部署中,采用Raft算法保证数据在多副本环境中保持一致。

    SequoiaDB集群中,总共包含3中角色节点,分别是协调节点、编目节点和数据节点。由于协调节点本身不存任何数据,所以只有编目节点和数据节点存在事务操作,换言之,编目分区组和数据分区组的副本同步采用Raft算法保证数据一致性。

    在这里插入图片描述

    3.1 编目节点和数据节点的事务日志介绍

    编目节点和数据节点由于都是需要存储数据的,并且在集群部署中该,为了确保数据的安全,都是建议采用分布式的方式进行部署,所以在数据同步中,需要采用Raft算法的基本原理进行数据同步。

    编目节点和数据节点在存储数据时,共包含两大部分,一个真实的数据文件,另一个是事务日志文件。
    在这里插入图片描述
    SequoiaDB的节点事务日志,默认情况下由20个64MB(总大小为1.25GB)的文件构成。节点的事务日志主要包含一个index编号和数据操作内容,index编号保持永远递增状态。

    另外,SequoiaDB节点的事务日志不会永久保存,而是当所有的事务日志写满后,再重新从第一个文件开始进行覆盖写入。

    3.2 编目分区组的数据一致性

    由于编目分区组是保存SequoiaDB集群的元信息,数据同步要求高,所以编目分区组的数据一致性要求为强一致性,即每次向编目分区组执行事务操作时,必须要确保所有的编目节点操作成功,才计算该操作执行成功,否则该事务操作将在整个编目分区组中回退事务日志,以保证分区组内的数据一致性。

    另外,编目分区组还有一个比较重要的特性,即编目分区组必须要存在主节点才能够正常工作,如果老的主节点宕机了,编目分区组暂时没有主节点,则该编目分区组不能够对外提供任何事务操作和数据查询操作。

    3.3 数据分区组的数据一致性

    数据分区组的数据一致性默认情况下为最终一致性性,即只要求主节点执行事务操作成功即视为操作成功,主节点将在未来异步同步ReplicaLOG到从节点上。

    3.4 主从节点的事务日志同步

    SequoiaDB的主从节点是通过事务日志同步来保证数据一致性的,并且主从节点的事务日志同步是单线程完成。

    如果当主节点和从节点的LSN差距为一条记录,则主节点会主动将最新的事务日志推送给从节点。

    如果主节点和从节点的LSN差距超过一条记录,则从节点会主动向主节点请求同步事务日志,主节点收到同步请求后,会将从节点的LSN号到主节点最新的LSN号对应的事务日志打包一次性发送给从节点。

    3.5 从节点日志重放

    当从节点获取到主节点推送过来的事务日志后,就会自动解析事务日志和重放。从节点在重放事务日志时,默认情况下会以10并发来重放事务日志。

    从节点在执行并发重放日志时有条件限制,即在集合的唯一索引个数<=1的情况下,INSERT、DELETE、UPDATE、LOB WRITE、LOBUPDATE、LOB REMOVE操作可以支持并发重放事务日志。从节点在做并发重放时,是通过记录的OID进行打散并发执行,这样就可以保证对相同记录的操作不会由于并发重放导致数据不一致。

    但是用户需要注意,从节点在重放事务日志时, DROP CL操作不能够支持并发重放。

    4、SequoiaDB数据一致性应用

    目前SequoiaDB数据分区组的数据一致性是基于集合级别进行配置的。用户在使用SequoiaDB过程中,可以随时调整数据一致性的强度。

    4.1 创建集合时指定

    在一个多副本的SequoiaDB集群中,集合默认的数据一致性行级别为“最终一致性”。用户可以在创建集合时显式指定该集合的“数据一致性强度”,例如可以在SequoiaDB Shell中执行以下命令

    db.CSNAME.createCL(“CLNAME”,{ReplSize:3})

    ReplSize参数填写范围
    在这里插入图片描述

    4.2 修改已经存在的集合

    如果集合在创建时没有设置“数据一致性”ReplSize参数,用户也可以对已经存在的集合进行修改,在SequoiaDB Shell修改命令如下

    db.CSNAME.CLNAME.alter({ReplSize:3})

    ReplSize的取值范围和创建集合时一致。

    4.3 如何查看集合的ReplSize参数

    如果用户希望检查当前集合的RepliSize参数值,可以通过数据库快照进行查看,在SequoiaDB Shell查看命令如下

    db.snapshot(SDB_SNAP_CATALOG,{}, {“Name”:null, “IsMainCL”:null,“MainCLName”:null, “ReplSize”:null})
    打印信息如下

    {

    “MainCLName”:“test.main2”,

    “Name”: “foo.bar2”,

    “IsMainCL”: null,

    “ReplSize”: null

    }

    {

    “IsMainCL”: true,

    “Name”: “test.main2”,

    “MainCLName”: null,

    “ReplSize”: null

    }

    {

    “Name”: “foo.tt”,

    “ReplSize”: 3,

    “IsMainCL”: null,

    “MainCLName”: null

    }

    5、总结

    分布式的数据库,通过Raft算法来确保在分布式情况上数据的一致性,并且编目分区组和数据分区组对数据一致性要求又有所不同,编目分区组始终要求的是数据在多副本请情况下数据强一致性,而数据分区组则可以由用户在创建集合时来执行数据一致性的强度,强度越高,数据安全性越好,但是执行的效率就会相对较差,反之依然。

    目前SequoiaDB在数据一致性场景上,用户的调整空间较大,可以根据不同的业务要求来调整数据一致性的强度,以满足业务或追求性能最优,或者数据最安全的技术要求。

    展开全文
  • ZooKeeper数据一致性

    千次阅读 2017-11-30 10:42:04
    ZooKeeper为存储的数据提供了一致性保证,不管应用从哪个...ZooKeeper的数据一致性保障ZooKeeper是高性能、可扩展的,为应用提供了以下的数据一致性保障: 1)顺序一致性 来自客户端的更新将严格按照客户端发送
  • 分布式下的数据一致性问题

    万次阅读 多人点赞 2020-07-31 14:03:53
    分布式下的数据一致性问题 CAP原则 在分布式系统要满足CAP原则,一个提供数据服务的存储系统无法同时满足:数据一致性、数据可用性、分区耐受性。 C数据一致性:所有应用程序都能访问到相同的数据。 A数据可用性:...
  • redis数据一致性

    万次阅读 2019-01-15 17:19:13
    前言:所谓的redis数据一致性即当进行修改或者保存、删除之后,redis中的数据也应该进行相应变化,不然用户再次查询的时候很可能查询出已经删除过的脏数据。 一、缓存一致的必要性 还是接上篇来说,我们已经解决了...
  • 如何保证微服务下的数据一致性?

    万次阅读 2020-09-15 10:50:35
    3、实现微服务下数据一致性的方式 3.1 可靠事件通知模式 3.1.1 同步事件 3.1.2 异步事件 3.1.2.1 本地事件服务 3.1.2.2 外部事件服务 3.1.2.3 可靠事件通知模式的注意事项 3.2 最大努力通知模式 3.3 业务补偿模式 ...
  • Redis集群的数据一致性

    万次阅读 2019-04-11 16:54:54
    Redis集群的数据一致性 Redis 集群没有使用一致性hash, 而是引入了哈希槽的概念。 Reds 集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽.集群的每个节点负责一部分hash槽。这种结构很容易...
  • 数据一致性处理

    2019-03-25 17:26:39
    数据一致性处理 当多个进程同时操作同一个数据,会产生资源争抢,数据一致性的问题。 高并发情况下,涉及到写操作时,不可能直接操作数据库,大并发的连接会导致mysql请求会阻塞,比如大量的insert update 请求...
  • 微服务架构下数据一致性的保证

    千人学习 2016-08-08 11:14:27
    传统应用使用本地事务和分布式事务保证数据一致性,但是在微服务架构中数据都是服务私有的,需要通过服务提供的api访问,分布式事务不再适用微服务架构。那么微服务架构又该怎么保证数据一致性呢?
  • redis主从保证数据一致性

    万次阅读 2020-03-11 11:00:04
    redis主从保证数据一致性 前言 在redis中为了保证redis的高可用,一般会搭建一种集群模式就是主从模式。 主从模式可以保证redis的高可用,那么redis是怎么保证主从服务器的数据一致性的,接下来我们浅谈下redis主...
  • 浅谈数据一致性

    千次阅读 2020-10-31 08:00:00
    浅谈数据一致性|0x00 数据不一致产生的原因互联网的工程开发,与传统软件相比,往往要面临非常复杂多变的业务场景,这是老生常谈的问题了。虽然在工程开发与协同领域已经有了比较多的实践案例,...
  • 数据一致性设计理念

    千次阅读 2018-07-17 13:20:11
    但是此做法也引来了数据一致性的问题。为了解决数据一致性的问题,业界常用的有CAP、ACID、BASE等理论模型。 CAP原则 CAP是对强一致性(Consistency)、可用性(Availability)、分区容忍性(Partition ...
  • 数据仓库之数据一致性

    千次阅读 2017-01-18 15:42:45
    数据仓库之数据一致性 不同阶段获取同样的指标,但是输出的数据不同,无法保持所有数据的一致性情况 栗子:注册用户数: 是在公司表中存在,且公司名称不为空的数据。 存在问题:在一月份注册数据10条,填写公司...
  • 沃趣科技罗小波:MySQL数据一致性

    千人学习 2016-08-17 14:10:53
    该视频教程深度讲解MySQL主备数据一致性:1、MySQL复制发展史简介,及其复制原理2、MySQL 崩溃恢复安全性,包括:如何使用sync_binlog,innodb_flush_log_at_trx_commit来保证3、MySQL崩溃时不丢失数据,如何使用...
  • ZooKeeper自身数据一致性

    千次阅读 2018-10-07 11:02:07
    文章目录0 前言1 ZK数据一致性1.1 ZK选举1.2 ZK原子广播机制(ZAB协议)1.2.1 广播模式1.2.2 恢复模式1.2.3 Paxos与Zab一致性对比1.3 ZK数据同步机制1.3.1 同步准备1.3.2 同步初始化1.3.3 数据同步场景ZK...
  • ZooKeeper 如何保证数据一致性

    千次阅读 2020-08-30 16:51:25
    ZooKeeper 提供了一个类似于 Linux 文件系统的数据模型,和基于 Watcher 机制的分布式事件通知,这些特性都依赖 ZooKeeper 的高容错数据一致性协议。 那么问题来了,在分布式场景下,ZooKeeper 是如何实现数据一致性...
  • 数据一致性模型

    2014-04-08 16:03:57
    数据一致性模型主要有以下几种
  • 细说分布式下的数据一致性

    千次阅读 2018-08-29 11:21:47
    细说分布式下的数据一致性 名词解释 强一致性 最终一致性 XA事物 JDBC事物、JTA事物 TCC 产生场景 单一数据库、单一系统无法支撑业务和数据的增长而出现拆分化的演进,数据存储于不同的事物管理单元但又要...
  • redis系列之数据库与缓存数据一致性解决方案

    万次阅读 多人点赞 2017-09-03 17:09:58
    redis系列之数据库与缓存数据一致性解决方案  -- 来自中华石杉老师视频 数据库与缓存读写模式策略 写完数据库后是否需要马上更新缓存还是直接删除缓存? (1)、如果写数据库的值与更新到缓存值是一样的,不...
  • 数据一致性实现技术

    千次阅读 2013-04-22 09:36:40
    数据一致性实现技术 分布式存储在不同的节点的数据采取什么技术保证一致性,取决于应用对于系统一致性的需求,在关系型数据管理系统中一般会采用悲观的方法(如加锁),这些方法代价比较高,对系统性能也有较大影响...
  • 带上你的问题来学redis:什么是redis?redis缓存穿透?redis缓存雪崩?redis缓存击穿?如何处理? 还有:redis与数据库之间的数据一致性问题!
  • 在数据集成中经常被提及的...而DataPipeline平台采用的Kafka Connect框架是如何保证数据一致性的? DataPipeline数据一致性示例 DataPipeline平台对于数据一致性的保证是通过Kafka Connect中内嵌的Offset管理机制,...
  • oracle数据一致性和事务控制

    千次阅读 2017-09-17 15:05:11
    oracle数据一致性和事务控制 1.数据一致性 数据一致性是指数据库中的数据每时每刻都是稳定且可靠的,而事务控制就是保证数据一致性的。 2.事务控制 事务命令 commit rollback savepoint roll back ...
  • 数据完整性和数据一致性

    千次阅读 2011-03-11 16:40:49
    数据一致性是指关联数据之间的逻辑关系是否正确和完整。问题可以理解为应用程序自己认为的数据状态与最终写入到磁盘中的数据状态是否一致。比如一个事务操作,实际发出了五个写操作,当系统把前面三个写操作的数据...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 61,492
精华内容 24,596
关键字:

数据一致性