TX-LCN原理

TX-LCN的核心控制流程

协调控制流程

在这里插入图片描述

  1. 事务发起方也是一个事务模块,它去调用TM创建一个事务组,这个事务组的数据就会在TM下存储(目前TM数据是存在Redis下),目前框架中TM数据是存在Redis下。

  2. 为了简化流程,参与方A是先执行业务再加入事务组,这样当参与方A在执行业务过程中出现异常就没有必要再加入事务组了,此时事务发起方会捕获该异常然后直接通知事务回滚。如果参与方A先加入事务组再执行业务,则A出现异常时再通过事务组去通知它,会多一个来回。

    执行完“加入事务组”之后,TM就把参与方A的状态记录下来。参与方A会给事务发起方返回数据(response)

  3. 参与方B与参与方A类似,也是由事务发起方调用,执行完业务再加入事务组,最后给事务发起方返回数据。

  4. 只要没有抛异常,事务发起方就视为要提交本次事务,此时通知事务管理器,让其知道是要提交还是回滚。

    TM会遍历这个事务组下关联的事务参与方信息,去通知各参与方要执行的状态(提交还是回滚)。

  5. 当TM拿到各参与方返回的信息时,才会响应给事务发起方,告知事务已处理完毕。事务发起方拿到TM的这个响应之后,就可以响应给调用者了。

核心步骤

  • 创建事务组
    是指在事务发起方开始执行业务代码之前先调用TxManager创建事务组对象,然后拿到事务标示GroupId的过程。
  • 加入事务组
    添加事务组是指参与方在执行完业务方法以后,将该模块的事务信息通知给TxManager的操作。
  • 通知事务组
    是指在发起方执行完业务代码以后,将发起方执行结果状态通知给TxManager,TxManager将根据事务最终状态和事务组的信息来通知相应的参与模块提交或回滚事务,并返回结果给事务发起方。

各事务模式的原理

LCN目前支持的三种事务模式 TCC、TXC、LCN。虽然分为这三种模式,但不会影响控制流程。控制流程是协调所有事务参与方。

举例表结构

idnamebalacne
1A200
2B100

TCC(Try Confirm Cancle)业务处理

原理

TCC事务机制相对于传统事务机制(X/Open XA Two-Phase-Commit),其特征在于它不依赖资源管理器(RM)对XA的支持,而是通过对(由业务系统提供的)业务逻辑的调度来实现分布式事务。
主要有三步操作 Try:尝试执行业务、 Confirm:确认执行业务、Cancel:取消执行业务。

举例 实现A转账给B

//尝试方法 - 对应流程图里的“执行业务”
function try(){
    //记录日志
    todo save A 转出了 100 元 
    todo save B 转入了 100//执行转账
    update amount set balacne = balacne-100 where id = 1
    update amount set balacne = balacne+100 where id = 2
}
//确认方法 - 对应流程图里的“通知事务单元”的“提交”
function confirm(){
    //清理日志
    clean save A 转出了 100 元 
    clean save B 转出了 100}

//取消方法 - 对应流程图里的“通知事务单元”的“回滚”
function cancle(){
    //加载日志
    load log A
    load log B

     //退钱
    update amount set balacne = balacne+100 where id = 1
    update amount set balacne = balacne-100 where id = 2    
}

特点

  • 该模式对代码的嵌入性高,要求每个业务需要写三种步骤的操作。

  • 该模式对有无本地事务控制都可以支持,使用面广。

    分布式框架有些是没有关系型数据库(比如nosql数据库),它本身没有事务的概念,但又参与到分布式的业务线,它只能采用TCC的方式来完成统一的数据控制。

  • 数据一致性控制几乎完全由开发者控制,对业务开发难度要求高。

TXC逆向SQL

原理

TXC模式命名来源于淘宝,实现原理是在执行SQL之前,先查询SQL的影响数据,然后保存执行的SQL数据信息和创建锁。当需要回滚的时候就采用这些记录数据回滚数据库,目前锁实现依赖redis分布式锁控制。

在这里插入图片描述当业务执行到这个方法时,首先切面拦截到它将要执行的SQL,拦截并解析,查询出受影响的数据,并记录下被修改前的数据。

