一文看懂分布式事务

标签: 分布 | 发表时间:2020-01-16 20:52 | 作者:老马
出处:http://weekly.dockone.io

本地事务

事务Transaction由一组SQL组成,具有四个ACID特性。

ACID

  • Atomicity 原子性,构成事务的一组SQL,要么全部生效,要么全不生效,不会出现部分生效的情况
  • Consistency 一致性,数据库经过事务操作后从一种状态转变为另一个状态。可以说原子性是从行为上描述,而一致性是从结果上描述
  • Isolation 隔离性,事务操作的数据对象 相对于 其他事务操作的数据对象相互隔离,互不影响
  • Durability 持久性,事务提交后,其结果就是永久性的,即使发生宕机(非磁盘损坏)


事务实现

对于MySQL数据库(InnoDB存储引擎)而言,隔离性是通过不同粒度的锁机制来实现事务间的隔离;原子性、一致性和持久性通过redo log重做日志和undo log回滚日志来保证的。
  • redo log,当数据库对数据做修改的时候,需要把数据页从磁盘读到buffer pool中,然后在buffer pool中进行修改,那么这个时候buffer pool中的数据页就与磁盘上的数据页内容不一致,称buffer pool的数据页为dirty page脏数据,如果这个时候发生非正常的DB服务重启,那么这些数据还没在内存,并没有同步到磁盘文件中(注意,同步到磁盘文件是个随机IO),也就是会发生数据丢失,如果这个时候,能够在有一个文件,当buffer pool中的data page变更结束后,把相应修改记录记录到这个文件(注意,记录日志是顺序IO),那么当DB服务发生crash的情况,恢复DB的时候,也可以根据这个文件的记录内容,重新应用到磁盘文件,数据保持一致。
  • undo log,undo日志用于存放数据被修改前的值,如果修改出现异常,可以使用undo日志来实现回滚操作,保证事务的一致性。另外InnoDB MVCC事务特性也是基于undo日志实现的。undo日志分为insert undo log(insert语句产生的日志,事务提交后直接删除)和update undo log(delete和update语句产生的日志,由于该undo log可能提供MVVC机制使用,所以不能再事务提交时删除)。


问题引入

CAP理论


CAP原则又称CAP定理,指的是在一个分布式系统中,一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)。CAP原则指的是,这三个要素最多只能同时实现两点,不可能三者兼顾。但由于在分布式系统中,分区容错性必然存在,所以只能在一致性和可用性妥协。

传统的DBMS,如MySQL其实CA组合,在主从架构下,读写分离的情况下,是牺牲一定的一致性的(主从延迟)。

Base理论:
  • base available,基本可用,分布式系统在出现故障时,允许损失部分可用功能,保证核心功能可用
  • soft state,软状态,允许系统中存在中间状态,这个状态不影响系统可用性
  • eventually consistent最终一致性,系统的中间状态经过短暂的时间后到达一致状态


如何解决

场景举例

考虑这样一种业务场景,系统A调用系统B的退款服务进行退款,系统A更改内部退款状态,接着调用系统C的短信服务通知用户。

在这样的一个场景下,由于网络不可靠的必然存在,存在A、B、C三个系统之间一致性的问题。

本地表

针对上述场景,设计两张表:退款记录表和短信发送记录表以及相应的补偿Job。

具体实现过程:
  1. 新增退款记录表,状态为处理中
  2. 调用系统B的退款服务进行退款
  3. 更新退款记录状态为对应的状态(成功/失败)
  4. 如果退款成功,则新增短信发送记录,记录状态为待发送
  5. 调用系统C的短信服务,发送短信
  6. 更新短信发送记录为已发送


退款补偿Job,查询退款记录表中处理中的记录,调用系统B的退款服务,退款成功处理:
  1. 新增短信发送记录,记录状态为待发送
  2. 调用系统C的短信服务,发送短信
  3. 更新短信发送记录为已发送


短信通知补偿Job,查询短信发送记录中待发送的记录,调用系统C的短信服务:
  1. 调用系统C的短信服务,发送短信
  2. 更新短信发送记录为已发送


注意:
  1. 系统B和系统C需要根据调用方传的uuid支持幂等
  2. 系统A、B、C会出现短暂的不一致,但最终一致


事务消息

可以将其视为两阶段提交消息实现,以确保分布式系统中的最终一致性。事务性消息可确保本地事务的执行和消息的发送可以原子方式执行。

