学习笔记事务与分布式事务

Posted 棉花糖灬

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了学习笔记事务与分布式事务相关的知识,希望对你有一定的参考价值。

一、事务

本地事务是在单个数据源上进行数据的访问和更新,分布式系统下由不同服务之间通过网络远程协作完成的事务被称为分布式事务。

分布式事务的应用场景:

  • 微服务架构之间,即多服务器访问多数据库实例
  • 单体系统访问多数据库实例
  • 多服务访问同一数据库实例

二、 事务的ACID特性

该部分参考了文章事务的实现原理MySQL事务的实现原理两篇文章。

ACID即原子性(Atomicity),一致性(Consistency),隔离型(Isolation)和持久性(Durability)

1. 原子性

原子性通过undo log实现。undo log 叫做回滚日志,undo log记录写入数据或者修改数据之前的数据信息,因此假如由于系统错误或者rollback操作而回滚的话可以根据undo log的信息来进行回滚到没被修改前的状态。redo log和undo log都属于InnoDB的事务日志

2. 持久性

持久性通过redo log实现。redo log叫做重做日志,用来恢复数据。mysql引入了redo log来记录已成功提交事务的修改信息,并且会把redo log持久化到磁盘,系统重启之后在读取redo log恢复最新数据。

3. 隔离性

隔离性通过读写锁 + MVCC实现。通过读写锁,可以做到读读可以并行,但是不能做到写读、写写并行,从而避免了一个事务的写操作对另一个事务写操作的影响。MVCC (MultiVersion Concurrency Control))即多版本并发控制,通过MVCC避免了一个事务的写操作对另一个事务读操作的影响。

InnoDB实现MVCC,多个版本的数据可以共存,主要基于以下技术及数据结构:

  • 隐藏列:InnoDB中每行数据都有隐藏列,隐藏列中包含了本行数据的事务id、指向undo log的指针等。
  • 基于undo log的版本链:每行数据的隐藏列中包含了指向undo log的指针,而每条undo log也会指向更早版本的undo log,从而形成一条版本链。
  • ReadView:通过隐藏列和版本链,MySQL可以将数据恢复到指定版本;但是具体要恢复到哪个版本,则需要根据ReadView来确定。所谓ReadView,是指事务(记做事务A)在某一时刻给整个事务系统打快照,之后再进行读操作时,会将读取到的数据中的事务id与事务系统快照比较,从而判断数据对该ReadView是否可见,即对事务A是否可见。

而事务的一致性是通过原子性,持久性,隔离性来实现的。

4. redo log与binlog的差异

  • 作用不同:redo log是用于crash recovery的,保证MySQL宕机也不会影响持久性;binlog是用于point-in-time recovery的,保证服务器可以基于时间点恢复数据,此外binlog还用于主从复制。

  • 层次不同:redo log是InnoDB存储引擎实现的,而binlog是MySQL的服务器层实现的,同时支持InnoDB和其他存储引擎。

  • 内容不同:redo log是物理日志,内容基于磁盘的Page;binlog的内容是二进制的,根据binlog_format参数的不同,可能基于sql语句、基于数据本身或者二者的混合。

  • 写入时机不同:binlog在事务提交时写入;redo log的写入时机相对多元,可以在事务提交时写入或按时写入

三、CAP理论

CAP(Consistency、Availability、Partitiontolerance)理论即一致性、可用性、分区容错性

1. 一致性

一致性是指写操作后的读操作可以读取到最新的数据状态,当数据分布在多个节点上,从任意结点读取到的数据都是最新的状态。

信息的读写要满足一致性就是要实现如下目标:

  • 服务写入主数据库成功,则向从数据库查询新数据也成功。

  • 服务写入主数据库失败,则向从数据库查询新数据也失败。

如何实现一致性:

  • 写入主数据库后要将数据同步到从数据库。
  • 写入主数据库后,在向从数据库同步期间要将从数据库锁定,待同步完成后再释放锁,以免在新数据写入成功后,向从数据库查询到旧的数据。

