专栏首页TechFlow分布式初探——分布式事务与两阶段提交协议

分布式初探——分布式事务与两阶段提交协议

点击上方蓝字,和我一起学技术。

今天的文章咱们聊的是分布式原理当中的原子性,也称为分布式事务。不知道会不会有人觉得奇怪,分布式系统CAP原则当中并没有原子性,这个原子性是从哪里冒出来的?

其实并不奇怪,之前我们在介绍各种一致性原则的时候,虽然没有明确提出来,但是原子性的相关内容已经隐藏在其中了。让我们回顾一下,分布式系统当中的一致性简单可以分为强一致性和弱一致性。强一致性很好理解,简单可以理解成主节点每次更新都通过同步的方式,同步更新所有副本。而弱一致性则是统称所有不满足强一致性的模型,可以简单理解成通过异步更新的方式实现的一致性模型

想象一下更新的时候,有节点出错的情况。如果是强一致性,很好办,因为我们采用同步更新,所以更新失败的话,主节点立刻就能感知。要么重试这次的更新,要么回滚放弃,或者是判断该从库是否已经宕机,将它移除资源池

如果是异步的更新机制就麻烦了,因为没有一个统筹大局的主库了。没有节点知道其他节点更新成功了没有,如果部分成功了,部分失败了,那么数据的一致性就完全没办法保障了,脏数据到处都是,这个系统也就没法用了。所以必须要保证即使是异步更新,也要做到原子性,要么所有节点一起更新成功,要么一起失败回滚,不允许出现一部分成功了,另一部分没有的情况。

那么怎么解决这个问题呢?这就需要用到两阶段提交协议了。

两阶段提交

两阶段提交协议的算法思路其实不难,非常直观,很好理解。前文当中说了,之所以会出现部分节点更新成功,而部分节点更新失败的情况,主要原因就是因为没有一个节点统筹全局,所以没办法做出整体决策导致的。既然如此,那么解决的策略也很简单,我们加上一个整体协调的节点即可。整个协调节点会首先分发任务到各个节点,各个节点收到任务成功之后,由协调节点统一指挥,决定执行任务还是回滚。这个思路和战争非常相似,一个将军坐镇中央,各个小分队散落四方。将军统一指挥,各分队进退有素。

我们整理一下整个过程,可以将它分成两个阶段,分别是表决阶段提交阶段

我们来看上面这张图,首先1号节点是协调节点,可以理解成将军节点,其他节点都是小兵节点。首先表决阶段也可以理解成任务分发阶段,由将军节点将本次更新的内容分发给各个小兵节点。小兵节点收到任务之后,如果当前情况允许执行该任务,则回复可以执行,否则回复无法执行。

第二个阶段是提交阶段,也就是执行阶段。由将军节点搜集各个小兵节点的回复消息,只要有一个小兵节点回复无法执行,那么说明这次的任务无法执行,所以将军节点会再次发送消息给各个小兵节点,告知任务取消,此时各个小兵节点删除此次任务。否则,将军节点会发送执行任务的命令,各个小兵节点各自执行任务。

这个就是整个两阶段提交协议的内容,是不是非常直观,非常好理解?

我们接着深入其中的细节,试着画出将军节点和小兵节点状态的状态机。

这是将军节点的状态机,一共只有四种状态。一开始是init状态,表示初始化,也就是分发任务之前的状态。当它给各个小兵节点分发任务之后,转变到等待状态,线程挂起等待,等待各个小兵节点的回复。等所有回复到齐了之后,决定是否执行任务,如果执行则转移到执行状态,否则转移到取消状态

小兵节点的状态机也类似,小兵节点初始化之后等待将军发放任务的消息。如果小兵判断当前任务无法执行,那么会直接报告将军节点并跳转到取消状态。否则会进入就绪状态,表示当前节点可以执行任务,但是要等将军的指挥。接着,根据将军的命令决定执行任务或者是取消回滚。

上面这两个状态机应该也很好理解,但是这当中有一个小问题。我们观察一下将军节点和小兵节点的状态机,会发现两者当中都存在挂起等待的状态。将军节点做决策之前需要等待所有小兵的回复,而小兵节点执行之前也同样需要等待将军的命令。我们不禁想问一个问题,如果消息传递的过程当中存在丢包应该怎么办?如果更严重一些,某个小兵节点或者是将军节点宕机了呢,应该怎么办?

以现在的模式显然是无法解决的,我们需要加入优化。

这里的优化针对将军节点和小兵节点需要做区分,两者的策略是不同的。我们一个一个来看。

首先是将军节点,如果小兵节点宕机或者是发送的消息丢包,那么将军节点将会无法决策,或者会长久等待。为了解决这个问题,我们需要加上超时机制。如果超时还没有搜集齐应答,那么可以判定是有小兵发生故障或者是网络出现问题,则自动视作任务失败,广播任务回滚的消息。

其次是小兵节点,如果小兵节点在初始状态超时,则自动在本地终止任务,并且发放任务无法执行的消息给将军节点。如果是在等待将军决定的时候超时,这个时候不能简单的终止任务,因为无法判断其他节点有没有执行任务,如果简单地终止,那么就会引起数据不一致。针对这个问题的策略稍稍不同,既不是继续等待,也不是去询问将军节点,而是引入互询机制,也就是说当前小兵去询问其他小兵

因为小兵的状态只有那么几种,为了方便区分,我们给小兵起个名字。发起询问的小兵称为A,被询问的小兵称为B。我们列举一下,总共只有四种可能。

B的状态是success,说明B已经收到将军节点的命令执行了任务。那么很简单,不管是什么原因导致A没有收到消息都没有关系,A直接也执行任务即可。

B的状态是fail,说明将军已经取消了任务,但是A没有收到。那么也很上面一样,A直接放弃任务执行即可。