但是由于事务消息异步的特性,调用方拿不到消费方的处理结果,适用于不关心对方的返回结果/对方负责保证处理成功。

针对上述场景,增加两个事务消息的方式解决一致性问题,系统A通过发送事务消息的方式与系统B和系统C进行交互。

具体实现过程:
  • 发送退款的事务消息
  • 新增退款记录,状态为:处理中
  • Commit退款事务消息


提供MQ事务callback:

  • 退款callback查询:
    • 有退款记录且为处理中则Commit
    • 其他则Rollback

  • 发送短信callback查询
    • 有退款记录且成功则Commit
    • 其他则Rollback


退款同步Job,查询退款记录表中处理中的记录,调用系统B的退款查询接口同步状态,其中退款成功处理:
  • 发送短信的事务消息
  • 更新退款记录为成功
  • Commit短信事务消息


相关理论

二阶段提交



二阶段提交是解决分布式事务问题的重要理论基础,但也存在着明显的问题:
  • 阻塞问题,参与者将协议消息发送给协调器后,它将阻塞直到收到提交或回滚,只能依赖协调者的超时机制
  • 协调者单点问题,如果协调者出现故障,则某些参与者将一直无法收到提交或回滚的消息。


为了解决二阶段提交出现的问题,又有了三阶段提交(Three-phase commit):
  • 解决阻塞问题:将2PC中的第一阶段一分为二,提供了一个CanCommit阶段,此阶段并不锁定资源,这样可以大幅降低了阻塞概率
  • 解决单点问题:在参与者这边也引入了超时机制


DTP Model

X / Open分布式事务处理DTP(Distributed Transaction Processing)模型是一种软件体系架构,已经成为事实上的事务模型组件的行为标准。它允许多个应用程序共享由多个资源管理器提供的资源,并允许其工作被协调为全局事务。

  • ApplicationProgram(AP),应用程序定义了事务边界并指定构成事务的操作
  • ResourceManager(RM),资源管理器用来管理我们需要访问的共享资源,我们可以将它理解为关系数据库、文件存储系统、消息队列、打印机等
  • TransactionManagger(TM),事务管理器是一个独立的组件,他为事务分配标识符并监视事务的执行情况,负责事务完成和故障恢复
  • CommunicationResourceManager(CRM),通信资源管理器控制一个或多个 TM domain 之间分布式应用的通信。


XA Specification

XA规范是X/Open关于分布式事务处理(DTP)的规范。规范描述了全局的事务管理器与局部的资源管理器之间的接口。XA规范的目的是允许多个资源(如数据库,应用服务器,消息队列,等等)在同一事务中访问,这样可以使ACID属性跨越应用程序而保持有效。XA使用两阶段提交来保证所有资源同时提交或回滚任何特定的事务。

XA规范描述了资源管理器要支持事务性访问所必需做的事情。

TCC



Saga


在Saga模式下,分布式事务内有多个参与者,每一个参与者都是一个冲正补偿服务,需要用户根据业务场景实现其正向操作和逆向回滚操作。

分布式事务执行过程中,依次执行各参与者的正向操作,如果所有正向操作均执行成功,那么分布式事务提交。如果任何一个正向操作执行失败,那么分布式事务会去退回去执行前面各参与者的逆向回滚操作,回滚已提交的参与者,使分布式事务回到初始状态。

Saga模式下分布式事务通常是由事件驱动的,各个参与者之间是异步执行的,Saga模式是一种长事务解决方案。

Saga模式的优势是:
  • 一阶段提交本地数据库事务,无锁,高性能;
  • 参与者可以采用事务驱动异步执行,高吞吐;
  • 补偿服务即正向服务的“反向”,易于理解,易于实现。


缺点:
  • Saga模式由于一阶段已经提交本地数据库事务,且没有进行“预留”动作,所以不能保证隔离性。


开源项目

Seata

Seata是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。支持AT、TCC、SAGA、XA四种模式,对微服务框架支持友好。

如下图所示,Seata中有三大模块,分别是TM、RM和TC。 其中TM和RM是作为Seata的客户端与业务系统集成在一起,TC作为Seata的服务端独立部署。
  • TC - 事务协调者,维护全局和分支事务的状态,驱动全局事务提交或回滚。
  • TM - 事务管理器,定义全局事务的范围:开始全局事务、提交或回滚全局事务。
  • RM - 资源管理器,管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。