分布式系统一致性的特点:

  • 由于存在数据同步的过程,写操作的响应会有一定的延迟。
  • 为了保证数据一致性会对资源暂时锁定,待数据同步完成释放锁定资源。
  • 如果请求数据同步失败的结点则会返回错误信息,一定不会返回旧数据。

2. 可用性

可用性是指任何事务操作都可以得到响应结果,且不会出现响应超时或响应错误。
信息读取满足可用性就是要实现如下目标:

  • 从数据库接收到数据查询的请求则立即能够响应数据查询结果。

  • 从数据库不允许出现响应超时或响应错误。

如何实现可用性:

  • 写入主数据库后要将数据同步到从数据库。
  • 由于要保证从数据库的可用性,不可将从数据库中的资源进行锁定。
  • 即使数据还没有同步过来,从数据库也要返回要查询的数据,哪怕是旧数据,如果连旧数据也没有则可以按照约定返回一个默认信息,但不能返回错误或响应超时。

分布式系统可用性的特点:

  • 所有请求都有响应,且不会出现响应超时或响应错误。

3. 分区容错性

通常分布式系统的各各结点部署在不同的子网,也就是网络分区,不可避免的会出现由于网络问题而导致结点之间通信失败,此时仍可对外提供服务,这叫分区容错性。
信息读写满足分区容错性就是要实现如下目标:

  • 主数据库向从数据库同步数据失败不影响读写操作。
  • 其一个结点挂掉不影响另一个结点对外提供服务。

如何实现分区容错性?

  • 尽量使用异步取代同步操作,例如使用异步方式将数据从主数据库同步到从数据,这样结点之间能有效的
    实现松耦合。
  • 添加从数据库结点,其中一个从结点挂掉其它从结点提供服务。

分布式分区容错性的特点:

  • 分区容错性分是布式系统具备的基本能力。

4. CAP的可行组合

(1) AP

放弃一致性,追求分区容错性和可用性。这是很多分布式系统设计时的选择。例如商品管理,完全可以实现AP,前提是只要用户可以接受所查询的到数据在一定时间内不是最新的即可。通常实现AP都会保证最终一致性,一些业务场景比如:订单退款,今日退款成功,明日账户到账,只要用户可以接受在一定时间内到账即可。

(2) CP

放弃可用性,追求一致性和分区容错性,zookeeper其实就是追求的强一致,比如跨行转账,一次转账请求要等待双方银行系统都完成整个事务才算完成。

(3) CA

放弃分区容错性,即不进行分区,不考虑由于网络不通或结点挂掉的问题,则可以实现一致性和可用性。那么系统将不是一个标准的分布式系统,我们最常用的关系型数据就满足了CA。

最常用的是选择AP,舍弃了强一致性,保证最终一致性。要求在任何时间查询每个结点数据都必须一致,它强调的是强一致性,但是最终一致性是允许可以在一段时间内每个结点的数据不一致,但是经过一段时间每个结点的数据必须一致

四、BASE理论

1. BASE

BASE是Basically Available(基本可用)、Softstate(软状态)和Eventually consistent(最终一致性)三个短语的缩写。BASE理论是对CAP中AP的一一个扩展,通过牺牲强一致性来获得可用性,当出现故障允许部分不可用但要保证核心功能可用,允许数据在一段时间内是不一致的,但最终达到一致状态。满足BASE理论的事务,我们称之为“柔性事务"。

2. 基本可用

分布式系统在出现故障时,允许损失部分可用功能,保证核心功能可用。如,电商网站交易付款出现问题了,商品依然可以正常浏览。

3. 软状态

由于不要求强一致性,所以BASE允许系统中存在中间状态(也叫软状态),这个状态不影响系统可用性,如订单的"支付中"、“数据同步中"等状态,待数据最终一致后状态改为"成功”状态。

4. 最终一致

最终一致是指经过一段时间后,所有节点数据都将会达到一致。如订单的"支付中”状态,最终会变为“支付成功”或者"支付失败",使订单状态与实际交易结果达成一致,但需要一定时间的延迟、等待。

五、两阶段提交(2PC)

1. 2PC

2PC即两阶段提交协议,是将整个事务流程分为两个阶段,准备阶段(Preparephase)、提交阶段(commitphase),2是指两个阶段,P是指准备阶段,C是指提交阶段。

