分布式事务处理入门
分布式事务的实现方法

分布式事务的实现方法
分布式事务的实现方法有多种,以下是其中几种常见的方案:
1. 两阶段提交(XA 方案):事务管理器先询问各个数据库是否准备好了,每个要操作的数据库都回复事务管理器ok,那么就正式提交事务。
2. TCC方案:TCC的全称是:Try、Confirm、Cancel。
Try阶段是对各个服务的资源做检测以及对资源进行锁定或者预留;Confirm阶段是在各个服务中执行实际的操作;Cancel阶段是如果任何一个服务的业务方法执行出错,那么就需要进行补偿,即执行已经执行成功的业务逻辑的回滚操作。
一般来说,支付、交易等需要严格保证资金正确性的场景会使用TCC方案。
3. 可靠消息最终一致性方案:通过消息队列将分布式事务中的操作进行异步解耦,从而实现最终的一致性。
这种方式能够处理大量并发操作,但是可能会存在数据不一致的风险。
4. 最大努力通知方案:通过最大努力的方式将通知发送给其他服务,以实现分布式事务的一致性。
这种方式简单易行,但是可能会因为网络问题或者服务不可用等原因导致通知失败。
5. SAGA方案:SAGA是一种基于事务的分布式事务处理模型,它将一个分布式事务视为一系列本地事务的组合。
每个本地事务在一个单独的节点上执行,并且通过消息传递进行通信。
SAGA能够保证全局事务的最终一致性,同时具有较好的容错性和可恢复性。
以上是分布式事务的几种常见实现方案,每种方案都有其优缺点,需要根据具体的业务场景和需求来选择适合的方案。
分布式事务解决方案之TCC

分布式事务解决方案之TCCTCC(Try-Confirm-Cancel)是一种分布式事务解决方案,它通过将一个大事务分解为三个小事务来解决分布式系统中的一致性问题。
在TCC 中,每个小事务都有三个阶段:Try(尝试)、Confirm(确认)和Cancel(取消)。
Try阶段是指在执行事务之前,先尝试预留资源和锁定资源。
在这个阶段,应用程序会执行一些检查,验证所有需要的资源是否可用,并预留这些资源,以确保后续的事务操作可以成功执行。
Confirm阶段是指在Try阶段所有资源预留成功后,执行真正的事务操作。
在这个阶段,应用程序会执行真正的业务逻辑,对事务进行处理,并将事务状态从“预备”状态转变为“已提交”状态。
Cancel阶段是指在Try阶段出现错误或者其他异常情况时,回滚事务所做的操作。
在这个阶段,应用程序会执行撤销操作,释放之前预留的资源,并将事务状态从“预备”状态转变为“已撤销”状态。
TCC解决分布式事务的核心思想是通过拆分大事务为多个小事务,在每个小事务中保证数据一致性,并且通过确认和撤销操作来保证事务的最终一致性。
这种方式可以避免传统的二阶段提交等集中式事务管理方式的性能问题和可扩展性问题。
TCC的优点有以下几点:1. 高性能:TCC在Try阶段并不等待资源的释放,只是预留资源,因此在分布式事务场景下可以获得较好的性能。
2.可扩展性:TCC通过将事务拆分为多个小事务,可以将并发控制的粒度细化,提高系统的并发性能。
3.高可靠性:TCC通过确认和撤销操作来保证事务的最终一致性,即使在一些小事务失败的情况下,也可以通过取消操作恢复到事务开始之前的状态。
然而,TCC也存在一些缺点:1.代码复杂性:TCC需要开发者在每个小事务中手动编写确认和取消操作的逻辑,增加了代码的复杂性和开发难度。
2.并发控制问题:由于TCC采用乐观锁的方式来进行并发控制,所以存在并发冲突的问题。
开发者需要解决并发冲突的业务逻辑和异常处理。
SpringCloudAlibaba使用Seata处理分布式事务的技巧