在Seata中,分布式事务的执行流程:
  • TM开启分布式事务(TM向TC 注册全局事务记录);
  • 按业务场景,编排数据库、服务等事务内资源(RM向TC汇报资源准备状态 );
  • TM结束分布式事务,事务一阶段结束(TM通知TC提交/回滚分布式事务);
  • TC汇总事务信息,决定分布式事务是提交还是回滚;
  • TC通知所有 RM 提交/回滚 资源,事务二阶段结束。


AT模式

AT模式是一种无侵入的分布式事务解决方案。在AT模式下,用户只需关注自己的“业务 SQL”,用户的 “业务 SQL” 作为一阶段,Seata 框架会自动生成事务的二阶段提交和回滚操作。
  • 一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。
  • 二阶段:提交异步化,非常快速地完成。回滚通过一阶段的回滚日志进行反向补偿。



在一阶段,Seata会拦截“业务SQL”,首先解析SQL语义,找到“业务SQL”要更新的业务数据,在业务数据被更新前,将其保存成“before image”,然后执行“业务 SQL”更新业务数据,在业务数据更新之后,再将其保存成“after image”,最后生成行锁。以上操作全部在一个数据库事务内完成,这样保证了一阶段操作的原子性。


TCC模式


一个分布式的全局事务,整体是 两阶段提交 的模型。全局事务是由若干分支事务组成的,分支事务要满足 两阶段提交 的模型要求,即需要每个分支事务都具备自己的:
  • 一阶段prepare 行为
  • 二阶段commit或rollback行为


TCC 模式,不依赖于底层数据资源的事务支持:
  • 一阶段prepare行为:调用自定义的prepare逻辑。
  • 二阶段commit行为:调用自定义的commit逻辑。
  • 二阶段rollback行为:调用自定义的rollback逻辑。


所谓 TCC 模式,是指支持把 自定义 的分支事务纳入到全局事务的管理中。

Saga模式

目前SEATA提供的Saga模式是基于状态机引擎来实现的,机制是:
  1. 通过状态图来定义服务调用的流程并生成json状态语言定义文件
  2. 状态图中一个节点可以是调用一个服务,节点可以配置它的补偿节点
  3. 状态图json由状态机引擎驱动执行,当出现异常时状态引擎反向执行已成功节点对应的补偿节点将事务回滚 (异常发生时是否进行补偿也可由用户自定义决定)
  4. 可以实现服务编排需求,支持单项选择、并发、子流程、参数转换、参数映射、服务执行状态判断、异常捕获等功能


状态机引擎原理:

  • 图中的状态图是先执行stateA,再执行stateB,然后执行stateC
  • "状态"的执行是基于事件驱动的模型,stateA执行完成后,会产生路由消息放入EventQueue,事件消费端从EventQueue取出消息,执行stateB
  • 在整个状态机启动时会调用Seata Server开启分布式事务,并生产xid,然后记录"状态机实例"启动事件到本地数据库
  • 当执行到一个"状态"时会调用Seata Server注册分支事务,并生产branchId,然后记录"状态实例"开始执行事件到本地数据库
  • 当一个"状态"执行完成后会记录"状态实例"执行结束事件到本地数据库,然后调用Seata Server上报分支事务的状态
  • 当整个状态机执行完成, 会记录"状态机实例"执行完成事件到本地数据库,然后调用Seata Server提交或回滚分布式事务


原文链接: https://juejin.im/post/5e066c9ff265da33b0718f89,作者:VectorJin

相关 [分布] 推荐:

分布式日志

- - Java - 编程语言 - ITeye博客
最近完成一个简单的日志管理系统,拿出来跟大家分享一下. 3、支持文件输出、habse输出、mongodb输出. 基于以上三点功能,我们下面详细说明. 说道支持这个功能,有个同事认为没有这个必要,他的观点是log4j的配置不需要经常变动,不需要支持这样的功能;本人的观点是“配置可以进行统一管理、而且正式机跟测试机的log4j的配置肯定会有一些差异的”,因此这个功能是必须的.

分布式事务简述