(1) 准备阶段(Prepare phase)

事务管理器给每个参与者发送Prepare消息,每个数据库参与者在本地执行事务,并写本地的Undo/Redo日志,此时事务没有提交。(Undo日志是记录修改前的数据,用于数据库回滚,Redo日志是记录修改后的数据,用于提交事务后写入数据文件)

(2) 提交阶段(commit phase)

如果事务管理器收到了参与者的执行失败或者超时消息时,直接给每个参与者发送回滚(Rollback)消息;否则,发送提交(Commit)消息;参与者根据事务管理器的指令执行提交或者回滚操作,并释放事务处理过程中使用的锁资源。注意:必须在最后阶段释放锁资源。

(3) 示意图

成功情况:

失败情况:

2. XA方案

(1) XA方案

2PC的传统方案是在数据库层面实现的,如Oracle、MySQL都支持2PC协议,为了统一标准减少行业内不必要的对接成本,需要制定标准化的处理模型及接口标准,国际开放标准组织OpenGroup定义了分布式事务处理模型DTP(Distributed Transaction Processing Reference Model)。DTP模型定义TM和RM之间通讯的接口规范叫XA,简单理解为数据库提供的2PC接口协议,基于数据库的XA协议来实现2PC又称为XA方案。

(2) DTP模型中角色

  • AP(Application Program):即应用程序,可以理解为使用DTP分布式事务的程序。
  • RM(Resource Manager):即资源管理器,可以理解为事务的参与者,一般情况下是指一个数据库实例,通过资源管理器对该数据库进行控制,资源管理器控制着分支事务。
  • TM(Transaction Manager):事务管理器,负责协调和管理事务,事务管理器控制着全局事务,管理事务生命周期,并协调各个RM。全局事务是指分布式事务处理环境中,需要操作多个数据库共同完成一个工作,这个工作即是一个全局事务。

以上三个角色之间的交互方式如下:

  • TM向AP提供应用程序编程接口,AP通过TM提交及回滚事务。
  • TM交易中间件通过XA接口来通知RM数据库事务的开始、结束以及重提交、回滚等。

(3) 执行流程

  • 应用程序(AP)持有用户库和积分库两个数据源。
  • 应用程序(AP)通过TM通知用户库RM新增用户,同时通知积分库RM为该用户新增积分,RM此时并未提交事务,此时用户和积分资源锁定。
  • TM收到执行回复,只要有一方失败则分别向其他RM发起回滚事务,回滚完毕,资源锁释放。
  • TM收到执行回复,全部成功,此时向所有RM发起提交事务,提交完毕,资源锁释放。

(4) 总结

整个2PC的事务流程涉及到三个角色AP、RM、TM。AP指的是使用2PC分布式事务的应用程序;RM指的是资源管理器,它控制着分支事务;TM指的是事务管理器,它控制着整个全局事务。

  • 在准备阶段RM执行实际的业务操作,但不提交事务,资源锁定。
  • 在提交阶段TM会接受RM在准备阶段的执行回复,只要有任一个RM执行失败,TM会通知所有RM执行回滚操作,否则,TM将会通知所有RM提交该事务。提交阶段结束资源锁释放。

(5) XA方案的问题

  • 需要本地数据库支持XA协议。
  • 资源锁需要等到两个阶段结束才释放,性能较差。

3. Seata方案

(1) Seata方案

Seata是由阿里中间件团队发起的开源项目Fescar,后更名为Seata,它是一个是开源的分布式事务框架。传统2PC的问题在Seata中得到了解决,它通过对本地关系数据库的分支事务的协调来驱动完成全局事务,是工作在应用层的中间件。主要优点是性能较好,胚长时间占用连接资源,它以高效并且对业务0侵入的方式解决微服务场景下面临的分布式事务问题,它目前提供AT模式(即2PC)及TCC模式的分布式事务解决方案。

(2) Seata的设计思想

Seata的设计目标其一是对业务无侵入,因此从业务无侵入的2PC方案着手,在传统2PC的基础上演进,并解决2PC方案面临的问题。Seata把一个分布式事务理解成一个包含了若干分支事务的全局事务。全局事务的职责是协调其下管辖的分支事务达成一致,要么一起成功提交,要么一起失败回滚。此外,通常分支事务本身就是一个关系数据库的本地事务。