SpringCloudAlibaba使⽤Seata处理分布式事务的技巧Seata简介在传统的单体项⽬中,我们使⽤@Transactional注解就能实现基本的ACID事务了。
但是前提是:1)数据库⽀持事务(如:MySQL的innoDB引擎)2)所有业务都在同⼀个数据库中执⾏随着微服务架构的引⼊,需要对数据库进⾏分库分表,每个服务拥有⾃⼰的数据库,这样传统的事务就不起作⽤了,那么我们如何保证多个服务中数据的⼀致性呢?这样就出现了分布式事务,⽽Seata就是为微服务架构⽽⽣的⼀种⾼性能、易于使⽤的分布式事务解决⽅案。
Seata 中有三个基础组件:1. Transaction Coordinator(TC协调者):维护全局和分⽀事务的状态,驱动全局提交或回滚。
2. Transaction Manager(TM事务管理):定义全局事务的范围,开启、提交或回滚⼀个全局事务。
3. Resource Manager(RM资源管理):管理分⽀事务资源,与 TC 通讯并报告分⽀事务状态,管理本地事务的提交与回滚。
可以这么说⼀个分布式事务就是全局事务GlobalTransaction,⽽全局事务是由⼀个个的分⽀事务组成的,每个分⽀事务就是⼀个本地事务。
Seata的⽣命周期1. TM 要求 TC ⽣成⼀个全局事务,并由 TC ⽣成⼀个全局事务XID 返回。
2. XID 通过微服务调⽤链传播。
3. RM 向 TC 注册本地事务,将其注册到 ID 为 XID 的全局事务中。
4. TM 要求 TC 提交或回滚XID 对应的全局事务。
5. TC 驱动 XID 对应的全局事务对应的所有的分⽀事务提交或回滚。
Seata安装和配置startup -m standalone安装和配置Seatafile.conf主要是数据库的配置,配置如下registry.conf 是注册中⼼的配置另外conf⽬录中还需要⼀个脚本⽂件:nacos-config.sh ⽤于对nacos进⾏初始化配置在seata1.4.0中是没有的,需要⾃⾏创建,内容如下:#!/usr/bin/env bash# Copyright 1999-2019 Seata.io Group.## Licensed under the Apache License, Version 2.0 (the "License");# you may not use this file except in compliance with the License.# You may obtain a copy of the License at、## /licenses/LICENSE-2.0## Unless required by applicable law or agreed to in writing, software# distributed under the License is distributed on an "AS IS" BASIS,# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.# See the License for the specific language governing permissions and# limitations under the License.while getopts ":h:p:g:t:u:w:" optdocase $opt inh)host=$OPTARG;;p)port=$OPTARG;;g)group=$OPTARG;;t)tenant=$OPTARG;;u)username=$OPTARG;;w)password=$OPTARG;;)echo " USAGE OPTION: $0 [-h host] [-p port] [-g group] [-t tenant] [-u username] [-w password] " exit 1;;esacdoneurlencode() {for ((i=0; i < ${#1}; i++))dochar="${1:$i:1}"case $char in[a-zA-Z0-9.~_-]) printf $char ;;*) printf '%%%02X' "'$char" ;;esacdone}if [[ -z ${host} ]]; thenhost=localhostfiif [[ -z ${port} ]]; thenport=8848fiif [[ -z ${group} ]]; thengroup="SEATA_GROUP"fiif [[ -z ${tenant} ]]; thentenant=""fiif [[ -z ${username} ]]; thenusername=""fiif [[ -z ${password} ]]; thenpassword=""finacosAddr=$host:$portcontentType="content-type:application/json;charset=UTF-8"echo "set nacosAddr=$nacosAddr"echo "set group=$group"failCount=0tempLog=$(mktemp -u)function addConfig() {curl -X POST -H "${contentType}" "http://$nacosAddr/nacos/v1/cs/configs?dataId=$(urlencode $1)&group=$group&content=$(urlencode $2)&tenant=$tenant&username=$username&password=$password" >"${tempLog}" 2>/dev/null if [[ -z $(cat "${tempLog}") ]]; thenecho " Please check the cluster status. "exit 1fiif [[ $(cat "${tempLog}") =~ "true" ]]; thenecho "Set $1=$2 successfully "elseecho "Set $1=$2 failure "(( failCount++ ))fi}count=0for line in $(cat $(dirname "$PWD")/config.txt | sed s/[[:space:]]//g); do(( count++ ))key=${line%%=*}value=${line#*=}addConfig "${key}" "${value}"doneecho "========================================================================="echo " Complete initialization parameters, total-count:$count , failure-count:$failCount "echo "========================================================================="if [[ ${failCount} -eq 0 ]]; thenecho " Init nacos config finished, please start seata-server. "elseecho " init nacos config fail. "fi在seata的根⽬录,与conf同级的⽬录下,还需要config.txt 配置⽂件,默认也是没有的只需要对mysql的配置进⾏修改完整⽂件:transport.type=TCPtransport.server=NIOtransport.heartbeat=truetransport.enableClientBatchSendRequest=truetransport.threadFactory.bossThreadPrefix=NettyBosstransport.threadFactory.workerThreadPrefix=NettyServerNIOWorkertransport.threadFactory.serverExecutorThreadPrefix=NettyServerBizHandlertransport.threadFactory.shareBossWorker=falsetransport.threadFactory.clientSelectorThreadPrefix=NettyClientSelectortransport.threadFactory.clientSelectorThreadSize=1transport.threadFactory.clientWorkerThreadPrefix=NettyClientWorkerThreadtransport.threadFactory.bossThreadSize=1transport.threadFactory.workerThreadSize=defaulttransport.shutdown.wait=3service.vgroupMapping.my_test_tx_group=defaultservice.default.grouplist=127.0.0.1:8091service.enableDegrade=falseservice.disableGlobalTransaction=falseclient.rm.asyncCommitBufferLimit=10000client.rm.lock.retryInterval=10client.rm.lock.retryTimes=30client.rm.lock.retryPolicyBranchRollbackOnConflict=trueclient.rm.reportRetryCount=5client.rm.tableMetaCheckEnable=falseclient.rm.tableMetaCheckerInterval=60000client.rm.sqlParserType=druidclient.rm.reportSuccessEnable=falseclient.rm.sagaBranchRegisterEnable=falseclient.rm.tccActionInterceptorOrder=-2147482648mitRetryCount=5client.tm.rollbackRetryCount=5client.tm.defaultGlobalTransactionTimeout=60000client.tm.degradeCheck=falseclient.tm.degradeCheckAllowTimes=10client.tm.degradeCheckPeriod=2000client.tm.interceptorOrder=-2147482648store.mode=filestore.lock.mode=filestore.session.mode=filestore.publicKey=xxstore.file.dir=file_store/datastore.file.maxBranchSessionSize=16384store.file.maxGlobalSessionSize=512store.file.fileWriteBufferCacheSize=16384store.file.flushDiskMode=asyncstore.file.sessionReloadReadSize=100store.db.datasource=druidstore.db.dbType=mysqlstore.db.driverClassName=com.mysql.jdbc.Driverstore.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&rewriteBatchedStatements=true er=rootstore.db.password=123456store.db.minConn=5store.db.maxConn=30store.db.globalTable=global_tablestore.db.branchTable=branch_tablestore.db.queryLimit=100store.db.lockTable=lock_tablestore.db.maxWait=5000store.redis.mode=singlestore.redis.single.host=127.0.0.1store.redis.single.port=6379store.redis.sentinel.masterName=xxstore.redis.sentinel.sentinelHosts=xxstore.redis.maxConn=10store.redis.minConn=1store.redis.maxTotal=100store.redis.database=0store.redis.password=xxstore.redis.queryLimit=100mittingRetryPeriod=1000server.recovery.asynCommittingRetryPeriod=1000server.recovery.rollbackingRetryPeriod=1000server.recovery.timeoutRetryPeriod=1000server.maxCommitRetryTimeout=-1server.maxRollbackRetryTimeout=-1server.rollbackRetryTimeoutUnlockEnable=falseserver.distributedLockExpireTime=10000client.undo.dataValidation=trueclient.undo.logSerialization=jacksonclient.undo.onlyCareUpdateColumns=trueserver.undo.logSaveDays=7server.undo.logDeletePeriod=86400000client.undo.logTable=undo_logpress.enable=truepress.type=zippress.threshold=64klog.exceptionRate=100transport.serialization=seatapressor=nonemetrics.enabled=falsemetrics.registryType=compactmetrics.exporterList=prometheusmetrics.exporterPrometheusPort=9898在conf⽬录中,使⽤Git Bash进⼊命令⾏,输⼊sh nacos-config.sh 127.0.0.1这是对Seata进⾏初始化配置,上图表⽰所有配置都成功设置了在nacos中可以看到出现了seata相关的配置接下来在seata数据库中,新建三个表drop table if exists `global_table`;create table `global_table` (`xid` varchar(128) not null,`transaction_id` bigint,`status` tinyint not null,`application_id` varchar(32),`transaction_service_group` varchar(32),`transaction_name` varchar(128),`timeout` int,`begin_time` bigint,`application_data` varchar(2000),`gmt_create` datetime,`gmt_modified` datetime,primary key (`xid`),key `idx_gmt_modified_status` (`gmt_modified`, `status`),key `idx_transaction_id` (`transaction_id`));drop table if exists `branch_table`;create table `branch_table` (`branch_id` bigint not null,`xid` varchar(128) not null,`transaction_id` bigint ,`resource_group_id` varchar(32),`resource_id` varchar(256) ,`lock_key` varchar(128) ,`branch_type` varchar(8) ,`status` tinyint,`client_id` varchar(64),`application_data` varchar(2000),`gmt_create` datetime,`gmt_modified` datetime,primary key (`branch_id`),key `idx_xid` (`xid`));drop table if exists `lock_table`;create table `lock_table` (`row_key` varchar(128) not null,`xid` varchar(96),`transaction_id` long ,`branch_id` long,`resource_id` varchar(256) ,`table_name` varchar(32) ,`pk` varchar(36) ,`gmt_create` datetime ,`gmt_modified` datetime,primary key(`row_key`));在项⽬相关的数据库中,新建表undo_log ⽤于记录撤销⽇志CREATE TABLE `undo_log` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`branch_id` bigint(20) NOT NULL,`xid` varchar(100) NOT NULL,`context` varchar(128) NOT NULL,`rollback_info` longblob NOT NULL,`log_status` int(11) NOT NULL,`log_created` datetime NOT NULL,`log_modified` datetime NOT NULL,`ext` varchar(100) DEFAULT NULL,PRIMARY KEY (`id`),UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;最后在bin⽬录中,启动命令⾏,执⾏seata-server.bat 启动Seata服务项⽬应⽤SeataSpringCloud项⽬中有两个服务:订单服务和库存服务,基本业务是:购买商品插⼊订单减少库存订单详情表DROP TABLE IF EXISTS `tb_order_detail`;CREATE TABLE `tb_order_detail` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '订单详情id ',`order_id` bigint(20) NOT NULL COMMENT '订单id',`sku_id` bigint(20) NOT NULL COMMENT 'sku商品id',`num` int(11) NOT NULL COMMENT '购买数量',`title` varchar(256) NOT NULL COMMENT '商品标题',`own_spec` varchar(1024) DEFAULT '' COMMENT '商品动态属性键值集',`price` bigint(20) NOT NULL COMMENT '价格,单位:分',`image` varchar(128) DEFAULT '' COMMENT '商品图⽚',PRIMARY KEY (`id`),KEY `key_order_id` (`order_id`) USING BTREE) ENGINE=MyISAM AUTO_INCREMENT=131 DEFAULT CHARSET=utf8 COMMENT='订单详情表';库存表DROP TABLE IF EXISTS `tb_stock`;CREATE TABLE `tb_stock` (`sku_id` bigint(20) NOT NULL COMMENT '库存对应的商品sku id',`seckill_stock` int(9) DEFAULT '0' COMMENT '可秒杀库存',`seckill_total` int(9) DEFAULT '0' COMMENT '秒杀总数量',`stock` int(9) NOT NULL COMMENT '库存数量',PRIMARY KEY (`sku_id`)) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='库存表,代表库存,秒杀库存等信息';⽗项⽬定义了springboot、springcloud、springcloud-alibaba的版本<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.10.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>0.9.0.RELEASE</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>2.2.1.RELEASE</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>Hoxton.SR8</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement>⼦项⽬的依赖定义了nacos和seata客户端<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.3.2</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-seata</artifactId><exclusions><exclusion><groupId>io.seata</groupId><artifactId>seata-spring-boot-starter</artifactId></exclusion></exclusions></dependency><dependency><groupId>io.seata</groupId><artifactId>seata-spring-boot-starter</artifactId><version>1.2.0</version></dependency>⼦项⽬配置⽂件完整配置server:port: 8001spring:application:name: stock-servicecloud:nacos:discovery:server-addr: localhost:8848alibaba:seata:enabled: trueenable-auto-data-source-proxy: truetx-service-group: my_test_tx_groupregistry:type: nacosnacos:application: seata-serverserver-addr: 127.0.0.1:8848username: nacospassword: nacosconfig:type: nacosnacos:server-addr: 127.0.0.1:8848group: SEATA_GROUPusername: nacospassword: nacosservice:vgroup-mapping:my_test_tx_group: defaultdisable-global-transaction: falseclient:rm:report-success-enable: falsedatasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/eshop?serverTimezone=UTC&useUnicode=true&useSSL=false&characterEncoding=utf8 username: rootpassword: 123456库存服务定义了减库存的⽅法@RestControllerpublic class StockController {@Autowiredprivate IStockService stockService;@PutMapping("/stock")public ResponseEntity<Stock> reduceSkuStock(@RequestParam("skuId")Long skuId,@RequestParam("number")Integer number){Stock stock = stockService.getById(skuId);if(stock.getStock() < number){throw new RuntimeException("库存不⾜,SkuId:" + skuId);}stock.setStock(stock.getStock() - number);stockService.updateById(stock);return ResponseEntity.ok(stock);}}订单服务在插⼊订单后,使⽤Feign调⽤了减库存的服务@Servicepublic class OrderDetailServiceImpl extends ServiceImpl<OrderDetailMapper, OrderDetail> implements IOrderDetailService {//库存服务Feign@Autowiredprivate StockFeignClient stockFeignClient;// @Transactional@GlobalTransactional(rollbackFor = {Exception.class})@Overridepublic void makeOrder(OrderDetail orderDetail) {this.save(orderDetail); //保存订单int x = 11 / 0; //抛出异常//减库存stockFeignClient.reduceSkuStock(orderDetail.getSkuId(),orderDetail.getNum());}}插订单和减库存属于两个服务,传统的@Transactional已经不能保证它们的原⼦性了这⾥使⽤了Seata提供的@GlobalTransactional全局事务注解,出现任何异常后都能实现业务回滚。
在SQL SERVER中使用分布式事务全攻略(图解)

