简介
TX-LCN框架,里面集成了LCN、TCC、TXC三种事务模式,并且TX-LCN是中国开发的开源框架,在源码里面可以看到有中文注释,主要使用LCN模式
框架主要由事务参与者(TxClient)、事务管理者(TxManager)两部分构成,事务控制的原理图如下:

由图中我们就能理解TX-LCN框架的原理了,第四步可以作为阶段分割线,第四步上面所有操作就像是2PC的第一阶段,第四步及下面所有操作就像是2PC的第二阶段,如果大家忘记了2PC的话,可以看一下我写的前一篇博客【分布式事务讲解 - 2PC、3PC】,这个时序图其实就是框架源码流程的翻译,源码中也用了很多巧妙的手法来实现整体事务的工作流程,这些我会在下面介绍旗下三种模式的时候给大家一起讲解。
1、创建事务组
是指在事务发起方开始执行业务代码之前先调用TxManager创建事务组对象,然后拿到事务标示GroupId的过程。2、加入事务组
添加事务组是指参与方在执行完业务方法以后,将该模块的事务信息通知给TxManager的操作。
3、通知事务组
是指在发起方执行完业务代码以后,将发起方执行结果状态通知给TxManager,TxManager将根据事务最终状态和事务组的信息来通知相应的参与模块提交或回滚事务,并返回结果给事务发起方。
结合案例分析其步骤:
订单服务为事务发起方,会在开始执行业务代码之前先调用TxManager创建事务组对象,然后拿到事务标示GroupId
当订单服务调用rpc远程调用支付服务之前(类似于图中发起方请求模块A),lcn会拦截请求,并将groupId放入到请求头中
当请求到达支付服务之前,请求会被拦截,判断请求头中是否含有groupId,如果有则是参与方,并且根据GroupId注册到TxManage的同一个事务组中
支付服务在执行业务代码之前,lcn会代理其数据源
支付服务和商品服务会在执行完业务方法以后,由于数据源被代理,本地事务不会立即提交。而是被代理假关闭
当订单服务业务执行完毕,并且提交本地事务,会将本地事务的结果通知给TxManager
TxManager去遍历事务组,取出其中的事务单元,并进行一一通知。
参与方接收到通知以后,再进行相应的事务提交或者回滚操作,保持数据的一致性
1、优点
保证数据的强一致性
2、缺点
可能会造成死锁的现象,比如,订单服务调用派单服务成功以后,订单服务还没执行完毕就宕机,此时,TxManage并没有收到通知,派单服务的事务也不能顺利进行,导致死锁。 lcn的性能不是特别强大
LCN 原理及主要特点
LCN模式使用了代理技术,把本地事务的数据库连接维持住不释放,通过TxManager来统一控制事务。
在本地事务中,对事务进行的commit/rollback/close操作都是假操作,大家再远那种可以看到代理后的数据库连接中的commit/rollback/close方法都是空方法,限制本地事务进行除执行SQL以外的其他操作。
可以说在本地事务执行SQL之后就会一直保持数据库连接,直到TxManager发来提交或者回滚操作才会使本地事务释放数据库连接,这样增加了数据库连接的占用时长。
最后一阶段的TxManager发出的提交或者回滚命令是通过Netty发送的,会循环所有TxClient并发送指令。
和seata对比
TX-LCN和Seata两种分布式事务各有优点。
LCN是采取代理数据源的模式,再根据发起方执行本地事务的结果进行回滚和提交。可以保证强一致性,但可能发生死锁的现象。
Seata采取的是根据undo_log日志表,进行逆向生成sql语句,来解决回滚。Seata能保证最终一致性,但可能造成脏读。
其实所有的分布式事务方案都不完美。一致性C、可用性A、分区容忍性P,只能同时满足两个。
seata

seata的原理分析 和上面一样,订单服务为发起方、派单服务为参与方
发起方(TM)和参与方(RM)项目启动后会和协调者(TC)保持长连接
发起方(TM)在调用参与方(TC)之前,会向协调者(TC)申请一个全局事务id(Xid),并保存到ThreadLocal中
发起方(TM)和参与方(RM)都会被seata代理数据源,利用aop在执行insert、update、delete语句之前和之后生成前置镜像和后置镜像,并写入到undo_log表中
发起方(TM)重写feign客户端请求,将全局事务id保存到请求头中传递给参与方(RM),参与方(RM)获取全局事务id,并在协调者(TC)中注册该分支
发起方(TM)调用参与方(RM)接口成功以后,执行本地业务代码也成功后,会将发起方(TM)本地事务结果commit通知给协调者(TC),协调者(TC)再将事务结果通知给给各事务分支(即参与方(RM))。
发起方(TM)调用参与方(RM)接口成功以后,执行本地业务代码失败,此时会产生分布式事务问题,发起方(TM)本地事务结果rollback通知给协调者(TC),协调者(TC)再将事务结果通知给给各事务分支(即参与方(RM))。
参与方(RM)接收到事务结果通知,如果是commit的情况下,代表事务执行成功,删除掉undo_log中对应的分支id的记录即可
参与方(RM)接收到事务结果通知,如果是rollback的情况下,代表事务执行失败,根据undo_log日志,逆向生成sql语句,比如先前是插入,现在就是删除。去删除掉相应的记录,完成以后,再删掉undo_log日志对应的记录。
什么是前置镜像和后置镜像
前置镜像:select * from order where orderId = 66; 并将查询的结果保存起来 state = 0;
update order set state = 1 where orderId=66;
后置镜像:select * from order where orderId = 66; 更改后的数据进行记录 state = 1;
当我们需要回滚的时候,就可以根据前置镜像和后置镜像保存的记录,进行逆向生成sql语句回滚
update order set state = 0 where orderId = 66;
当发起方调用参与方成功以后,发起方宕机了怎么办?
当发起方(TM)调用参与方成功(RM)以后,发起方(TM)宕机
协调者(TC)会检测发起方(TM)是否还存在(会多次重试连接发起方(TM)), 如果发现发起方(TM)已经宕机,则会将本次事务结果记录为rollback,并且通知参与方(RM)
参与方(RM)接收到协调者(TC)发来的消息,根据undo_log日志进行回滚
当发起方(TM)重启后,会接收到协调者(TC)的消息通知,将上一次宕机执行的业务也进行rollback(如果接口相应超时,协调者(TC)还会进行重试)
1、优点
seata的性能比lcn要好 seata不会造成死锁的情况
2、缺点
seata没有管理化界面 seata会造成数据的脏读,不能保证数据的强一致性,只能保证最终一致性 需要在每个数据库都创建一个undo_log的日志表
3、seata和lcn比较,有什么不一致?
seata和lcn大致的实现思路是一致的,但是回滚的机制不一样。
lcn是采取代理数据源的模式,再根据发起方执行本地事务的结果进行回滚或者提交
seata采取的是根据undo_log日志表,进行逆向生成sql语句,来解决回滚
lcn能够保证强一致性,但可能发生死锁的现象
seata能保证最终一致性,但可能造成脏读