(3) Seata中的角色

  • Transaction Coordinator(TC):事务协调器,它是独立的中间件,需要独立部署运行,它维护全局事务的运行状态,接收TM指令发起全局事务的提交与回滚,负责与RM通信协调各各分支事务的提交或回滚。
  • Transaction Manager(TM):事务管理器,TM需要嵌入应用程序中工作,它负责开启一个全局事务,并最终向TC发起全局提交或全局回滚的指令。
  • Resource Manager(RM):控制分支事务,负责分支注册、状态汇报,并接收事务协调器TC的指令,驱动分支事务的提交和回滚。

(4) 执行流程

  • 用户服务的TM向TC申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的XID。
  • 用户服务的RM向TC注册分支事务,该分支事务在用户服务执行新增用户逻辑,并将其纳入XID对应全局事务的管辖。
  • 用户服务执行分支事务,向用户表插入一条记录。
  • 逻辑执行到远程调用积分服务时(XID在微服务调用链路的上下文中传播)。积分服务的RM向TC注册分支事务,该分支事务执行增加积分的逻辑,并将其纳入XID对应全局事务的管辖。
  • 积分服务执行分支事务,向积分记录表插入一条记录,执行完毕后,返回用户服务。
  • 用户服务分支事务执行完毕。
  • TM向TC发起针对XID的全局提交或回滚决议。
  • TC调度XID下管辖的全部分支事务完成提交或回滚请求。

(5) Seata实现2PC与传统2PC的差别

架构层次方面,传统2PC方案的RM实际上是在数据库层,RM本质上就是数据库自身,通过XA协议实现,而Seata的RM是以jar包的形式作为中间件层部署在应用程序这一侧的。两阶段提交方面,传统2PC无论第二阶段的决议是commit还是rollback,事务性资源的锁都要保持到Phase2完成才释放。而Seata的做法是在Phase1就将本地事务提交,这样就可以省去Phase2持锁的时间,整体提高效率。

六、TCC

1. TCC

TCC是Try、Confirm、Cancel三个词语的缩写,TCC要求每个分支事务实现三个操作:预处理Try、确认Confirm、撤销Cancel。Try操作做业务检查及资源预留,Confirm做业务确认操作,Cancel实现一个与Try相反的操作即回滚操作。TM首先发起所有的分支事务的try操作,任何一个分支事务的try操作执行失败,TM将会发起所有分支事务的Cancel操作,若try操作全部成功,TM将会发起所有分支事务的Confirm操作,其中Confirm/Cancel操作若执行失败,TM会进行重试。

2. TCC的三个阶段

  • Try阶段是做业务检查(一致性)及资源预留(隔离),此阶段仅是一个初步操作,它和后续的Confirm一起才能真正构成一个完整的业务逻辑。
  • Confirm阶段是做确认提交,Try阶段所有分支事务执行成功后开始执行Confirm。通常情况下,采用TCC则认为Confirm阶段是不会出错的。即:只要Try成功,Confirm一定成功。若Confirm阶段真的出错了,需引入重试机制或人工处理。
  • Cancel阶段是在业务执行错误需要回滚的状态下执行分支事务的业务取消,预留资源释放。通常情况下,采用TCC则认为Cancel阶段也是一定成功的。若Cance|阶段真的出错了,引入重试机制或人工处理。

3. 示意图

成功时:

失败时:

4. 其他

TM事务管理器可以实现为独立的服务,也可以让全局事务发起方充当TM的角色,TM独立出来是为了成为公用组件,是为了考虑系统结构和软件复用。TM在发起全局事务时生成全局事务记录,全局事务ID贯穿整个分布式事务调用链条,用来记录事务下文,追踪和记录状态,由于Confirm和cancel失败需进行重试,因此需要实现为幂等,幂等性是指同一个操作无论请求多少次,其结果都相同。

七、可靠消息最终一致性

1. 可靠消息最终一致性