在SQL SERVER中使用分布式事务全攻略(图解)[原创文章] 作者:cyw操作系统:Win2003 Enterprise Edition。
版本:5.2.3790 Service Pack 2 内部版本号 3790。
数据库:SQL Server 2000 企业版 + SP4 + SP4后的补丁。
版本:8.00.2187 (Intel X86) Mar 9 2006。
网络环境:两台服务器仅安装TCP/IP协议,处于相同网段,工作组可以不同,相互间Ping主机名成功,Ping IP地址也能成功。
如果不能Ping成功,请在两台服务器上安装NETBIOS协议后再重试。
如果还不行,请在“C:\WINDOWS\system32\drivers\etc\hosts”文件中增加一条记录:xxx.xxx.xxx.xxx 服务器主机名作用同样是把服务器名对应到链接服务器的IP地址。
一、建立链接服务器假设服务器A的IP是172.16.10.6,SQLServer登录用户sa,密码8888;服务器B的IP是172.16.10.16,SQLServer登录用户sa,密码9999。
现在先在服务器A上建立与B通信的远程链接服务器,假设链接的别名是BServer,如图:点击“确定”,完成。
同理,在B服务器上也建立对A服务器的远程链接,链接别名为AServer,数据源地址改为172.16.10.6,密码输入8888,其他相同。
当然,也可以使用SQL语句完成以上操作,如下:-- 建立远程链接服务器BServerEXEC sp_addlinkedserve r @server = 'BServer', @srvproduct = '', @provider = 'SQLOLEDB',@datasrc = '172.16.10.16', @catalog = 'HYCommon'-- 建立远程登录到BServerEXEC sp_addlinkedsrvlogin@rmtsrvname = 'BServer', @useself = 'false', @rmtuser = 'sa',@rmtpassword = '9999'-- 设置远程服务器选项:允许RPC和RPC输出(该步可选)Exec sp_serveroption'BServer', 'RPC', 'true'Exec sp_serveroption'BServer', 'RPC Out', 'true'现在测试一下链接是否成功,打开查询分析器,登录到A服务器,输入以下SQL运行:Select * From BServer.pubs.dbo.titles正常的话就可以看到B服务器上pubs数据库的titles表数据,如果不行,请检查网络连接是否满足文章开头所述的网络环境。
TCC分布式事务解决方案

