写点什么

分布式下,我想要一致性

发布于: 2020 年 10 月 21 日
分布式下,我想要一致性

每一个程序员架构梦的实现之路上,总是绕不开分布式系统CAP这个理论的学习。如果你还没有了解过,可以翻看之前的文章,相信我,如果你想成为架构师,这个理论无论是在工作中还是面试中吹水,你始终都是需要的

晦涩难懂的CAP

CAP理论作为分布式的重要理论基础,指出了在分布式环境下,其实只有AP和CP两种模型去选择。BASE理论作为CAP理论的一个延伸,主张牺牲一致性去换取可用性。反之,一个分布式系统也可以去牺牲可用性去换取一致性。

分布式事务



说到一致性就不能不提事务,事务这个词现在经常用于数据库,但是有一点要注意,站在一定的角度,事务并非只适用于数据库。如果站在数据库角度非要下一个定义的话:



事务(Transaction),一般是指要做的或所做的事情。在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。事务通常由高级数据库操纵语言或编程语言(如SQL,C++或Java)书写的用户程序的执行所引起,并用形如begin transaction和end transaction语句(或函数调用)来界定。事务由事务开始(begin transaction)和事务结束(end transaction)之间执行的全体操作组成。



为了更好的认识并实现事务,抽象出了ACID理论,换句话说,如果一个系统能实现ACID特性,那么就实现了事务特性。



  • Atomic原子性: 一个事务的所有系列操作步骤被看成是一个动作,所有的步骤要么全部完成要么一个也不会完成。

  • Consistent一致性:事物完成时,必须所有数据保持一致状态

  • Isolated隔离性:主要用于实现并发控制, 隔离能够确保并发执行的事务能够顺序一个接一个执行,通过隔离,一个未完成事务不会影响另外一个未完成事务。

  • Durable持久性:持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响



在单机上实现ACID并不困难,通常可以利用锁、时间序列或者顺序日志等机制来保证。这里多说一句,一般而言,隔离性可以利用锁机制来实现,而原子性、一致性和持久性都可以利用日志来实现,一致性算法Raft也是通过日志形式来保证一致性的。



但是在分布式环境下,网络环境却比单机要复杂的多,原因在于网络通信的不可靠性,处于不同网络的多个节点要想保证一致性,网络延迟、网络故障等因素都需要考虑。

二阶段提交协议



二阶段(2PC)提交协议是根据业界首个分布式事务标准规范X/OpenDTP提出的,顾名思义,它通过两个阶段来协商一个提交操作。



X/OpenDTP设计了一个模型来描述分布式事务的各个角色以及规范:



  1. AP:用户程序,负责触发分布式事务,在这个过程中采用了特殊的事务指令(XA指令),这些指令由TM接管并发送给相关的RM去执行

  2. RM:资源管理器,一般指数据库,每个RM只执行自己相关的指令。映射到程序级别如:ODBC,ADO.Net,JDBC等。

  3. TM:事务管理器或者说是事务协调者,它负责接收AP发起的指令,调度和协调参与事务的所有RM,确保事务正常完成或者回滚。



二阶段提交协议最早用来实现数据库的分布式事务,现在大部分数据库的分布式事务都采用了XA协议。在二阶段提交协议中,一个事务的提交过程被分为两个过程:



  1. 通知阶段。在这个阶段会通知所有参与事务的资源管理器(RM)进行资源的预留和其他准备工作,其中包括了持久化日志文件和资源的锁定。所以这个阶段在整个事务过程中占据了大部分时间,准备完成并把结果返回给事务管理器(TM)。

  2. 提交阶段。在该阶段,事务管理器(TM)会根据上一步的结果来决定是提交还是回滚操作。仅当全部资源管理器(RM)都同意提交的时候,事务管理器(TM)才通知所有的资源管理器(RM)正式提交事务,否则TM将会通知所有的RM取消事务。



二阶段协议的精髓在于,它通过两个阶段来把不可靠事务提交失败的几率降低到了最小,在一个真正的二阶段提交事务的过程中,第一阶段其实占据了整个事务的大部分时间,而真正提交事务的第二阶段几乎是瞬间完成的,所以这正是二阶段的巧妙之处。



但是现实中我们很少使用二阶段提交协议来保证事务性,为什么呢?



  1. 在现实场景中,很少有强一致性的业务,最常用的是基于BASE理论的最终一致性

  2. 二阶段提交协议需要锁定资源,在性能上会有一定损失,这在高并发的场景中是不适合的。

  3. 二阶段提交协议引入了事务管理器(TM),增加了系统的复杂性,而且多数开发人员并不精通TM以及RM的技能。

TCC

由于二阶段提交协议的一系列缺陷,TCC被引入分布式事务。TCC是Try(预留)、Confirm(确认)、Cancel(撤销)3个操作的简称,它包含了预留、确认或撤销这2个阶段。TCC针对分布式事务大体过程是这样的:



  1. 预留阶段:事务发起者分别向所有参与事务的业务方发起请求,要求预留业务资源,业务方并给予回复。

  2. 提交阶段:事务发起者收到每个参与事务的业务方的回复,如果都是ok,则通知每个业务方提交事务操作,如果至少有一个业务方返回结果非ok,则通知所有的业务方撤销事务操作。





TCC本质上是补偿事务,从操作上就可以看出来,每个业务方针对当前事务需要注册三个操作:预留操作,确认操作,取消操作。这三个操作是需要参与事务的每个业务来编码实现的。对应到编码层面,每个业务方都需要提供三个操作的接口,为了一致性,确认操作和取消操作必须是幂等的,因为这两个操作可能会系统性的重试或者人为干预的重试。

写在最后



TCC在操作上更像是一种编程模型,它主要针对业务层面,所以它在性能上要比主要针对数据库层面的二阶段提交要高很多。目前二阶段提交协议主要的应用场景还是在数据库上,所以它本质上使用的是数据库的锁机制,这也是在高并发的互联网应用中很少使用二阶段提交协议的重要原因之一。



说了那么多,在真实的业务场景中,如果能用单机数据库的事务来代替分布式事务,那就首选单机数据库事务。如果业务允许放弃强一致性,那就采用最终一致性原则来保证一致性,而最终一致性最常用的解决方案是利用可靠的MQ消息,这个有时间我们详聊。



更多精彩文章





发布于: 2020 年 10 月 21 日阅读数: 2010
用户头像

一个坚持把架构做到极致的技术人 2014.10.19 加入

还未添加个人简介

评论 (1 条评论)

发布
用户头像
写了跟没写一样
2020 年 10 月 29 日 20:33
回复
没有更多了
分布式下,我想要一致性