带你完整复习数据库分布式事务的相关知识
Posted 南淮北安
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了带你完整复习数据库分布式事务的相关知识相关的知识,希望对你有一定的参考价值。
分布式和集群的区别:
小饭店原来只有一个厨师,切菜洗菜备料炒菜全干。后来客人多了,厨房一个厨师忙不过来,又请了个厨师,两个厨师都能炒一样的菜,这两个厨师的关系是集群。 为了让厨师专心炒菜,把菜做到极致,又请了个配菜师负责切菜,备菜,备料,厨师和配菜师的关系是分布式,一个配菜师也忙不过来了,又请了个配菜师,两个配 菜师关系是集群
文章目录
随着互联网的快速发展,软件系统由原来的单体应用转变为分布式应用,下图描述了单体应用向微服务的演变:
分布式系统会把一个应用系统拆分为可独立部署的多个服务,因此需要服务与服务之间远程协作才能完成事务操作,这种分布式系统环境下由不同的服务之间通过网络远程协作完成事务称之为分布式事务,例如用户注册送积分事务、创建订单减库存事务,银行转账事务等都是分布式事务。
一、CAP
CAP原则又称CAP定理,指的是在一个分布式系统中,线性一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance)三者不可兼得。
- 一致性:在分布式系统的所有数据备份中,在同一时刻是否有同样的值(等同于所有节点都访问同一份最新的数据副本)。
- 可用性:在集群中一部分节点发生故障后,集群整体能否响应客户端的读写请求(对数据更新具备高可用性)
简单来说就是什么时候访问都能获取到正常的数据值,不会出现操作失败或者访问超时等用户体验不好的情况
- 分区容错性:系统如果不能在时限内达成数据的一致性,就意味着发生了分区,必须就当前操作在C和A之间做出选择。以实际效果而言,分区相当于对通信的时限要求。
分区容错性一般不会被舍弃,因为舍弃了就意味着不是分布式了,因为分布式的思想就是把功能分开,部署到不同的机器上
CAP 理论出现故障时只能满足两个不能同时满足三个,比如A访问1号服务器,B访问2号服务器,A将1号服务器上的值value由10改成了9,正常情况下,1号服务器需要通知2号服务器执行对应的修改保证一致性。
但是这个时候出现了网络故障,此时如果要满足一致性,那么B就需要阻塞等待网络恢复读取最新的值,也就是违背了可用性
如果要满足可用性,也就是,B正常读取,由于网络故障2号服务器未收到修改命令,所以B读取的是一个旧值,就违背了一致性
事实证明,大多数都是牺牲了一致性。像12306还有淘宝网,就好比是你买火车票,本来你看到的是还有一张票,其实在这个时刻已经被买走了,你填好了信息准备买的时候发现系统提示你没票了。这就是牺牲了一致性。
但是不是说牺牲一致性一定是最好的。就好比mysql中的事务机制,张三给李四转了100块钱,这时候必须保证张三的账户上少了100,李四的账户多了100。因此需要数据的一致性,而且什么时候转钱都可以,也需要可用性。但是可以转钱失败是可以允许的。
二、两阶段提交协议
分布式事务指涉及操作多个数据库的事务,在分布式系统中,各个节点之间在物理上相互独立,通过网络进行沟通和协调。
二阶段提交(Two-Phase Commit)指在计算机网络及数据库领域内,为了使分布式数据库的所有节点在进行事务提交时都保持一致性而设计的一种算法。在分布式系统中,每个节点虽然都可以知道自己的操作是否成功,却无法知道其他节点的操作是否成功。
在一个事务跨越多个节点时,为了保持事务的ACID特性,需要引入一个作为协调者的组件来统一掌控所有节点(称作参与者)的操作结果,并最终指示这些节点是否真正提交操作结果(比如将更新后的数据写入磁盘等)。
因此,二阶段提交的算法思路可以概括为:参与者将操作成败通知协调者,再由协调者根据所有参与者的反馈决定各参与者是提交操作还是中止操作。
1. Prepare(准备阶段)
事务协调者(事务管理器)给每个参与者(源管理器)都发送Prepare消息,每个参与者要么直接返回失败(如权限验证失败),要么在本地执行事务,写本地的redo和undo日志但不提交,是一种“万事俱备,只欠东风”的状态。
2. Commit(提交阶段)
如果协调者接收到了参与者的失败消息或者超时,则直接给每个参与者都发送回滚消息,否则发送提交消息,参与者根据协调者的指令执行提交或者回滚操作,释放在所有事务处理过程中使用的锁资源,如图7-8所示。
3. 两阶段提交的缺点
(1)性能问题:无论是第一阶段还是第二阶段,所有的参与者和协调者资源都是被锁住的,只有当所有节点准备完毕,协调者才会通知提交。整个过程中,参与者进行本地事务提交之后才会释放资源。过程比较漫长,对性能影响比较大。
(2)单节点故障:所有请求都需要经过协调者,在协调者发生故障时,所有参与者都会被阻塞。(随着协调者挂点会重新选举一个新的协调者,但是协调者挂掉会导致所有的参与者进入阻塞,就算新选举了新的协调者也无法解决参与者的阻塞)
其中单节点故障具体可以分为:
a. 协调者正常,参与者宕机:由于协调者无法收集所有参与者的反馈,协调者会进入阻塞
解决办法:引入超时机制,如果参与者在指定的时间内还没有反馈认定失败
b. 协调者宕机,参与者正常:由于协调者宕机,无法发送提交请求,所有处于执行了操作但是未提交状态的参与者都会陷入阻塞情况.
解决方案:引入协调者备份,同时协调者需记录操作日志.当检测到协调者宕机一段时间后,协调者备份取代协调者,并读取操作日志,向所有参与者询问状态
c. 协调者和参与者都宕机
-
发生在第一阶段: 因为第一阶段,所有参与者都没有真正执行commit,所以只需重新在剩余的参与者中重新选出一个协调者,新的协调者在重新执行第一阶段和第二阶段就可以了。
-
发生在第二阶段:并且 挂了的参与者在挂掉之前没有收到协调者的指令。也就是上面的第4步挂了,这是可能协调者还没有发送第4步就挂了。这种情形下,新的协调者重新执行第一阶段和第二阶段操作。
-
发生在第二阶段 并且 有部分参与者已经执行完commit操作。就好比这里订单服务A和支付服务B都收到协调者 发送的commit信息,开始真正执行本地事务commit,但突发情况,Acommit成功,B确挂了。这个时候目前来讲数据是不一致的。虽然这个时候可以再通过手段让他和协调者通信,再想办法把数据搞成一致的,但是,这段时间内他的数据状态已经是不一致的了! 2PC 无法解决这个问题。
三、三阶段提交协议
三阶段提交(Three-Phase Commit),也叫作三阶段提交协议(Three-PhaseCommit Protocol),是二阶段提交(2PC)的改进版本。具体改进如下。
- 引入超时机制:在协调者和参与者中引入超时机制,如果协调者长时间接收不到参与者的反馈,则认为参与者执行失败。
- 在第1阶段和第2阶段都加入一个预准备阶段,以保证在最后的任务提交之前各参与节点的状态是一致的。
也就是说,除了引入超时机制,三阶段提交协议(3PC)把两阶段提交协议(2PC)的准备阶段再次一分为二,这样三阶段提交就有CanCommit、PreCommit、DoCommit三个阶段。
1. CanCommit阶段
协调者向参与者发送Commit请求,参与者如果可以提交就返回Yes响应,否则返回No响应。
2. PreCommit阶段
协调者根据参与者的反应来决定是否继续进行,有以下两种可能。
- 假如协调者从所有参与者那里获得的反馈都是Yes响应,就预执行事务。
- 假如有任意参与者向协调者发送了No响应,或者在等待超时之后协调者都没有接收到参与者的响应,则执行事务的中断。
3. DoCommit阶段
该阶段进行真正的事务提交,主要包括:协调者发送提交请求,参与者提交事务,参与者响应反馈(在事务提交完之后向协调者发送Ack响应),协调者确定完成事务,如图7-9所示。
超时机制解决了2PC过程中,如果参与者宕机,协调者一直阻塞的问题,超时之后会认定参与者反对(同理协调者宕机,参与者一直阻塞)
新增一个准备阶段,保证了最后提交阶段之前参与者的状态都是一致的
四、分布式事务
传统事务:遵循ACID原则,即原子性、一致性、隔离性和持久性。
1. 柔性事务
在分布式数据库领域,基于CAP理论及BASE理论,阿里巴巴提出了柔性事务的概念。BASE理论是CAP理论的延伸,包括基本可用(Basically Available)、柔性状态(Soft State)、最终一致性(Eventual Consistency)三个原则,并基于这三个原则设计出了柔性事务。
我们通常所说的柔性事务分为:两阶段型、补偿型、异步确保型、最大努力通知型
两阶段型事务指分布式事务的两阶段提交,对应技术上的XA和JTA/JTS,是分布式环境下事务处理的典型模式。
2. TCC型事务
TCC型事务(Try、Confirm、Cancel)为补偿型事务,是一种基于补偿的事务处理模型。如图7-10所示,服务器A发起事务,服务器B参与事务,如果服务器A的事务和服务器B的事务都顺利执行完成并提交,则整个事务执行完成。但是,如果事务B执行失败,事务B本身就回滚,这时事务A已被提交,所以需要执行一个补偿操作,将已经提交的事务A执行的操作进行反操作,恢复到未执行前事务A的状态。需要注意的是,发起提交的一般是主业务服务,而状态补偿的一般是业务活动管理者,因为活动日志被存储在业务活动管理中,补偿需要依靠日志进行恢复。TCC事务模型牺牲了一定的隔离性和一致性,但是提高了事务的可用性。
3. 异步确保型事务
异步确保型事务指将一系列同步的事务操作修改为基于消息队列异步执行的操作,来避免分布式事务中同步阻塞带来的数据操作性能下降。如图7-11所示,在写业务数据A触发后将执行以下流程。
(1)业务A的模块在数据库A上执行数据更新操作。
(2)业务A调用写消息数据模块。
(3)写消息日志模块将数据库的写操作状态写入数据库A中。
(4)写消息日志模块将写操作日志发送给消息服务器。
(5)读消息日志模块接收操作日志。
(6)读消息数据调用写业务B的模块。
(7)写业务B更新数据到数据库B。
(8)写业务数据B的模块发送异步消息更新数据库A中的写消息日志状态,说明自己已经完成了异步数据更新操作。
4. 最大努力通知型事务
最大努力通知型事务也是通过消息中间件实现的,与前面异步确保型操作不同的是:在消息由MQ服务器发送到消费者之后,允许在达到最大重试次数之后正常结束事务,因此无法保障数据的最终一致性。
如图7-12所示,写业务数据A在更新数据库后调用写消息日志将数据操作以异步消息的形式发送给读消息日志模块;读消息日志模块在接收到数据操作后调用写业务B写数据库。和异步确保型不同的是,数据库B在写完之后将不再通知写状态到数据库A,如果因为网络或其他原因,在如图7-12所示的第4步没有接收到消息,则消息服务器将不断重试发送消息到读消息日志,如果经过N次重试后读消息日志还是没有接收到日志,则消息不再发送,这时会出现数据库A和数据库B数据不一致的情况。
以上是关于带你完整复习数据库分布式事务的相关知识的主要内容,如果未能解决你的问题,请参考以下文章