可靠消息最终一致性方案是指当事务发起方执行完成本地事务后并发出一条消息,事务参与方(消息消费者)一定能够接收消息并处理事务成功,此方案强调的是只要消息发给事务参与方最终事务要达到一致。此方案是利用消息中间件完成,如下图:事务发起方(消息生产方)将消息发给消息中间件,事务参与方从消息中间件接收消息,事务发起方和消息中间件之间,事务参与方(消息消费方)和消息中间件之间都是通过网络通信,由于网络通信的不确定性会导致分布式事务问题。

2. 要解决的问题

  • 本地事务与消息发送的原子性问题本地事务与消息发送的原子性问题即:事务发起方在本地事务执行成功后消息必须发出去,否则就丢弃消息。即实现本地事务和消息发送的原子性,要么都成功,要么都失败。本地事务与消息发送的原子性问题是实现可靠消息最终一致性方案的关键问题。

  • 事务参与方接收消息的可靠性事务参与方必须能够从消息队列接收到消息,如果接收消息失败可以重复接收消息。

  • 消息重复消费的问题由于网络2的存在,若某一个消费节点超时但是消费成功,此时消息中间件会重复投递此消息,就导致了消
    息的重复消费。要解决消息重复消费的问题就要实现事务参与方的方法幂等性。

3. 本地消息表方案

本地消息表这个方案最初是eBay提出的,此方案的核心是通过本地事务保证数据业务操作和消息的一致性,然后通过定时任务将消息发送至消息中间件,待确认消息发送给消费方成功再将消息删除。下面以注册送积分为例来说明,下例共有两个微服务交互,用户服务和积分服务,用户服务负责添加用户,积分服务负责增加积分。

4. RocketMQ事务消息方案

在RocketMQ4.3后实现了完整的事务消息,实际上其实是对本地消息表的一个封装,将本地消息表移动到了MQ内部,解决Producer端的消息发送与本地事务执行的原子性问题。

八、最大努力通知

1. 流程

以充值的通知为例

  • 账户系统调用充值系统接口。

  • 充值系统完成支付处理向账户系统发起充值结果通知若通知失败,则充值系统按策略进行重复通知。

  • 账户系统接收到充值结果通知修改充值状态。

  • 账户系统未接收到通知会主动调用充值系统的接口查询充值结果。

2. 目标

目标是发起通知方通过一定的机制最大努力将业务处理结果通知到接收方。具体包括:

  • 有一定的消息重复通知机制。因为接收通知方可能没有接收到通知,此时要有一定的机制对消息重复通知。
  • 消息校对机制。如果尽最大努力也没有通知到接收方,或者接收方消费消息后要再次消费,此时可由接收方主动向通知方查询消息
    信息来满足需求。

3. 与可靠消息一致性的差异

  • 解决方案思想不同。可靠消息一致性,发起通知方需要保证将消息发出去,并且将消息发到接收通知方,消息的可靠性关键由发起通知方来保证。最大努力通知,发起通知方尽最大的努力将业务处理结果通知为接收通知方,但是可能消息接收不到,此时需要接收通知方主动调用发起通知方的接口查询业务处理结果,通知的可靠性关键在接收通知方。
  • 两者的业务应用场景不同。可靠消息一致性关注的是交易过程的事务一致,以异步的方式完成交易。最大努力通知关注的是交易后的通知事务,即将交易结果可靠的通知出去。
  • 技术解决方向不同。可靠消息一致性要解决消息从发出到接收的一致性,即消息发出并且被接收到。最大努力通知无法保证消息从发出到接收的一致性,只提供消息接收的可靠性机制。可靠机制是,最大努力的将消息通知给接收方,当消息无法被接收方接收时,由接收方主动查询消息(业务处理结果)。

以上是关于学习笔记事务与分布式事务的主要内容,如果未能解决你的问题,请参考以下文章

学习笔记事务与分布式事务

java事务学习笔记--深度剖析JTA原理与实现

《分布式技术原理与算法解析》学习笔记Day06

微服务架构 | 11. 分布式事务 #yyds干货盘点#

读书笔记丨理解和学习事务,让你更好地融入云原生时代

10分钟说透Saga分布式事务