001 分布式事务实现 | 基本概念理论和协议

数据库事务的基本特性:

  1. 原子性(Atomicity )、
  2. 一致性( Consistency )、
  3. 隔离性或独立性( Isolation)、
  4. 持久性(Durabilily),

简称就是ACID

在一个分布式微服务的系统之上,如果一个业务操作,涉及到了多个服务一起完成,有可能涉及到多个数据库的数据变更,这种情况下,需要分布式事务的解决方案。

分布式理论

集群环境下,再想保证集群的ACID几乎是很难达到,这时我们就需要引入一个新的理论原则来适应这种集群的情况,就是 CAP 原则或者叫CAP定理。

CAP理论

  • 一致性(Consistency) : 客户端知道一系列的操作都会同时发生(生效)
  • 可用性(Availability) : 每个操作都必须以可预期的响应结束
  • 分区容错性(Partition tolerance) : 即使出现单个组件无法可用,操作依然可以完成

BASE理论

  • Basically Available(基本可用)
  • Soft state(软状态)
  • Eventually consistent(最终一致性)

BASE理论是对CAP中的一致性和可用性进行一个权衡的结果,理论的核心思想就是:我们无法做到强一致,但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性(Eventual consistency)

分布式事务解决方案

列出以下几个常用的解决方案,每个方案适用的场景不同。

两阶段提交2PC[分布式一致性理论]

系统一般包含两类机器(或节点):一类为协调者(coordinator),通常一个系统中只有一个;另一类为事务参与者(participants,cohorts或workers),一般包含多个。

两阶段:

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

XA协议

XA协议是 2PC在数据库分布式事务方面( 理解成分布式一致性问题2PC方案的一种实际应用,分布式事务其实也是一致性问题 ) 的定义,又叫做XA Transactions,XA规范主要定义了(全局)事务管理器(Transaction Manager,TM)和(局部)资源管理器(Resource Manager,RM)之间的接口,接口是双向互通的,可以彼此完成数据交换。

JTA

Java Transaction API,定义了Java平台的XA协议的规范和接口,JTA 中,事务管理器抽象为javax.transaction.TransactionManager接口,并通过底层事务服务(即Java Transaction Service)实现。像很多其他的Java规范一样,JTA仅仅定义了接口,具体的实现则是由供应商(如J2EE厂商)负责提供。

JTA定义了接口,需要不同的J2EE厂商和数据库厂商共同完成,即,需要支持事务管理器以及事务的提交和撤回。

目前,框架Atomikos可以实现JTA,通过和Spring等配合,实现JTA功能。

2PC的优缺点

  • 同步阻塞: 当参与者占有公共资源时,其他第三方节点访问公共资源不得不处于阻塞状态
  • 单点故障:一旦协调者发生故障。参与者会一直阻塞下去。第二阶段,若协调者发生故障,那么所有的参与者还都处于锁定事务资源的状态中,而无法继续完成事务操作
  • 数据不一致:提交的阶段中,当协调者向参与者发送commit请求之后,若在发送commit请求过程中协调者发生了故障,导致只有部分参与者接受到了commit请求。而这部分参会执行commit操作。但是其未接到commit请求的协调者则无法执行事务提交。于是出现了数据不一致性的现象
  • 耗费资源(JTA,Atomikos)

三阶段提交3PC[分布式一致性理论]

在协调者和参与者引入超时机制,将2PC的第一个阶段拆分,询问和锁资源,最后是真正提交。如下图,


3PC流程图

执行流程解释,

  1. can-commit阶段:协调者向参与者发送commit请求,参与者如果可以提交,就返回yes,反之,no,之后,协调者计时等待(等待协调者返回yes还是no,没有及时返回,就是no)
  2. pre-commit阶段:协调者根据参与者返回来判断是否执行pre-commit阶段
  • 参与者都返回yes
    (1) 协调者发送预提交请求后,进入到prepare阶段
    (2) 参与者接受到请求后,执行事务预提交,记录undo和redo信息(MVCC)
    (3) 参与者执行完后,返回ACK,同时,开始计时等待最终指令
  • 任何一个返回no 或者 协调者在获取所有参与者返回之前等待超时。
    (1)协调者向参与者发送abort中断指令
    (2)参与者执行中断任务,收到中断指令,或者等待超时(等待协调者发送提交指令还是abort指令,没有及时返回,就是默认提交指令)
    3.do-commit阶段: 根据第二阶段的结果,分成两种情况
  • 执行提交
    (1)协调者发送ack指令
    (2)参与者执行事务提交,并发送ack相应
    (3)参与者发送ack响应
  • 中断事务
    (1)协调者发送abort指令
    (2)参与者回滚事务

三阶段3PC协议缺点

相对于2PC,3PC主要解决的单点故障问题,并减少阻塞,因为一旦参与者无法及时收到来自协调者的信息之后,他会默认执行commit,因此pre-Commit之后,如果协调者发送abort请求,但是有部分协调者未收到abort请求,那么在他们等待超时后,会继续执行commit操作,导致数据不一致。

补偿事务TCC