- If you are thinking one year ahead, you plant rice. If you are thinking twenty years ahead, you plant trees. If you are thinking a hundred years ahead, you educate people. - BlogJava-首页技术区
  随着系统越来越大,不断的模块化和SOA化,你的系统可能被分散于不同的机器上,这时候,你原先的单机本地事务可能已经无法满足你的需求,你可能要跨系统跨资源的去使用事务.   具体就不多介绍了,相信大家都能明白ACID特性的基本含义. 而一个具体的事务需要涉及到的模型(无论哪种模型)一般由下面几部分组成:.

Hadoop与分布式计算

- 透明 - 丕子
写本文由leftnoteasy发布于http://leftnoteasy.cnblogs.com 本文可以被全部或者部分的使用,但请注明出处,如果有问题,可以联系wheeleast (at) gmail.com, 也可以加作者的新浪微博:http://weibo.com/leftnoteasy. 很久没有写写博客了,之前主要是换工作,耽误了很多的时间,让人也变得懒散,不想花大时间来写东西.

分布式缓存-Memcached

- - 人月神话的BLOG
分布式缓存出于如下考虑,首先是缓存本身的水平线性扩展问题,其次是缓存大并发下的本身的性能问题,再次避免缓存的单点故障问题(多副本和副本一致性). 分布式缓存的核心技术包括首先是内存本身的管理问题,包括了内存的分配,管理和回收机制. 其次是分布式管理和分布式算法,其次是缓存键值管理和路由. 原文: http://wenku.baidu.com/view/8686d46c7e21af45b307a8c3.html.

再谈集中和分布

- - 人月神话的BLOG
上篇文章转载了关于mysql数据库的垂直和水平拆分的相关内容,本篇文章再谈下关于应用集中化后的集中和分布相关策略问题,以及最近关于完全去IOE思路的一些思考和回顾. 现在有一个问题我暂时还没得到比较明确的一些验证,如对于当前x86的pc server服务器好的配置完全可以达到200万TPMC,对于磁盘阵列可以挂接到20T甚至更高的存储容量.

hadoop分布式配置

- - CSDN博客云计算推荐文章
一、前面的部分见伪分布式配置. 二、实现SSH无密码登录远程主机(只在源主机上配置). 注意:以上scp命令表示把authoriezd_keys远程复制到对应主机的相应目录下. slave2是目的主机的名字,需要在源主机的/etc/hosts下配置slave2以及对应的IP地址 192.168.0.5.

浅谈分布式缓存

- - CSDN博客推荐文章
在前面的一些文章中,从实战的角度,讲解了有关 memcached的应用、容灾、监控等等. 但是缺乏对理论的讲解和原理性的剖析. 本文将从理论的角度去介绍,让大家从宏观上对“分布式缓存、nosql”等技术有所了解,以便进一步学习和使用. 在构建大规模的web应用时,缓存技术可以说是必备的,学习的必要性不言而喻.

关于分布式事务

- - Web前端 - ITeye博客
Mysql当前分布式事务只支持Innodb存储引擎. 1个分布式事务由多个行为在不同的数据库上执行,1个分布式事务的执行成功意味着相关数据库上的行为执行均成功. 使用分布式事务的应用程序设计1个或多个资源管理器和一个事务管理器. 资源管理器(RM):用户提供通向事务的途径. 数据库服务器是一个种资源管理器.

BDRP分布式redis集群

- - 百度运维团队技术博客
BDRP(baidu distributed redis platform)是包含 twemproxy, redis,redis-sentinel等多个模块开发的分布式redis平台. bdrp已经在github上进行了开源, bdrp的github项目点这里. 目前redis集群架构主要有以下几个组件: twemproxy:redis的代理系统,可以选择多种数据分片算法 redis:集群的redis存储节点 sentinel:redis官方的集群高可用组件,可以监控redis主节点故障,并进行主备切换.

分布式搜索算法

- - 杨尚川的个人页面
对于搜索引擎来说,索引存放在成千上万台机器上,如何进行分布式搜索呢. 假设搜索结果是以分页的方式显示,以PageNumber代表当前页,从1开始,以PageSize代表页面大小,默认为10,以N代表搜索服务器数量. 最简单的分布式搜索算法为:有一台 合并服务器负责接受用户的搜索请求,然后分别向N台机器获取前PageNumber*PageSize条结果,得到的结果数为N*PageNumber*PageSize,然后把这些数据重新进行排序,根据所要显示的页面PageNumber,获取从(PageNumber - 1) * PageSize + 1开始的PageSize条结果返回给用户.