TCC分布式事务解决⽅案⼀、什么是 TCC事务TCC 是Try、Confirm、Cancel三个词语的缩写,TCC要求每个分⽀事务实现三个操作:预处理Try、确认Confirm、撤销Cancel。
Try操作做业务检查及资源预留,Confirm做业务确认操作,Cancel实现⼀个与 Try或者 Commit相反的操作即回滚操作。
TM⾸先发起所有的分⽀事务的 try操作,任何⼀个分⽀事务的 try操作执⾏失败,TM将会发起所有分⽀事务的 Cancel操作,若 Try操作全部成功,TM将会发起所有分⽀事务的 Confirm操作,其中 Confirm/Cancel 操作若执⾏失败,TM会进⾏重试。
⼆、TCC 解决⽅案⽬前市⾯上的 TCC框架众多⽐如下⾯这⼏种:框架名称Gitbub地址star数量tcc-transaction4351Hmily2788ByteTCC2156EasyTransaction1907Seata也⽀持TCC,但 Seata的 TCC模式对 Spring Cloud并没有提供⽀持。
因此更请倾向于轻量级易于理解的框架Hmily,来理解 TCC的原理以及事务协调运作的过程。
Hmily是⼀个⾼性能分布式事务 TCC开源框架。
基于Java语⾔来开发,⽀持Dubbo,Spring Cloud等。
RPC框架进⾏分布式事务。
它⽬前⽀持以下特性:■⽀持嵌套事务(Nested transaction support);■采⽤ disruptor框架进⾏事务⽇志的异步读写,与 RPC框架的性能毫⽆差别;■⽀持 SpringBoot-starter 项⽬启动,使⽤简单;■ RPC框架⽀持 : dubbo,motan,springcloud;■本地事务存储⽀持 : redis,mongodb,zookeeper,fifile,mysql;■事务⽇志序列化⽀持:java,hessian,kryo,protostuff;■采⽤ Aspect AOP 切⾯思想与 Spring⽆缝集成,天然⽀持集群;■ RPC事务恢复,超时异常恢复等;Hmily利⽤ AOP对参与分布式事务的本地⽅法与远程⽅法进⾏拦截处理,通过多⽅拦截,事务参与者能透明的调⽤到另⼀⽅的Try、Confirm、Cancel⽅法;传递事务上下⽂;并记录事务⽇志,酌情进⾏补偿,重试等。
解析分布式事务的四种解决方案