全称 try-confirm-cannel,则是将业务逻辑分成try、confirm/cancel两个阶段执行。原理性质的,可以参考中华石杉的笔记,https://mp.weixin.qq.com/s?__biz=MzU0OTk3ODQ3Ng==&mid=2247483862&idx=1&sn=f94857a050ae0e98521a70f331fe5420&chksm=fba6e9d5ccd160c3c39b2a474f2e0a636465a79446d71822148e0129164cb91dfb11e61e7555&mpshare=1&scene=1&srcid=03044xUUHt8UlgTcU1ZscXeH#rd

这里说下和2PC的区别和关系,

  • 2PC机制的业务阶段 等价于 TCC机制的try业务阶段
  • 2PC机制的提交阶段(prepare & commit) 等价于 TCC机制的提交阶段(confirm)
  • 2PC机制的回滚阶段(rollback) 等价于 TCC机制的回滚阶段(cancel)
  • 2PC的案JTA是需要底层数据库支持,而TCC则不要求,甚至后面都不是数据库

2PC是begin -> 业务逻辑 -> prepare -> commit, 而TCC是begin -> 业务逻辑(try业务) -> commit(comfirm业务)。这里TCC的try业务在中华石杉笔记中得到体现。

本地消息列表

该方法是业界使用最多的,其实在我们日常的开发中,使用过类似的方式,只是我们不知道这种方式叫做本地消息列表而已,核心思想,是将分布式的业务,拆分成本地的事务处理,看如下图,


本地消息列表处理机制

含义是,将后续跨库的操作,以消息的形式,落在本地统一库中,这样就可以控制本地事务。而落地的消息会通过kafka等队列,在其他库中消费者会获取到,进行更改相应的数据。

思路如下,

  1. 消息生产方,建立额外消息表,记录消息的发送状态(消息是对后续业务数据的更改),后续消息操作表和当前消息发送方在一个数据库,如果消息生成成功,表明消息已经记录,且数据已经发送到mq中。
  2. 消费方,监听队列,提取消息,根据消息指定的操作完成业务,后,更改消息生产方中的消息表中对应的记录,如果最终事务执行成功,则完成。如果事务出现异常,则捕获异常,通过mq发送业务补偿的消息给生产方。
  3. 生产方和消费方,都需要定期扫描本地的消息表,将未成功的记录,根据异常类型,判断是否再次发送。
  • 发送方还是继续扫描本地的消息表
  • 消费方如果执行成功,但是,毕竟要垮库去修改生产方的数据,也会出现事务问题,因此,消费者也可以本地设置一个信息表,自己定期轮训这个信息表,直接跨库操作,不需要在通过kafka传递,毕竟通过kafka的话,生产方还要定义消费者,影响业务逻辑。

优缺点

  • 这种方案遵循BASE理论,采用的是最终一致性
  • 不会出现像2PC那样复杂的实现,多子系统,要每个都反复调用,影响性能
  • 不会像TCC那样可能出现确认或者回滚不了的情况
  • 消息表会耦合到业务系统中,如果没有封装好的解决方案,会有很多杂活需要处理

事务消息队列

本地消息队列的严重限制是消息表和业务表耦合度过大,而且当业务库出现问题,导致消息表也失去功能,因此,将消息表独立出来做好,这就诞生了MQ事务消息队列的应用。

这里我们使用RocketMQ为例子,

  • 第一阶段Prepared消息,会拿到消息的地址。
  • 第二阶段执行本地事务,
  • 第三阶段通过第一阶段拿到的地址去访问消息,并修改状态。
RocketMQ使用过程

业务需要向消息队列中发送两次请求,一次消息发送和一次确认消息,过程如下,

  1. 发送Prepared消息
  2. update DB
  3. 根据update DB结果成功或失败,Confirm或者取消Prepared消息。

最后1步失败了怎么办?这里就涉及到了RocketMQ的关键点:RocketMQ会定期(默认是1分钟)扫描所有的Prepared消息,询问发送方,到底是要确认这条消息发出去?还是取消此条消息?

通过RocketMQ的checkListener来实现,RocketMQ会回调此Listener,从而实现上面所说的方案。

RocketMQ最大的改变,其实就是把“扫描消息表”这个事情,不让业务方做,而是消息中间件帮着做了。

至于消息表,其实还是没有省掉。因为消息中间件要询问发送方,事物是否执行成功,还是需要一个“变相的本地消息表”,记录事物执行状态。

如果消费者失败了如何?虽然如图上所示,会一直循环,但是终有到头的时候,此时,需要人工介入。从工程实践角度讲,这种整个流程自动回滚的代价是非常巨大的,不但实现复杂,还会引入新的问题。比如自动回滚失败,又怎么处理?对应这种极低概率的case,采取人工处理,会比实现一个高复杂的自动化回滚系统,更加可靠,也更加简单

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,552评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,666评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,519评论 0 334
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,180评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,205评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,344评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,781评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,449评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,635评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,467评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,515评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,217评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,775评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,851评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,084评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,637评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,204评论 2 341

推荐阅读更多精彩内容