梵美传媒网站是谁做的,如皋网站建设,网站开发与app开发原理,设计师网站模版前言这篇文章将给大家介绍一下对分布式事务的一些见解#xff0c;并讲解分布式事务处理框架 TX-LCN 的执行原理#xff0c;错误之处望各位不吝指正。1. 什么情况下需要使用分布式事务#xff1f;使用的场景很多#xff0c;先举一个常见的#xff1a;在微服务系统中#x…前言这篇文章将给大家介绍一下对分布式事务的一些见解并讲解分布式事务处理框架 TX-LCN 的执行原理错误之处望各位不吝指正。1. 什么情况下需要使用分布式事务使用的场景很多先举一个常见的在微服务系统中如果一个业务需要使用到不同的微服务并且不同的微服务对应不同的数据库。打个比方电商平台有一个客户下订单的业务逻辑这个业务逻辑涉及到两个微服务一个是库存服务(库存减一)另一个是订单服务(订单数加一)示意图如下如果在执行这个业务逻辑时没有使用分布式事务当库存与订单其中一个出现故障时就很可能出现这样的情况库存数据库的值减少了 1但是订单数据库没有变化或是库存没变化多了一个订单也就是出现了数据不一致现象。所以在类似的场合下我们要使用分布式事务保证数据的一致性。2. 分布式事务的解决思路2.1引入MySQL 中的两阶段提交策略在谈分布式事务的解决思路之前我们先来看看单一数据源是如何做事务处理的我们可以从中获取一些启发。我们以 MySQL 的 InnoDB 引擎为例由于 MySQL 中有两套日志机制一套是存储层的 redo log另一套是 server 层的 binlog每次更新数据都要对两个日志进行更新。为了防止写日志时只写了其中一个而没有写另外一个MySQL 使用了一个叫两阶段提交的方式保证事务的一致性。具体是这样的假设创建一个这样的数据库mysql create table T(ID int primary key, c int); 然后执行一条这样的更新语句mysql update T set cc1 where ID2;这条更新语句的执行流程是这样子的首先执行器会找引擎取 ID2 这一行数据拿到数据后会把数据进行1 操作然后调用引擎接口把新数据写入引擎将数据更新到内存中并将操作记录到 redo log 里此时 redo log 处于 prepare 状态。但它不会提交事务只是通知执行器已经完成任务可以随时提交。执行器生成这个操作的 binlog并把 binlog 写入磁盘最后执行器调用引擎的事务接口把 redo log 改为提交状态更新完成。在上述过程中redo log 写完后没有直接提交而是处于 prepare 状态等通知执行器并把 binlog 写完后redo log 再进行提交。这个过程就是两阶段提交这是一个精妙的设计。可能你会问为什么要有两阶段提交如果不采用两阶段提交的话也就是使用一阶段提交那就相当于按顺序执行写 redo log 和 binlog如果写完 redo log 后系统出现了故障那么就会只有 redo log 记录了操作binlog 没有记录造成数据不一致使用两阶段提交的话假设写完 redo log 后系统出现了故障由于事务还没有提交所以可以顺利回滚。两阶段提交的设计还有什么好处首先要奠定一个概念一个操作执行的时间越长这个操作就越有可能失败。打个比方你吃饭要用 20 分钟上厕所要用 1 分钟在吃饭的过程中收到微信消息的概率肯定比去上厕所的过程中收到微信消息的概率大。由于在数据库中更新操作的时间要远大于提交事务的时间所以先把更新操作做完等所有耗时操作都做完最后再提交事务能够最大程度保证事务执行成功。2.2分布式事务的两阶段提交策略根据上述的两阶段提交策略分布式事务也可以采取类似的办法完成事务。在第一阶段我们要新增一个事务管理者的角色通过它来协调各个数据源。还是拿开头的订单案例讲解在执行下订单的逻辑时先让各个数据库去执行各自的事务比如从库存中减 1在订单库中加 1但是完成后不提交只是通知事务管理者已经完成了任务。到了第二阶段由于在阶段一我们已经收到了各个数据源是否就绪的信息只要有一个数据源没有就绪在第二阶段就通知所有数据源回滚如果全部数据源都已经就绪就通知所有数据源提交事务。总结一下这个两阶段提交的过程就是首先事务管理器通知各个数据源进行操作并返回是否准备好的信息。等所有数据源都准备好后再统一发送事务提交(回滚)的通知让各个数据源提交事务。由于最后的提交操作耗时极短所以操作失败的可能性会很低。那么这个两阶段提交协议可能存在什么缺点呢很可能存在被阻塞的问题假如其中一个数据源出现了某些问题阻塞了既不能返回成功信息也不能返回失败信息那么整个事务将被阻塞。对应的策略是添加一些倒计时的操作或者是重新发送消息。3. 分布式事务框架 TX-LCN讲了这么多理论的知识下面讲解一款真正应用在生产中的分布式事务框架 TX-LCN 的运行原理。(典型的分布式事务框架不止 TX-LCN比如还有阿里的 GTS不过 GTS 是收费的TX-LCN 是开源的)我们先看一下官方文档中给出的运行原理示意图思路和我们上面讲的两阶段分布式事务处理流程差不多(有小不同)核心步骤分为 3 步创建事务组在事务发起方开始执行业务代码之前先调用 TxManager 创建事务组对象然后拿到事务表示 GroupId 的过程。简单来说就是对这次下订单的操作在事务管理中心里创建一个对象拿到一个 id。加入事务组参与方在执行完业务方法后将该模块的事务信息通知给 TxManager 的操作。也就是指各个数据源(各个服务)完成操作后和事务管理中心说一声注册一下自己。通知事务组发起方执行业务代码后将发起方执行结果状态通知给 TxManagerTxManager 将根据事务最终状态和事务组的信息来通知相应的参与模块提交或回滚事务并返回结果给事务发起方。和客户打交道的下订单服务会收到减库存和加订单是否成功消息它会把这两个消息通知给事务管理者事务管理者根据情况通知两个库存服务提交事务或回滚事务。目前网上有不错的 TX-LCN 执行源码分析文章:文章中跟着源码走一遍会发现和上面的流程图差不多落实到代码中有一些精彩的地方比如public Object runTransaction(DTXInfo dtxInfo, BusinessCallback business) throws Throwable { if (Objects.isNull(DTXLocalContext.cur())) { DTXLocalContext.getOrNew(); } else { return business.call(); } log.debug(); DTXLocalContext dtxLocalContext DTXLocalContext.getOrNew(); TxContext txContext; // ---------- 保证每个模块在一个DTX下只会有一个TxContext ---------- // if (globalContext.hasTxContext()) { // 有事务上下文的获取父上下文 txContext globalContext.txContext(); dtxLocalContext.setInGroup(true); log.debug(Unit[{}] used parents TxContext[{}]., dtxInfo.getUnitId(), txContext.getGroupId()); } else { // 没有的开启本地事务上下文 txContext globalContext.startTx(); } //......} 这段代码保证了每个模块下只会有一个 TxContext换个说法就是假设一个业务逻辑不是操作不同的数据源而是对同一个数据源执行多次相同的操作那么该数据源对应的模块在 DTX 下会只有一个 TxContextLCN 的事务协调机制LCN 的口号是LCN 并不生产事务LCN 只是本地事务的协调工。大家肯定会有个疑问它不生产事务那么它是怎么控制各个模块在完成事务的逻辑操作之后不马上提交而是等到 TxManager 最后一起通知各模块提交的呢因为每个模块都是一个 TxClient每个 TxClient 下都有一个连接池是框架自定义的连接池对 Connection 使用静态代理的方式进行包装。public class LcnConnectionProxy implements Connection { private Connection connection; public LcnConnectionProxy(Connection connection) { this.connection connection; } /** * notify connection * * param state transactionState * return RpcResponseState RpcResponseState */ public RpcResponseState notify(int state) { try { if (state 1) { log.debug(commit transaction type[lcn] proxy connection:{}., this); connection.commit(); } else { log.debug(rollback transaction type[lcn] proxy connection:{}., this); connection.rollback(); } connection.close(); log.debug(transaction type[lcn] proxy connection:{} closed., this); return RpcResponseState.success; } catch (Exception e) { log.error(e.getLocalizedMessage(), e); return RpcResponseState.fail; } } Override public void setAutoCommit(boolean autoCommit) throws SQLException { connection.setAutoCommit(false); } //......} 连接池在没有接收到通知事务之前会一直占有着这次分布式事务的连接资源。等到最后 TxManager 通知 TxClient 时TxClient 才会去执行相应的提交或回滚。所以 LCN 的事务协调机制相当于是拦截了一下连接池控制了连接的事务提交。imageLCN 的事务补偿机制由于我们不能保证事务每次都正常执行如果在执行某个业务方法时本应该执行成功的操作却因为服务器挂机或网络抖动等问题导致事务没有正常提交这种场景就需要通过补偿来完成事务。在这种情况下 TxManager 会做一个标示;然后返回给发起方。告诉他本次事务有存在没有通知到的情况然后 TxClient 再次执行该次请求事务。最后欢迎大家关注我的公众号【程序员追风】文章都会在里面更新整理的资料也会放在里面。作者程序员追风链接https://www.jianshu.com/p/2973fbd8c8af来源简书著作权归作者所有。商业转载请联系作者获得授权非商业转载请注明出处。