欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

分布式事务,解决方案

程序员文章站 2023-03-26 20:18:37
"聊聊分布式事务,再说说解决方案" "分布式事务CAP理解论证 解决方案" "分布式系统的2PC、3PC详细分析" "github tcc示例" "分布式事务、重复消费、顺序消费" 一、理论 CAP相关: CAP与BASE相关: "我的博客" 而对于分布式中的问题的解决方案,CAP原则出现,描述如下 ......


分布式事务cap理解论证-解决方案
分布式系统的2pc、3pc详细分析

一、理论

cap相关:

cap与base相关:我的博客

而对于分布式中的问题的解决方案,cap原则出现,描述如下:

一致性(consistency):

像a节点写入一条信息之后,同一时刻,在其他节点都可以读到这条信息

可用性(availability):

多布一些节点a,b,c…,任何时刻,用户访问,都应该以可预期的结果返回,而不是浏览器报错,404,500,页面丢失…等用户体验不好的情况发生

分区容忍性(partitiontolerance):

当各系统模块间通信出现问题时,设计一个策略,使系统仍可对外提供满足一致性或可用性

刚接触cap时,有些不理解分区容忍性,我们自己倒推一下:

  1. 为了保证一致性,我们需要各个节点同步消息
  2. 为了保证可用性我们可以多部署节点,部分节点挂了仍可对外提供服务
  3. 为了保证分区容忍性:此刻卡壳了,怎么做?没了一种具体的方式,然而他还是客观存在的。后来发现:进入了思维盲点:只要在分布式场景中,分区必然存在,那么如果不处理分区发生时的情况,节点无法通讯时会发生什么?–此刻如果仍对外提供服务,那么导致无法同步消息,即保证不了强一致性;如果要保证强一致性,那么就需要节点阻塞,一直等待通讯恢复,即保证不了可用性.

所以分区容忍性就是:当发生分区问题时,我们使用策略,在一致性和可用性二者间选择
注意: 无法通信包括网络问题,或者节点机器宕机

误区: cap理论中说三者不可兼得,但实际情况是,在分布式场景中分区一定存在,即必须有分区容忍性对应的策略,之后才能在一致性和可用性间二者之间选择.所以对主流架构来说不是三选二,而是二选一。

对p的理解

很多人可能对分区容忍性不太理解,知乎有一个回答对这个解释的比较清楚cap理论中的p到底是个什么意思?,这里引用一下:

  • 一个分布式系统里面,节点组成的网络本来应该是连通的。然而可能因为一些故障,使得有些节点之间不连通了,整个网络就分成了几块区域。数据就散布在了这些不连通的区域中。这就叫分区。
  • 当你一个数据项只在一个节点中保存,那么分区出现后,和这个节点不连通的部分就访问不到这个数据了。这时分区就是无法容忍的。
  • 提高分区容忍性的办法就是一个数据项复制到多个节点上,那么出现分区之后,这一数据项就可能分布到各个区里,容忍性就提高了。
  • 然而,要把数据复制到多个节点,就会带来一致性的问题,就是多个节点上面的数据可能是不一致的。
  • 要保证一致,每次写操作就都要等待全部节点写成功,而这等待又会带来可用性的问题。
  • 总的来说就是,数据存在的节点越多,分区容忍性越高,但要复制更新的数据就越多,一致性就越难保证。为了保证一致性,更新所有节点数据所需要的时间就越长,可用性就会降低。

xa规范:


xa规范中,事务管理器主要通过以下的接口对资源管理器进行管理

  • xa_open,xa_close:建立和关闭与资源管理器的连接。
  • xa_start,xa_end:开始和结束一个本地事务。
  • xa_prepare,xa_commit,xa_rollback:预提交、提交和回滚一个本地事务。
  • xa_recover:回滚一个已进行预提交的事务。

xa规范:

解决方案

  • 维护本地消息表
  • 使用rocketmq事务消息:
  • 两阶段提交协议(2pc)
  • tcc事务补偿机制

使用限制:

a. xa事务和本地事务以及锁表操作是互斥的

开启了xa事务就无法使用本地事务和锁表操作:

mysql> xa start 't1xa';
query ok, 0 rows affected (0.04 sec)
mysql> begin;
error 1399 (xae07): xaer_rmfail: the command cannot be executed when global transaction is in the active state
mysql> lock table t1 read;
error 1399 (xae07): xaer_rmfail: the command cannot be executed when global transaction is in the active state

开启了本地事务就无法使用xa事务:

mysql> begin;
query ok, 0 rows affected (0.00 sec)
mysql> xa start 'rrrr';
error 1400 (xae09): xaer_outside: some work is done outside global transaction