解析分布式事务的四种解决方案分布式事务指事务的操作位于不同的节点上,需要保证事务的 AICD 特性。
例如在下单场景下,库存和订单如果不在同一个节点上,就涉及分布式事务。
在分布式系统中,要实现分布式事务,无外乎那几种解决方案。
一、两阶段提交(2PC)两阶段提交(Two-phase Commit,2PC),通过引入协调者(Coordinator)来协调参与者的行为,最终决定这些参与者是否要真正执行事务。
1、运行过程①准备阶段:协调者询问参与者事务是否执行成功,参与者发回事务执行结果。
②提交阶段:如果事务在每个参与者上都执行成功,事务协调者发送通知让参与者提交事务;否则,协调者发送通知让参与者回滚事务。
需要注意的是,在准备阶段,参与者执行了事务,但是还未提交。
只有在提交阶段接收到协调者发来的通知后,才进行提交或者回滚。
2、存在的问题①同步阻塞:所有事务参与者在等待其它参与者响应的时候都处于同步阻塞状态,无法进行其它操作。
②单点问题:协调者在 2PC 中起到非常大的作用,发生故障将会造成很大影响。
特别是在阶段二发生故障,所有参与者会一直等待状态,无法完成其它操作。
③数据不一致:在阶段二,如果协调者只发送了部分 Commit 消息,此时网络发生异常,那么只有部分参与者接收到 Commit 消息,也就是说只有部分参与者提交了事务,使得系统数据不一致。
④太过保守:任意一个节点失败就会导致整个事务失败,没有完善的容错机制。
二、补偿事务(TCC)TCC 其实就是采用的补偿机制,其核心思想是:针对每个操作,都要注册一个与其对应的确认和补偿(撤销)操作。
它分为三个阶段:①Try 阶段主要是对业务系统做检测及资源预留。
②Confirm 阶段主要是对业务系统做确认提交,Try阶段执行成功并开始执行Confirm阶段时,默认 Confirm阶段是不会出错的。
即:只要Try成功,Confirm一定成功。
③Cancel 阶段主要是在业务执行错误,需要回滚的状态下执行的业务取消,预留资源释放。
C#分布式事务解决方案-TransactionScope