特点

  • 该模式同样对代码的嵌入性低。
  • 该模式仅限于对支持SQL方式的模块支持。
  • 该模式由于每次执行SQL之前需要先查询影响数据,因此相比LCN模式消耗资源与时间要多。
  • 该模式不会占用数据库的连接资源,但中间状态可见(如果不加全局锁的话)

要考虑一种特殊情况:表没有主键id,那如果事务回滚,将数据update成记录的样子时,where条件匹配不合理,可能会产生脏数据。

全局锁就是 各个资源方在去操作一条记录时,要对这条记录做一个锁定。TM通知各个事务参与方提交或回滚之前,这个锁不解放,这便屏蔽了别人访问这些数据的中间状态的可能。但加了这个锁有个缺点是影响整体性能。

LCN代理连接

原理

LCN模式是通过代理Connection的方式实现对本地事务的操作,然后在由TxManager统一协调控制事务。当本地事务提交回滚或者关闭连接时将会执行假操作,该代理的连接将由LCN连接池管理。

在这里插入图片描述有两个切面,一个是执行业务之前,一个是事务提交之前。
执行业务之前 会拦截拿到连接对象,对这个对象做一个代理,再返回给业务。业务代码在执行过程中是用的代理的连接对象。这个代理占有这个连接对象不关闭,假提交 假回滚。等TM通知的时候再去进行提交或回滚。

特点

  • 该模式对代码的嵌入性低。
  • 该模式仅限于本地存在连接对象且可通过连接对象控制事务的模块(JDBC)。
  • 该模式下的事务提交与回滚是由本地事务方控制,对于数据一致性上有较高的保障。
  • 该模式缺陷在于代理的连接需要随事务发起方一共释放连接,增加了连接占用的时间。

最后一条现在的解决方案是固定一部分连接用于LCN的代理,与业务执行的连接区分开来。

负载问题

负载情况下的事务控制,对于无状态的TXC TCC来说是不需要关心事务的。但是对LCN来说需要考虑负载调用同一个模块时若模块不同会可能触发锁的问题。

若存在这样的请求链,A模块先调用了B模块的one方法,然后再调用了two方法,
A ->B.one(); A ->B.two();
假如one与two方法的业务都是在修改同一条数据,假如两个方法的id相同,伪代码如下:

void one(id){
   execute => update demo set state = 1 where id = {id} ;
}

void two(id){
   execute => update demo set state = 2 where id = {id} ;
}

若B模块做了集群 存在B1、B2两个模块,那么就可能出现A分别调用了B1 B2模块
A ->B1.one(); A ->B2.two();
在这样的情况下,第一次调用B1执行业务修改了该条数据,第二次调用B2就会因为行级锁而导致B2执行失败,即:业务方将在LCN下会因为资源占用而导致执行失败而回滚事务。

为了支持这样的场景,框架提供了重写了rpc的负载模式,控制在同一次事务下同一个被负载的模块被重复调用时将只会请求到第一次被选中的模块。优点是B模块共用连接,不会在B1和B2两个模块建立两条数据库连接,只会使用一条。两个线程分别到B1和B2两个模块的话 极有可能拿到不同的连接对象。

保障机制与补偿

超时机制

当业务模块在接受到事务请求,并完成响应以后会自动加入定时任务,等待TM通知,若TM迟迟不通知则触发TC主动请求的状况,若TC没有请求到数据就记录补偿(回滚事务)。

解释:

TM在通知业务模块的时候 可能由于TM本身出了问题或者网络情况而导致没有通知到业务模块。

在业务模块执行完业务response数据给事务发起方时,会启动一个定时任务,若到了时间还没有接收到TM的通知,则业务模块会主动去请求TM。若主动请求不通,则做补偿。

TM清理机制

TM全局都在记录着事务的状态消息,只有当TM确认完全都通知到了TC模块才能清除事务信息,不然将永久保存。
"通知到了"是指TM发送给业务模块通知(无论是提交还是回滚),并成功拿到了业务模块给出的响应。