b. xa start 之后必须xa end, 否则不能执行xa commit 和xa rollback

所以如果在执行xa事务过程中有语句出错了,你也需要先xa end一下,然后才能xarollback。

注意事项:

a. mysql只是提供了xa事务的接口,分布式事务中的mysql实例之间是互相独立的不感知的。 所以用户必须
自己实现分布式事务的调度器

b. xa事务有一些使用上的bug, 参考http://www.mysqlops.com/2012/02/24/mysql-xa-optimize.html

主要是:
“mysql数据库的主备数据库的同步,通过binlog的复制完成。而binlog是mysql数据库内部xa事务的协调者,并且mysql数据库为binlog做了优化——binlog不写prepare日志,只写commit日志。
所有的参与节点prepare完成,在进行xa commit前crash。crash recover如果选择commit此事务。由于binlog在prepare阶段未写,因此主库中看来,此分布式事务最终提交了,但是此事务的操作并未 写到binlog中,因此也就未能成功复制到备库,从而导致主备库数据不一致的情况出现。
而crash recover如果选rollback, 那么就会出现全局不一致(该分布式事务对应的节点,部分已经提交,无法回滚,而部分节点回滚。最终导致同一分布式事务,在各参与节点,最终状态不一致)”

参考的那篇blog中给出的办法是修改mysql代码,这个无法在dbscale中使用。 所以可选的替代方案是不使用
主从复制进行备份,而是直接使用xa事务实现同步写来作为备份。

二、两阶段提交2pc

1. 介绍

分布式事务,解决方案

两个角色:

  1. 协调者
  2. 参与者

两个阶段:

  1. 阶段一:提交事务请求
  2. 阶段二:执行事务提交

牺牲了一部分可用性来换取的一致性。解决方案有:springboot+atomikos or bitronix

优点: 原理简单,实现方便

缺点:

  1. 同步阻塞:在提交的过程中,所有参与者都处于阻塞状态,大大降低并发度
  2. 单点问题:一旦协调者出现问题,则所有参与者处于锁定状态,无法对外服务
  3. 数据不一致:在阶段二,协调者发送了commit之后,发生了局部网络异常或者协调者尚未发送完commit请求就宕机了,导致部分参与者收到commit,导致系统出现不一致
  4. 太过保守:协调者在阶段一中,参与者出现故障而导致协调者无法获取到所有参与者的响应,协调者只能依靠超时时间来判断是否中断事务。换句话说,没有完善的容错机制。

2. 实现

jta(java transaction api)定义了对xa事务的支持。像很多其他的java规范一样,jta仅仅定义了接口,具体的实现则是由供应商(如j2ee厂商)负责提供,目前jta的实现主要有以下几种:

  • j2ee容器所提供的jta实现(如jboss)。
  • 独立的jta实现:如jotm(java open transaction manager),atomikos。这些实现可以应用在那些不使用j2ee应用服务器的环境里用以提供分布事事务保证。

mysql中的xa实现分为:外部xa和内部xa。前者是指我们通常意义上的分布式事务实现;后者是指单台mysql服务器中,server层作为tm(事务协调者),而服务器中的多个数据库实例作为rm,而进行的一种分布式事务,也就是mysql跨库事务;也就是一个事务涉及到同一条mysql服务器中的两个innodb数据库(因为其它引擎不支持xa)。

三、三阶段提交3pc

是二阶段的改进版,将二阶段的提交事务请求过程一分为二,形成了:

  1. cancommit:协调者发送事务询问、参与者反馈
  2. precommit:协调者发送预提交请求、参与者事务预提交(执行事务操作,写undo、redo日志)、参与者响应
  3. docommit:协调者发送提交请求、参与者事务提交(事务提交,释放资源)、参与者响应

在阶段二中,参与者可能会响应no,或者协调者等待超时时间后还无法收到所有参与者的反馈,则中断事务:协调者向所有参与者发送abort请求。参与者无论是收到协调者的abort请求,或者等待协调者请求过程中超时,都会中断事务。

在阶段三中,如果有任一参与者发送了no,或者等待超时后协调者还没收到所有参与者的反馈,则中断事务。需要注意的事,进入阶段三,可能会有下面两种故障:

  • 协调者出现问题
  • 协调者、参与者之间的网络出现问题

无论哪种情况,都会导致参与者无法及时收到来自协调者的docommit或者abort请求,这种情况,参与者在等待超时后继续进行事务提交。

优点:

  1. 降低了参与者的阻塞范围(二阶段中如果参与者与协调者断开,参与者abort;三阶段,提交),并且能够在单点故障后继续达成一致。

缺点:

  1. 参与者在收到precommit后出现网络分区,参与者依然会提交事务,会造成不一致。

四、实现

todo