C#分布式事务解决⽅案-TransactionScope引⽤⼀下别⼈的导读:在实际开发⼯作中,执⾏⼀个事件,然后调⽤另⼀接⼝插⼊数据,如果处理逻辑出现异常,那么之前插⼊的数据将成为垃圾数据,我们所希望的是能够在整个这个⽅法定义为⼀个事务,TransactionScope 类提供⼀个简单⽅法,通过这⼀⽅法,您不必与事务本⾝交互,即可将代码块标记为参与某个事务。
TransactionScope对象创建了⼀个事务,同时将该事务设置给Transaction类的Current属性。
⼀、TransactionScope的优点1、使⽤起来⽐较⽅便.TransactionScope可以实现隐式的事务,使你可以在写数据访问层代码的时候不⽤考虑到事务,⽽在业务层的控制事务.2、可以实现分布式事务,⽐如跨库或MSMQ.⼆、TransactionScope缺点1、性价⽐不⾼.⽐如,你只是在"Scope"⾥控制⼀个库的事务.⽤"TransactionScope"就有点浪费了.2、⼀般情况下只要你使⽤"TransactionScope",都要配置MSDTC,要配防⽕墙,要开139端⼝.这个端⼝不可以更改三、如果你不得不⽤分布式事务,那也得琢磨琢磨1.这步操作⼀定得在事务当中吗?这步操作如果没完成或者失败了,值得回滚整个事务吗?难道没有优雅的补偿措施或者容错措施?2.分布式事务涉及到的点,必须的这么多?必须得实时的操作这⼀⼤串?不能通过通知类操作去精简掉某些点?3.在发起分布式事务之后,你是不是做了事务⽆关的操作,尽管这些操作跟事务⽆关?(如,读取数据、计算、等⽤户返回消息、等其他模块的调⽤返回等等)要知道事务应该尽快结束。
4.你没有把⼀些读操作也算在事务⾥⾯了吧?这是很容易犯的错误,你在事务中Enlist了⼀个select 操作。
5.你的操作,某些步骤可以等全部操作完成之后再执⾏.这类操作具有明显的通知类特点。
详解Java分布式事务的6种解决方案

