当前位置: 首页 > 后端技术 > Java

分布式事务(二)两阶段提交

时间:2023-04-01 19:39:44 Java

上一篇我们介绍了分布式系统中的CAP理论和BASE理论。本文将对分布式事务的一种实现方案:两阶段提交(2PC)进行介绍。2PC是一个非常经典的强一致性、集中式原子提交协议。中心化是指协议中有两类节点:一种是中心化的协调者节点(coordinator)和N个参与者节点(participant)。2PC一致性概念一致性是指每个节点更新一份数据。整个集群都知道更新并且是一致的。假设一个有N个节点的分布式系统,当满足以下条件时,我们说这个系统满足一致性:完全一致:所有N个节点都同意一个结果;合法值:结果必须由N个节点的半数以上提出;endable:解析过程在一定时间内结束,不会无休止地进行下去;一致性挑战消息传输异步无序:真实网络不是可靠通道,存在消息延迟和丢失,节点间消息传输无法同步有序:节点宕机:节点持续宕机,不会恢复;节点宕机恢复:节点在停机一段时间后恢复,在分布式系统中最常见;网络分化:网络链路问题,N个节点被隔离成多个部分;拜占庭将军问题:节点要么宕机要么逻辑失效,甚至不按套路抛出干扰解析的信息。2PC原则2PC(towphasecommit)两阶段提交。所谓两个阶段是指:第一阶段:准备阶段(投票阶段)第二阶段:提交阶段(执行阶段)。我们将提议节点称为协调者,将其他参与决策的节点称为参与者(或群组)。2PC的第一阶段2PC的第一阶段是投票环节。投票由协调节点发起,可以进一步细分为以下几个步骤:交易查询:协调节点向所有参与者发送交易预处理请求,称为Prepare,并开始等待每个参与者的响应。执行本地事务:每个参与节点执行本地事务操作,但执行完成后并不真正提交数据库本地事务,而是先向协调器报告:“我这里可以处理/我这里不能处理”。各参与者将交易查询的响应反馈给协调器:如果参与者成功执行了交易操作,则向协调器反馈Yes响应,表示交易可以执行,如果没有参与者成功执行交易,然后会反馈给协调器NoResponse,表示交易无法执行。第一阶段执行后,有两种可能。1.全部返回Yes。2.一个或多个返回号2PC第二阶段:正常提交如果第一阶段所有参与者都返回Yes,那么我们就可以继续执行2PC第二阶段的正常提交步骤:协调节点通知所有参与者提交事务请求;参与者收到Commit请求后,会正式执行本地事务的Commit操作,整个事务执行过程中占用的事务资源会在commit完成后释放。2PC第二阶段:异常回滚如果有任何参与者反馈给协调器Noresponse,或者等待超时后,协调器还没有收到所有参与者的反馈,那么我们就需要进行2PC第二阶段的回滚操作:协调节点通知所有参与者回滚请求;参与者收到Rollback请求后,会正式执行本地事务Rollback操作,并在commit完成后释放整个事务执行过程中所占用的事务资源。2PC中的问题通过上面的演示,很容易想到2pc带来的缺陷:性能问题:无论是在第一阶段还是第二阶段的过程中,所有参与者资源和协调者资源都被锁定,只有当所有节点就绪,事务协调器通知全局提交,参与者在本地事务提交后释放资源。这样的过程会比较长,对性能的影响也比较大。单节点故障:由于协调器的重要性,一旦协调器发生故障。参与者将永远被阻止。尤其是在第二阶段,如果协调器失效,所有参与者仍处于锁定事务资源的状态,无法继续完成事务操作。(虽然协调器挂掉了,可以重新选举一个协调器,但是不能解决由于协调器宕机导致参与者被阻塞的问题)。在2PC节点故障的情况下,协调器正常,参与者宕机。由于协调器无法收集所有参与者的反馈,因此会陷入阻塞的境地。解决方案:引入超时机制。如果协调者在规定的时间内没有收到参与者的反馈,交易将失败,并向所有节点发出终止交易的请求。协调器宕机,参与者正常无论在哪个阶段,因为协调器宕机,无法发送提交请求,所有处于执行操作但未提交状态的参与者都会被阻塞。解决方案:引入协调器备份,协调器需要记录操作日志。当检测到协调器宕机一段时间后,协调器备份替换协调器,读取操作日志,向所有参与者询问状态。coordinator和participants都down如果发生在第一阶段:因为在第一阶段,所有的participant都没有真正执行commit,所以只需要在剩余的participants中重新选举一个coordinator,新的coordinatorcoordinator重新执行Phase1和Phase2就可以了。第二阶段发生挂掉的参与者在挂掉之前没有收到协调器的指令:也就是上面第二步挂掉了,有可能协调器在发送第二步之前就挂了。在这种情况下,新的协调器重新执行第一阶段和第二阶段的操作。发生在第二阶段,部分参与者已经执行了commit操作:就像这里一样,订单服务A和支付服务B都收到协调者发送的commit信息,开始真正执行本地事务commit,但是意外情况下,AcommitSuccess,B挂机。此时,数据当前不一致。虽然这个时候他可以通过手段和协调器沟通,然后想办法让数据保持一致,但是这段时间他的数据状态已经不一致了!2PC无法解决这个问题。mysql对XA事务的支持MySQL从5.0.3开始支持XA分布式事务,只有InnoDB存储引擎支持。MySQLConnector/J从5.0.0版本开始直接提供了对XA的支持。需要注意的是,在DTP模型中,mysql属于资源管理器(RM)。在一个完整的分布式事务中,一般会有多个RM,由事务管理器TM进行协调。所以这里所说的mysql对XA分布式事务的支持,泛指单个mysql实例如何执行自己的事务分支。XA{START|BEGIN}xid[JOIN|RESUME]//开启XA事务,如果使用XASTART代替XABEGIN,不支持[JOIN|RESUME],xid为唯一值,表示事务分支标识XAENDxid[SUSPEND[FORMIGRATE]]//结束一个XA事务,不支持[SUSPEND[FORMIGRATE]]XAPREPARExid准备提交XACOMMITxid[ONEPHASE]//提交,如果使用ONEPHASE,则表示使用One-stagecommit。在两阶段提交协议中,如果只有一个RM参与,可以针对一阶段提交进行优化XAROLLBACKxid//回滚XARECOVER[CONVERTXID]//列出PREPARE阶段的所有XA事务JTA实现Java事务API(JTA:JavaTransactionAPI)及其同胞Java事务服务(JTS:JavaTransactionService),为J2EE平台提供分布式事务服务(distributedtransaction)能力。JTA规范在某种程度上可以看作是Java版的XA规范,将XA规范中规定的DTP模型交互接口抽象为Java接口中的方法,并规定了每个方法应该实现哪些功能。在DTP模型中,规定了模型的五个组成元素:Application、ResourceManager、TransactionManager、CommunicationResourceManager、CommunicationProtocol(通信协议)。在JTA规范中,模型中增加了一个额外的元素ApplicationServer,如下图:下面介绍JTA规范中模型中各个组件的作用:事务管理器:图中最核心的位置,其他事务参与者与事务管理器进行交互。事务管理器提供事务声明、事务资源管理、同步和事务上下文传播等功能。JTA规范定义了事务管理器与其他事务参与者交互的接口,而JTS规范定义了事务管理器的实现要求,所以我们看到事务管理器的底层是基于JTS的。应用服务器(applicationserver):顾名思义,就是应用程序运行的容器。JTA规范规定事务管理器的功能应该由应用服务器来提供,比如上图中的EJBServer。其他一些常见的web容器,如:jboss、weblogic、websphere等,都可以作为应用服务器,这些web容器都实现了JTA规范。特别要注意的是,并不是所有的Web容器都实现JTA规范。比如tomcat没有实现JTA规范,所以不能提供事务管理器的功能。Application:简单来说,就是我们自己编写的一个应用程序,部署到一个实现了JTA规范的应用服务器上。之后,我们可以使用JTA规范中定义的UserTransaction类声明分布式事务。通常,为了简化开发者的工作量,应用服务器并不一定要求开发者使用UserTransaction来声明一个事务。开发者可以在需要使用分布式事务的方法上加上注解,就像spring的声明式事务一样,声明一个分布式事务。特别是JTA规范规定,事务管理器的功能由应用服务器提供。但是如果我们的应用不是web应用,而是本地应用,就不需要部署到应用服务器上,应用服务器提供的事务管理器功能也无法使用。或者我们使用的web容器没有事务管理器的功能,比如tomcat。对于这些情况,我们可以直接使用一些第三方的事务管理器库,比如JOTM、Atomikos。将事务管理器直接集成到应用程序中,不再依赖于应用服务器。资源管理器(resourcemanager):理论上,任何可以存储数据的软件都可以认为是资源管理器RM。最典型的RM就是关系型数据库,比如mysql。另一种常见的资源管理器是消息中间件,比如ActiveMQ、RabbitMQ等,这些才是真正的资源管理器。事实上,将资源管理器称为资源适配器似乎更合适。因为在java程序中,我们都是通过client与RM进行交互的,例如:我们使用mysql-connector-java-x.x.x.jar驱动包来获取Conn,执行sql,与mysql服务器通信;通过ActiveMQ、RabbitMQ等客户端发送消息等。一般情况下,数据库驱动供应商只需要实现JDBC规范,消息中间件供应商只需要实现JMS规范即可。引入分布式事务的概念后,DB、MQ等在DTP模型中的作用就是RM。两者是等价的,需要TM来协调。为此,JTA规范定义了一个XAResource接口,该接口定义了一些RM必须提供给TM调用的方法。之后TM就不管RM是DB还是MQ了,因为它操作的是XAResource接口。其他规范(如JDBC、JMS)的实现者也实现了这个接口。例如,MysqlXAConnection实现了XAResource接口。CommunicationResourceManager:这是DTP模型中已经存在的概念。对于需要跨应用的分布式事务,事务管理器需要相互通信。这是通过CRM组件完成的。JTA规范中规定CRM需要实现JTS规范定义的接口。下图更直观地展示了JTA规范中各个模型组件是如何交互的:交互方式如下:应用程序运行在应用服务器中;应用程序需要访问资源管理器(RM)上的3个资源:1个MQ资源和2个DB资源;由于这些资源服务器是独立部署的,如果需要同时更新数据并保证一致性,就需要使用分布式事务,需要一个事务管理器来统一协调;ApplicationServer提供事务管理器的功能;作为资源管理器DB和MQ的客户端驱动包,都实现了XAResource接口供事务管理器调用。参考文章mysqlsupportsXAtransaction3.0JTAspecification。我是玉壶神。欢迎大家关注我的微信公众号:wzm2zsd