一些特殊的情况介绍

  1. 通知事务的时候通知不到。
    通知不到会触发超时机制,超时机制分为两种可能 1.联系不上TM 不清楚事务状态;2.提前询问了TM,业务还没有确认最终状态。
    对于第1种情况,只能做事务提交或回滚,事实上TX-LCN是做事务回滚,然后记录补偿。记录补偿会写清楚什么时间什么状态下把该事务回滚了但是并不清楚该事务真正是需要提交还是回滚;
    对于第2种情况,TM只能回应“再等等 不能轻举妄动”。

  2. 通知事务组执行时没有响应。

    有两种可能的原因 1.网络等原因导致没有通知到 2.模块挂掉了,无法通知了。
    导致两种不清楚的信息 1.不清楚有没有执行完业务(可能执行了一半挂掉了,可能执行完了但在返回消息之前挂掉);2.不清楚有没有收到消息

  3. 若业务模块挂掉了,TM的日志在全部确认清楚之前,不能清理事务数据,TM清理数据需要全部都确认OK方可清理。

需要补偿的情况

由上述一些特殊情况可见,需要补偿的情况有

  1. 上面的情况1中对联系不上TM的情况需要记录补偿记录。
  2. 上面的情况2、3中描述的场景可能会存在业务模块在没有接收到TM消息的时候联系不上了。
    若是服务挂了,那么就要在下次服务启动时通过切面日志来与TM通讯确认状态,然后再执行业务。
    若是通讯出现了故障,那么会触发超时机制自动写补偿日志。

“若是服务挂了,那么就要在下次服务启动时通过切面日志来与TM通讯确认状态,然后再执行业务。”
因为这种情况存在,所以只能在切面进入时记录数据,当出现时可通过切面记录来触发业务,然后再补偿事务。

可以在参与方执行业务之前记录一个日志,记录对于这个业务方法切面的序列化数据,就是对于整个业务描述的一个详细数据。这样当这个模块在通知的时候挂掉而不知情的情况下,对于LCN的模式,只有通过对于全面的模拟事务的切面,才有可能再次执行这业务逻辑。
目前TX-LCN对这块做了一个优化,不在事务参与方执行业务前记录切面,而是在事务发起方记录切面。只要记录了事务发起方的切面,则模拟事务发起方的请求就可以还原一次事务的调用过程。在还原的过程中通过TM的配合,就可以对各个模块做相应的提交与回滚的处理。比如事务发起方和事务参与方A都提交了,事务参与方B回滚了,在再模拟一次调用过程时就让事务发起方和事务参与方A都回滚,让事务参与方B提交。
如果在各个参与方切面去记录的话,一是会影响性能,二是数据量会比较大。

还有一种情况是 服务已经接收到TM的消息,然后执行完提交或回滚,只是没有给TM响应消息。此时TM数据虽然没有清理,但是完全可以通过参与方本地知道该事务是有没有已经执行完了提交或回滚。但要知道数据有没有提交的前提,还得是通过TM看到有没有情况,TM会有一个补偿记录,此处的补偿记录与上方讲的超时补偿记录的差异是 此处不会记录得很清楚。
超时的补偿情况是业务完全执行完,记录清楚这个补偿是什么情况,在目前的LCN框架中,如果是记录清楚了补偿,当参与方联系上TM时,会把补偿的数据告诉TM,然后TM会记录一条补偿记录,这种会记录清楚整个方法执行失败的原因。
但对于参与方自己的业务执行完毕(提交或回滚),却响应信息没有通知到TM,这种TM虽然有记录,但不会有补偿的记录存在。这种情况就可以判断为参与方已经成功执行了提交或回滚,因为它没有提交补偿上来。

补偿出现的处理机制

  1. 自动补偿 (需要开启)

    如果开启了自动补偿,就会通过程序的方式去进行自动补偿。比如对于超时,参与方本地记录了这个补偿记录,TM也有记录,这种情况TM事务的状态数据是没有删的,所以可以通过 事务的状态数据 以及 参与方操作完是提交还是回滚的状态(超时实际上是回滚的) 比较是否一致,来判断要对该参与方做什么操作。此处举例的超市情况,该参与方理应是提交 但实际回滚了,TM会模拟一次请求链,让该参与方提交事务,其他模块回滚事务。如果本身这个参与方理应就是回滚的,则本身就是一致的,TM就不执行一次模拟了,TM直接把记录删除。也就是说TM这种补偿记录存在也并不是代表一定需要去做补偿,

  2. 手动补偿 (自行处理删除补偿记录即可)

    手动补偿就是工程师自己去找原因,通过TM查到关于补偿的数据,然后再手动对它进行补偿。

参考

https://www.codingapi.com/blog/2020/02/01/txlcn002/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值