详解Java分布式事务的6种解决⽅案介绍在分布式系统、微服务架构⼤⾏其道的今天,服务间互相调⽤出现失败已经成为常态。
如何处理异常,如何保证数据⼀致性,成为微服务设计过程中,绕不开的⼀个难题。
在不同的业务场景下,解决⽅案会有所差异,常见的⽅式有:1. 阻塞式重试;2. 2PC、3PC 传统事务;3. 使⽤队列,后台异步处理;4. TCC 补偿事务;5. 本地消息表(异步确保);6. MQ 事务。
本⽂侧重于其他⼏项,关于 2PC、3PC 传统事务,⽹上资料已经⾮常多了,这⾥不多做重复。
阻塞式重试在微服务架构中,阻塞式重试是⽐较常见的⼀种⽅式。
伪代码⽰例:m := db.Insert(sql)err := request(B-Service,m)func request(url string,body interface{}){for i:=0; i<3; i ++ {result, err = request.POST(url,body)if err == nil {break}else {log.Print()}}}如上,当请求 B 服务的 API 失败后,发起最多三次重试。
如果三次还是失败,就打印⽇志,继续执⾏下或向上层抛出错误。
这种⽅式会带来以下问题1. 调⽤ B 服务成功,但由于⽹络超时原因,当前服务认为其失败了,继续重试,这样 B 服务会产⽣ 2 条⼀样的数据。
2. 调⽤ B 服务失败,由于 B 服务不可⽤,重试 3 次依然失败,当前服务在前⾯代码中插⼊到 DB 的⼀条记录,就变成了脏数据。
3. 重试会增加上游对本次调⽤的延迟,如果下游负载较⼤,重试会放⼤下游服务的压⼒。
第⼀个问题:通过让 B 服务的 API ⽀持幂等性来解决。
第⼆个问题:可以通过后台定时脚步去修正数据,但这并不是⼀个很好的办法。
第三个问题:这是通过阻塞式重试提⾼⼀致性、可⽤性,必不可少的牺牲。
阻塞式重试适⽤于业务对⼀致性要求不敏感的场景下。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
分布式定时任务
mutex_task字段说明
字段 id 说明
task_name lock_host
lock_status gmt_last_lock
任务名称 被哪个机器锁了
锁状态 上次锁的时间,如果 跟当前时间差很多, 有权忽略 lock_status 当前时间减去 start_span就是查询 的开始时间 当前时间减去 end_span就是查询 的结束时间 锁的超时时间 Y
分布式事务方案
大纲
基本原理 服务端设计 应用改造方案 典型业务场景 接入方式
数据一致性
数据一致性。
关联数据之间的逻辑关系是否正确和完整。
一致性分类。
强一致 弱一致性
最终一致性
分布式事务
分布式事务就是指事务的参与者、支持事务的服务器、 资源服务器以及事务管理器分别位于不同的分布式系 统的不同节点之上。 产生原因
发起者
1.开启本地事务 2.注册一个远程事务(主事务)
参与者
XTS
4.调用参与者 5.加入分支事务
3.Business_activity增加一条的I的记录
7.完成参与者业务 8.本地事务提交 8.XTS根据本地事务结果commit/rollback
6.Business_action增加一条记录
10.循环第2阶段commit/rollback
6.根据配置结果通知
5.更新Business_activity状态
大纲
基本原理 服务端设计 应用改造方案 典型业务场景 接入方式
事务改造-发起者
发起者在本地事务内开启。
只要负责开启事务,客户端会自动在本地事务管理器 中注册事务同步器。 本地事务提交时,由事务同步器自动通知服务端进行 事务回滚或者提交。
1.银行支付完成 2.开启本地事务 3.注册一个远程事务(主事务)
account
XTS
如果发生回 滚,支付能 发起重试
4.增加余额并冻结 5.加入分支事务
6.完成充值,并冻结预处理
7.本地事务提交 8.XTS根据本地事务结果commit/rollback 第2阶段commit/rollback
典型业务场景-提现
business_action(分支事务)
字段 id activity_id system_id business_type 说明 逻辑主键 主事务id 系统id,二阶段提交 时区分目标系统 参与者使用,区分业 务类型,xts本身不 关心 参与者使用,xts不 关心 创建时间 修改时间
Context gmt_create gmt_modified
回滚状态:发起方 本地事务回滚了, 事务同步器回滚
定时恢复
daemon
捞取主事务的时候 需要注意时间段, 不能跨度太长导致 db压力。 查询事务状态的时 候只有在提交或者 回滚的时候才需要 恢复。
捞取主事务状 态为I的记录
loop
查询发起者事 务状态
根据主事务id 获取分支事务 提交/回滚分 支事务
主事务管理器 定 时 任 务 分支事务管理器
发起者
注册分 同库:主事务和业务流水在一个db中,占用业务 系统的db容量,但是减少了主事务注册的远程调 用,性能有优势,对应用来说改动相对比较大。 异库:主事务在xts系统中,所有的分布式事务都 需要xts来主控,可能存在单点问题,比如db挂掉, 应用挂掉。
propagation context gmt_create gmt_modifie d
主事务状态机
NULL
初始化状态:发起 方注册主事务。 未知状态:是由协 调者会查时发起者 给的结果,理论上 不应该出现,出现 了可能需要人工介 入了。
初始化
确认提交状态
确认回滚状态
未知状态
提交状态:发起方 本地事务提交了, 事务同步器提交到 服务端
抢锁
N
捞取初始数 据进行恢复
Start_span
结束
End_span
expire
容量分析
基本原则
• 前提:调用异常占比极少
• 只记结果数据,不记过程数据
最终结果
• 主事务:1次insert,1次update。
• 分支事务:1次insert。 • 查询数量:只在调用异常时使用,量非常少
分布式事务实现-正常调用
三、新增一个表,存放分布式事务相关的信息。
一阶段的时候把数据单独存起来,业务处理时要考虑到这个新增 的表中数据。 二阶段提交时直接把数据移到主流水中,回滚则直接删除数据。 注意和主流水在一个事务里。
大纲
基本原理 服务端设计 应用改造方案 典型业务场景 接入方式
典型业务场景-充值
payment
transcore
异步提现 开启本地事务
payment
account
XTS
事务失败, payment能 自动恢复
开启分布式事务 转账 加入分布式事务
转到中间账户 XTS根据本地事务结果commit/rollback
银行提现
大纲
基本原理 服务端设计 应用改造方案 典型业务场景 接入方式
发起者配置
参与者接入
数据库分库分表 应用SOA化
常见方案
基于XA协议的两阶段提交
消息事务+最终一致性
TCC编程模式
两阶段提交基本模型
事务协调者
1.准备 2.已准备 5.提交 6.完成
3.准备 4.已准备 7.提交 8.完成
参与者
参与者
事务协调者
1.准备 2.已准备 5.回滚 6.完成
3.准备 4.已准备 7.回滚 8.完成
参与者
参与者
分布式事务基本模型
XTS
1.开启主事务 2.已准备 5.提交
4.已准备 3.加入分支事务 6.提交 7.完成
发起者
参与者
XTS
1.开启主事务 2.已准备 5.回滚
4.已准备 3.加入分支事务 6.回滚 7.完成
发起者
参与者
分布式事务基本模型
协调者
注册主事务 事务提交 事务回查 恢复数据查询
参与者状态机改造的几种方案
一、在业务状态中带入分布式事务状态。
适用情况,状态机比较简单,加入后不会对业务流程造成很大影 响。
二、业务流水表上新增一个分布式事务状态字段。
比如支付在充值完成后需要完成支付时,业务状态停在“已充 值”,如果支付第1阶段成功,状态变成“已充值”-“预处理成 功”,第2阶段提交成功后,业务状态和分布式事务状态一起变化。 在一条记录上,变更时天生带有事务性。
Thanks
Q&A
9.捞取Business_action记录 11.更新Business_activity状态
12.根据配置结果通知
分布式事务实现-恢复流程
发起者 参与者 XTS
2.查询事务状态
1.Business_activity捞取I,U的记录
4. commit/rollback
3.根据事务id查询Business_action
发起者在本地事务外开启。
应用可以选择在合适的时候绑定本地事务。 应用需要确保事务一定会提交或者回滚。
需要实现回查接口,数据要确保可重复读。
事务改造-参与者
在业务过程中需要显式的调用客户端把自己加入 到分支事务中。 需要实现xts提供的二阶段提交/回滚服务。 事务状态需要保证幂等性(比如xts发起提交的时 候,此时数据已经提交,也需要返回提交成功)
几个注意点
1.事务id传递有两种方式,一是在服务接口里直接传递,二是在
dubbo上下文里获取。 2.由于同一笔业务可能发起多次调用,发起者应做好记录,防止在查
询的时候查询不到流水。
3.context中可以存放业务流水,如果根据事务id无法查询到数据,可 以通过业务流水查询状态。 4.参与者要做好状态控制,支持多次提交和回滚。 5.参与者要做好请求流水的幂等控制,xts只能保证单个事务的最终一 致性,业务上的控制需要应用自行控制。
优缺点
一定程度降低了系统处理分布式事务一致性带来的复杂度, 提供了最终一致性的保障。
整个设计是存储无关的,底层数据库可以采用任何一种数 据库。 对性能的影响,因为是在本地事务重开启了分布式事务, 单个事务的操作时间会很大程度受rpc响应时间影响,如 果rpc时间过长,会影响单机的吞吐量。
未来对于主路径肯定是主推同库模式,异库模式 也需要支持。
根据业务实际情况,采用异库的模式。
xts:extended transaction service
大纲
基本原理 服务端设计 应用改造方案 典型业务场景 接入方式
分布式事务主要模型
business_activity(主事务)
字段 id activity_id system_id business_typ e state 说明 逻辑主键 随机生成的一段永不重 复的id 系统id,在回查的时候 用来区分目标系统 业务类型,给发起者使 用,xts本身不关心 状态,I(初始化),C (已提交),R(已回 滚),U(未知) 传播范围,暂不使用 发起者使用,xts不关心 创建时间 修改时间