B的状态是init,说明B一直还在等待任务发放,而A已经领到了任务并且在等待将军决策了。一般来说这种情况由两种可能,一种可能是B和将军的通信出现了问题,所以迟迟收不到将军的消息,还有一种可能是将军自己崩溃了。如果将军崩溃了,那当然没的说,任务肯定不能继续了,如果将军还在,那么它肯定还在等待B的消息,由于将军有超时机制,所以最终仍然会超时导致取消任务。所以A把任务置为失败是正确的。

B的状态也是等待,说明B这个节点也不清楚状况,这种情况比较麻烦,只能再换一个节点询问。那问题来了,如果所有节点都处于这个状态呢,应该怎么办?显然在当前的设计里,无法避免这个问题,会导致长久的等待

所有节点在执行任务的时候,需要实时将状态和消息写入本地log当中,这样如果节点宕机了,根据log日志也可以恢复之前的状态。

三阶段提交

针对上文当中说的二阶段提交的那个问题,大数据专家提出了解决方案,就是在执行阶段再细分成两个阶段,也就是预执行状态执行状态。因为多了一个阶段,所以也称为三阶段提交。

我们来看三阶段提交中将军节点的状态机:

和之前的相比差别不大,只不过多了一个预执行的节点。我们再看小兵节点:

小兵节点也一样,相当于多接受将军节点一次消息,第一次收到执行消息之后转到预执行状态之后,其实节点就会执行任务了,只是不会提交任务和释放资源,只有进入执行结束的状态之后才会进行这一操作。

用数据库里的事务打个比方,在进入预执行状态之后,节点就会执行事务,但是不会提交事务已经执行完成的状态,也就是说依然可以回滚。所以在预执行之后,如果收到将军的消息需要取消任务,那么小兵节点依然会回滚,进入状态失败。

和两阶段提交相比,在三阶段提交当中,如果小兵在等待的状态超时,那么会直接进入任务取消状态,不会再询问其他节点。如果小兵执行成功会返回将军ACK,如果失败则不会返回。将军如果没有收到所有小兵的成功消息,则会通知所有小兵回滚。如果在预执行状态等待将军消息超时,那么会直接进入执行成功状态,只有收到将军消息取消任务,才会回滚。

显然三阶段提交也不是完美的,虽然降低了阻塞等待的可能性,但是如果小兵节点没有收到将军发来的回滚消息,那么也会产生数据不一致。并且三阶段提交的时间开销要比二阶段提交大得多,加上二阶段提交出现阻塞的概率非常低,因此绝大多数分布式协议当中还是用的二阶段提交

二阶段提交的协议在分布式系统当中广泛使用,并且它非常直观,推导的过程也很有意思,状态机的应用也很巧妙。因此推荐大家都能深入思考,理解一下其中的精髓。

今天的文章就到这里,如果觉得有所收获,请顺手点个在看或者转发吧,你们的支持是我最大的动力。

本文分享自微信公众号 - TechFlow(techflow2019),作者:梁唐

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-01-31

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 每日一题 | 解一个复杂的方程

    拜占庭将军问题是由著名的计算机大神图灵奖获得者兰伯特提出来的非常有名的问题,我们把这个问题外面包着的故事背景去除,实际上的内涵其实围绕的是分布式系统当中的一致性...

    TechFlow-承志
  • 动态规划入门——动态规划与数据结构的结合,在树上做DP

    之前的几篇文章当中一直在聊背包问题,不知道大家有没有觉得有些腻味了。虽然经典的文章当中背包一共有九讲,但除了竞赛选手,我们能理解到单调优化就已经非常出色了。像是...

    TechFlow-承志
  • 一点微小的改动,让你从B树理解到B+树

    首先和大家道个歉,昨天晚上由于我的失误,发文忘了改标题,引发了一些疑惑。昨天文章的标题应该是“快速求解方程的根——二分法与牛顿迭代法”,我在收录的专题目录当中已...

    TechFlow-承志
  • zookeeper ZAB协议的实现

    在 zookeeper源码分析系列 中按照服务端客户端启动或交互等主线讲解了源码,但并没有将Zab协议的完整实现串起来。本文主要翻译自ZooKeeper’s a...

    Monica2333
  • CAP 一致性协议及应用解析

    假设系统中有 5 个节点,n1~n5。n1,n2,n3 在A物理机房。n4,n5 在 B 物理机房。现在发生了网络分区,A 机房和 B 机房网络不通。

    [3306 Pai ] 社区
  • CAP 一致性协议及应用解析

    假设系统中有 5 个节点,n1~n5。n1,n2,n3 在A物理机房。n4,n5 在 B 物理机房。现在发生了网络分区,A 机房和 B 机房网络不通。

    用户1278550
  • 重温数据结构:树 及 Java 实现

    数据结构,指的是数据的存储形式,常见的有线性结构(数组、链表,队列、栈),还有非线性结构(树、图等)。 今天我们来学习下数据结构中的 树。 什么是树 线性结构中...

    张拭心 shixinzhang
  • 【深入学习MySQL】MySQL的索引结构为什么使用B+树?

    在MySQL中,无论是Innodb还是MyIsam,都使用了B+树作索引结构(这里不考虑hash等其他索引)。本文将从最普通的二叉查找树开始,逐步说明各种树解决...

    Java_老男孩
  • 分析比特币网络:一种去中心化、点对点的网络架构

    Tiny熊
  • 《Redis设计与实现》读书笔记(二十八) ——Redis集群节点结构与槽分配

    《Redis设计与实现》读书笔记(二十八) ——Redis集群节点结构与槽分配 (原创内容,转载请注明来源,谢谢) 一、概述 redis集群是...

    用户1327360

扫码关注云+社区

领取腾讯云代金券