做科研有什么好的网站,流程图,网站内链建设,网站建设seo虾哥网络一、分布式事务
1. 事务介绍
1.1 基础概念 事务#xff1a;保证我们多个数据库操作的原子性#xff0c;多个操作要么都成功要么都不成功 事务ACID原则 A#xff08;Atomic#xff09;原子性#xff1a;构成事务的所有操作#xff0c;要么都执行完成#xff0c;要么全部…一、分布式事务
1. 事务介绍
1.1 基础概念 事务保证我们多个数据库操作的原子性多个操作要么都成功要么都不成功 事务ACID原则 AAtomic原子性构成事务的所有操作要么都执行完成要么全部不执行不可能出现部分成功部分失 败的情况。 CConsistency一致性在事务执行前后数据库的一致性约束没有被破坏。 比如张三向李四转100元 转账前和转账后的数据是正确状态这叫一致性如果出现张三转出100元李四账户没有增加100元这就出现了数 据错误就没有达到一致性。 IIsolation隔离性数据库中的事务一般都是并发的隔离性是指并发的两个事务的执行互不干扰一个事 务不能看到其他事务运行过程的中间状态。通过配置事务隔离级别可以避脏读、重复读等问题。 DDurability持久性事务完成之后该事务对数据的更改会被持久化到数据库且不会被回滚。 1.2 事务分类 本地事务同一数据库和服务器称为本地事务在计算机系统中更多的是通过关系型数据库来控制事务这是利用数据库本身的事务特性来实现的因此叫数据库事务由于应用主要靠关系数据库来控制事务而数据库通常和应用在同一个服务器所以基于关系型数据库的事务又被称为本地事务。 分布式事务 分布式事务指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上且属于不同的应用分布式事务需要保证这些操作要么全部成功要么全部失败。本质上来说分布式事务就是为了保证不同数据库的数据一致性。 举例 分布式系统会把一个应用系统拆分为可独立部署的多个服务因此需要服务与服务之间远程协作才能完成事务操 作这种分布式系统环境下由不同的服务之间通过网络远程协作完成事务称之为分布式事务例如用户注册送积分事务、创建订单减库存事务银行转账事务等都是分布式事务。 通过以上的图中我们可以看出其实只要涉及到操作多个数据源就可能会产生事务问题当然在实际开发中我们要尽量避免这种问题的出现当然如果避免不了我们就需要进行解决在我们的微服务系统架构中目前比较好比较常用的解决方案就是Seata。
2.分布式事务概念 随着互联化的蔓延各种项目都逐渐向分布式服务做转换。如今微服务已经普遍存在本地事务已经无法满足分布式的要求由此分布式事务问题诞生。 分布式事务被称为世界性的难题目前分布式事务存在两大理论依据CAP定律 BASE理论。
CAP定律
这个定理的内容是指的是在一个分布式系统中、Consistency一致性、 Availability可用性、Partition tolerance分区容错性三者不可得兼。 一致性C 在分布式系统中的所有数据备份在同一时刻是否同样的值。等同于所有节点访问同一份最新的数据副本 可用性A 在集群中一部分节点故障后集群整体是否还能响应客户端的读写请求。对数据更新具备高可用性 分区容错性P 以实际效果而言分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性就意味着发生了分区的情况必须就当前操作在C和A之间做出选择
CAP是无法同时存在的一下通过这个例子来说明 当库存服务减库存以后那么需要将数据同步到其他的服务上这是为了保证数据一致性C但是网络是不可靠的所以我们系统就需要保证分区容错性P也就是我们必须容忍网络所带来的的一些问题此时如果我们想保证C那么就需要舍弃A也就是说我们在保证C的情况下就必须舍弃A也就是CP无法保证高可用。如果为了保证A高可用的情况下也就是必须在限定时间内给出响应同样由于网络不可靠P订单服务就有可能无法拿到新的数据但是也要给用户作出响应那么也就无法保证C一致性。所以AP是无法保证强一致性的。如果我们想保证CA也就是高可用和一致性也就是必须保证网络良好才能实现那么也就是说我们需要将库存、订单、用户放到一起但是这种情况也就丧失了P这个保证这个时候系统也就不是分布式系统了。总结在分布式系统中p是必然的存在的所以我们只能在C和A之间进行取舍在这种条件下就诞生了BASE理论
BASE理论
BASE是Basically Available基本可用、Soft state软状态和 Eventually consistent最终一致性三个短语的缩写。BASE理论是对CAP中一致性和可用性权衡的结果其来源于对大规模互联网系统分布式实践的总结 是基于CAP定理逐步演化而来的。BASE理论的核心思想是即使无法做到强一致性但每个应用都可以根据自身业务特点采用适当的方式来使系统达到最终一致性。 基本可用 基本可用是指分布式系统在出现不可预知故障的时候允许损失部分可用性—-注意这绝不等价于系统不可用。比如 1响应时间上的损失。正常情况下一个在线搜索引擎需要在0.5秒之内返回给用户相应的查询结果但由于出现故障查询结果的响应时间增加了1~2秒 2系统功能上的损失正常情况下在一个电子商务网站上进行购物的时候消费者几乎能够顺利完成每一笔订单但是在一些节日大促购物高峰的时候由于消费者的购物行为激增为了保护购物系统的稳定性部分消费者可能会被引导到一个降级页面 软状态 软状态指允许系统中的数据存在中间状态并认为该中间状态的存在不会影响系统的整体可用性即允许系统在不同节点的数据副本之间进行数据同步的过程存在延时 最终一致性 最终一致性强调的是所有的数据副本在经过一段时间的同步之后最终都能够达到一个一致的状态。因此最终一致性的本质是需要系统保证最终数据能够达到一致而不需要实时保证系统数据的强一致性。
那这个位置我们依旧可以用我们刚才的例子来进行说明
基本可用保证核心服务是可以使用的至于其他的服务可以适当的降低响应时间甚至是服务降级 软状态存在中间状态不影响整体系统使用数据同步存在延时 最终一致性再过了流量高峰期以后经过一段时间的同步保持各服务数据的一致 3.分布式事务解决方案
https://www.processon.com/view/link/62a1ddce0791293ad1a552c0
3.1 两阶段提交2PC
2PC即两阶段提交协议是将整个事务流程分为两个阶段: 准备阶段Prepare phase 提交阶段commit phase
2是指两个阶段P是指准备阶段C是指提交阶段。 第一阶段
事务协调器要求每个涉及到事务的数据库预提交(precommit)此操作并反映是否可以提交.
第二阶段
事务协调器要求每个数据库提交数据。 其中如果有任何一个数据库否决此次提交那么所有数据库都会被要求回滚它们在此事务中的那部分信息。 目前主流数据库均支持2PC【2 Phase Commit】
XA 是一个两阶段提交协议又叫做 XA Transactions。 MySQL从5.5版本开始支持SQL Server 2005 开始支持Oracle 7 开始支持。 总的来说XA协议比较简单而且一旦商业数据库实现了XA协议使用分布式事务的成本也比较低。但是XA也有致命的缺点那就是性能不理想特别是在交易下单链路往往并发量很高XA无法满足高并发场景。
两阶段提交涉及多次节点间的网络通信通信时间太长事务时间相对于变长了锁定的资源的时间也变长了造成资源等待时间也增加好多。XA目前在商业数据库支持的比较理想在mysql数据库中支持的不太理想mysql的XA实现没有记录prepare阶段日志主备切换会导致主库与备库数据不一致。许多nosql也没有支持XA这让XA的应用场景变得非常狭隘。
3.2 TCC补偿式事务
TCC 是一种编程式分布式事务解决方案。
TCC 其实就是采用的补偿机制其核心思想是针对每个操作都要注册一个与其对应的确认和补偿撤销操作。TCC模式要求从服务提供三个接口Try、Confirm、Cancel。
Try主要是对业务系统做检测及资源预留Confirm真正执行业务不作任何业务检查只使用Try阶段预留的业务资源Confirm操作满足幂等性。Cancel释放Try阶段预留的业务资源Cancel操作满足幂等性。
整个TCC业务分成两个阶段完成 第一阶段
主业务服务分别调用所有从业务的try操作并在活动管理器中登记所有从业务服务。当所有从业务服务的try操作都调用成功或者某个从业务服务的try操作失败进入第二阶段。
第二阶段
活动管理器根据第一阶段的执行结果来执行confirm或cancel操作。如果第一阶段所有try操作都成功则活动管理器调用所有从业务活动的confirm操作。否则调用所有从业务服务的cancel操作。
举个例子假如 Bob 要向 Smith 转账100元思路大概是
我们有一个本地方法里面依次调用
首先在 Try 阶段要先检查Bob的钱是否充足并把这100元锁住Smith账户也冻结起来。在 Confirm 阶段执行远程调用的转账的操作转账成功进行解冻。如果第2步执行成功那么转账成功如果第二步执行失败则调用远程冻结接口对应的解冻方法 (Cancel)。
缺点 Canfirm和Cancel的幂等性很难保证。 这种方式缺点比较多通常在复杂场景下是不推荐使用的除非是非常简单的场景非常容易提供回滚Cancel而且依赖的服务也非常少的情况。 这种实现方式会造成代码量庞大耦合性高。而且非常有局限性因为有很多的业务是无法很简单的实现回滚的如果串行的服务很多回滚的成本实在太高。
不少大公司里其实都是自己研发 TCC 分布式事务框架的专门在公司内部使用。国内开源出去的ByteTCCTCC-transactionHimly。
3.3 消息事务最终一致性 基于消息中间件的两阶段提交往往用在高并发场景下将一个分布式事务拆成一个消息事务A系统的本地操作发消息B系统的本地操作其中B系统的操作由消息驱动只要消息事务成功那么A操作一定成功消息也一定发出来了这时候B会收到消息去执行本地操作如果本地操作失败消息会重投直到B操作成功这样就变相地实现了A与B的分布式事务。 虽然上面的方案能够完成A和B的操作但是A和B并不是严格一致的而是最终一致的我们在这里牺牲了一致性换来了性能的大幅度提升。当然这种玩法也是有风险的如果B一直执行不成功那么一致性会被破坏具体要不要玩还是得看业务能够承担多少风险。
适用于高并发最终一致
低并发基本一致二阶段提交
高并发强一致没有解决方案
二、Seata
1.Seata简介
官网https://seata.io/zh-cn/docs/overview/what-is-seata.html
概念Seata 是一款开源的分布式事务解决方案致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、 和 XA 事务模式为用户打造一站式的分布式解决方案。 在我们的微服务系统中对应业务被对应的拆分成独立模块在官方提供的架构图中我们可以看出当前是三个服务
仓储服务对给定的商品进行增删操作记录数量订单服务根据采购者的需求创建订单账户服务从用户账户中扣除余额、积分等
在这套架构中用户下单购买商品的业务就需要三个服务来完成每个服务内部的数据一致性由本地事务来保证但是全局的数据一致性问题就没办法保证Seata就是来进行解决这种问题的解决方案。
2.Seata概念讲解
要了解Seata首先我们要了解一下Seata的几个关键的概念
- TC (Transaction Coordinator) - 事务协调者 维护全局和分支事务的状态驱动全局事务提交或回滚。
- TM (Transaction Manager) - 事务管理器 定义全局事务的范围开始全局事务、提交或回滚全局事务。
- RM (Resource Manager) - 资源管理器 管理分支事务处理的资源与TC交谈以注册分支事务和报告分支事务的状态并驱动分支事务提交或回滚。
3. 服务安装
3.1 Seata-Server下载
官方下载地址https://github.com/seata/seata/releases 下载完成之后需要解压
3.2 Seata-Server配置
1.需要打开conf目录 2.先配置registry.conf配置文件修改Seata的注册中心和配置中心为Nacos
注册中心 配置中心 registry {# file 、nacos 、eureka、redis、zk、consul、etcd3、sofatype nacosnacos {application seata-serverserverAddr 127.0.0.1:8848group SEATA_GROUPnamespace cluster defaultusername password }eureka {serviceUrl http://localhost:8761/eurekaapplication defaultweight 1}redis {serverAddr localhost:6379db 0password cluster defaulttimeout 0}zk {cluster defaultserverAddr 127.0.0.1:2181sessionTimeout 6000connectTimeout 2000username password }consul {cluster defaultserverAddr 127.0.0.1:8500aclToken }etcd3 {cluster defaultserverAddr http://localhost:2379}sofa {serverAddr 127.0.0.1:9603application defaultregion DEFAULT_ZONEdatacenter DefaultDataCentercluster defaultgroup SEATA_GROUPaddressWaitTime 3000}file {name file.conf}
}config {# file、nacos 、apollo、zk、consul、etcd3type nacosnacos {serverAddr 127.0.0.1:8848namespace group SEATA_GROUPusername password dataId seataServer.properties}consul {serverAddr 127.0.0.1:8500aclToken }apollo {appId seata-server## apolloConfigService will cover apolloMetaapolloMeta http://192.168.1.204:8801apolloConfigService http://192.168.1.204:8080namespace applicationapolloAccesskeySecret cluster seata}zk {serverAddr 127.0.0.1:2181sessionTimeout 6000connectTimeout 2000username password nodePath /seata/seata.properties}etcd3 {serverAddr http://localhost:2379}file {name file.conf}
}3.接着我们需要修改Seata的存储模式修改file.conf文件把Seata的默认存储模式修改为数据库DB同时需要配置JDBC
Server端存储模式store.mode支持三种
file单机模式全局事务会话信息内存中读写并持久化本地文件root.data性能较高默认DB高可用模式全局事务会话信息通过DB共享相对性能差一些redisSeata-Server1.3及以上版本支持性能较高存在事务信息丢失风险需要配合实际场景使用。 ## transaction log store, only used in seata-server
store {## store mode: file、db、redismode db## rsa decryption public keypublicKey ## file store propertyfile {## store location dirdir sessionStore# branch session size , if exceeded first try compress lockkey, still exceeded throws exceptionsmaxBranchSessionSize 16384# globe session size , if exceeded throws exceptionsmaxGlobalSessionSize 512# file buffer size , if exceeded allocate new bufferfileWriteBufferCacheSize 16384# when recover batch read sizesessionReloadReadSize 100# async, syncflushDiskMode async}## database store propertydb {## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari) etc.datasource druid## mysql/oracle/postgresql/h2/oceanbase etc.dbType mysqldriverClassName com.mysql.jdbc.Driver## if using mysql to store the data, recommend add rewriteBatchedStatementstrue in jdbc connection paramurl jdbc:mysql://localhost:3306/seata?useUnicodetruecharacterEncodingUTF-8user rootpassword rootminConn 5maxConn 100globalTable global_tablebranchTable branch_tablelockTable lock_tablequeryLimit 100maxWait 5000}## redis store propertyredis {## redis mode: single、sentinelmode single## single mode propertysingle {host 127.0.0.1port 6379}## sentinel mode propertysentinel {masterName ## such as 10.28.235.65:26379,10.28.235.65:26380,10.28.235.65:26381sentinelHosts }password database 0minConn 1maxConn 10maxTotal 100queryLimit 100}
}
建表语句地址https://github.com/seata/seata/blob/develop/script/server/db/mysql.sql
global_table:全局事务会话表branch_table:分支事务会话表lock_table:锁数据表
-- the table to store GlobalSession data
CREATE TABLE IF NOT EXISTS 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)
) ENGINE InnoDBDEFAULT CHARSET utf8;-- the table to store BranchSession data
CREATE TABLE IF NOT EXISTS branch_table
(branch_id BIGINT NOT NULL,xid VARCHAR(128) NOT NULL,transaction_id BIGINT,resource_group_id VARCHAR(32),resource_id VARCHAR(256),branch_type VARCHAR(8),status TINYINT,client_id VARCHAR(64),application_data VARCHAR(2000),gmt_create DATETIME(6),gmt_modified DATETIME(6),PRIMARY KEY (branch_id),KEY idx_xid (xid)
) ENGINE InnoDBDEFAULT CHARSET utf8;-- the table to store lock data
CREATE TABLE IF NOT EXISTS lock_table
(row_key VARCHAR(128) NOT NULL,xid VARCHAR(128),transaction_id BIGINT,branch_id BIGINT NOT NULL,resource_id VARCHAR(256),table_name VARCHAR(32),pk VARCHAR(36),status TINYINT NOT NULL DEFAULT 0 COMMENT 0:locked ,1:rollbacking,gmt_create DATETIME,gmt_modified DATETIME,PRIMARY KEY (row_key),KEY idx_status (status),KEY idx_branch_id (branch_id)
) ENGINE InnoDBDEFAULT CHARSET utf8;3.3 启动
启动步骤为先启动nacos然后在启动Seata-Server
启动Seata-Server的方式非常简单直接双击此文件即可:seata-server-1.4.2\bin\seata-server.bat 启动完成效果 然后在nacos控制台上就可以看到Seata-Server 3.4 配置信息
在Nacos配置中心中创建对应的配置信息seataServer.properties 注意修改数据库地址
#For details about configuration items, see https://seata.io/zh-cn/docs/user/configurations.html
#Transport configuration, for client and server
transport.typeTCP
transport.serverNIO
transport.heartbeattrue
transport.enableTmClientBatchSendRequestfalse
transport.enableRmClientBatchSendRequesttrue
transport.enableTcServerBatchSendResponsefalse
transport.rpcRmRequestTimeout30000
transport.rpcTmRequestTimeout30000
transport.rpcTcRequestTimeout30000
transport.threadFactory.bossThreadPrefixNettyBoss
transport.threadFactory.workerThreadPrefixNettyServerNIOWorker
transport.threadFactory.serverExecutorThreadPrefixNettyServerBizHandler
transport.threadFactory.shareBossWorkerfalse
transport.threadFactory.clientSelectorThreadPrefixNettyClientSelector
transport.threadFactory.clientSelectorThreadSize1
transport.threadFactory.clientWorkerThreadPrefixNettyClientWorkerThread
transport.threadFactory.bossThreadSize1
transport.threadFactory.workerThreadSizedefault
transport.shutdown.wait3
transport.serializationseata
transport.compressornone#Transaction routing rules configuration, only for the client
service.vgroupMapping.default_tx_groupdefault#Transaction rule configuration, only for the client
client.rm.asyncCommitBufferLimit10000
client.rm.lock.retryInterval10
client.rm.lock.retryTimes30
client.rm.lock.retryPolicyBranchRollbackOnConflicttrue
client.rm.reportRetryCount5
client.rm.tableMetaCheckEnabletrue
client.rm.tableMetaCheckerInterval60000
client.rm.sqlParserTypedruid
client.rm.reportSuccessEnablefalse
client.rm.sagaBranchRegisterEnablefalse
client.rm.sagaJsonParserfastjson
client.rm.tccActionInterceptorOrder-2147482648
client.tm.commitRetryCount5
client.tm.rollbackRetryCount5
client.tm.defaultGlobalTransactionTimeout60000
client.tm.degradeCheckfalse
client.tm.degradeCheckAllowTimes10
client.tm.degradeCheckPeriod2000
client.tm.interceptorOrder-2147482648
client.undo.dataValidationtrue
client.undo.logSerializationjackson
client.undo.onlyCareUpdateColumnstrue
server.undo.logSaveDays7
server.undo.logDeletePeriod86400000
client.undo.logTableundo_log
client.undo.compress.enabletrue
client.undo.compress.typezip
client.undo.compress.threshold64k
#For TCC transaction mode
tcc.fence.logTableNametcc_fence_log
tcc.fence.cleanPeriod1h#Log rule configuration, for client and server
log.exceptionRate100#Transaction storage configuration, only for the server. The file, DB, and redis configuration values are optional.
store.modedb
store.lock.modefile
store.session.modefile#If store.mode,store.lock.mode,store.session.mode are not equal to file, you can remove the configuration block.
store.file.dirfile_store/data
store.file.maxBranchSessionSize16384
store.file.maxGlobalSessionSize512
store.file.fileWriteBufferCacheSize16384
store.file.flushDiskModeasync
store.file.sessionReloadReadSize100#These configurations are required if the store mode is db. If store.mode,store.lock.mode,store.session.mode are not equal to db, you can remove the configuration block.
store.db.datasourcedruid
store.db.dbTypemysql
store.db.driverClassNamecom.mysql.cj.jdbc.Driver
store.db.urljdbc:mysql://127.0.0.1:3306/seata?useUnicodetruerewriteBatchedStatementstruecharacterEncodingUTF-8serverTimezoneAsia/Shanghai
store.db.userroot
store.db.password123456
store.db.minConn5
store.db.maxConn30
store.db.globalTableglobal_table
store.db.branchTablebranch_table
store.db.distributedLockTabledistributed_lock
store.db.queryLimit100
store.db.lockTablelock_table
store.db.maxWait5000#Transaction rule configuration, only for the server
server.recovery.committingRetryPeriod1000
server.recovery.asynCommittingRetryPeriod1000
server.recovery.rollbackingRetryPeriod1000
server.recovery.timeoutRetryPeriod1000
server.maxCommitRetryTimeout-1
server.maxRollbackRetryTimeout-1
server.rollbackRetryTimeoutUnlockEnablefalse
server.distributedLockExpireTime10000
server.xaerNotaRetryTimeout60000
server.session.branchAsyncQueueSize5000
server.session.enableBranchAsyncRemovefalse
server.enableParallelRequestHandlefalse#Metrics configuration, only for the server
metrics.enabledfalse
metrics.registryTypecompact
metrics.exporterListprometheus
metrics.exporterPrometheusPort9898到此seata就配置完成
三、微服务集成seata
1.引入seata相关依赖 dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-seata/artifactIdexclusionsexclusiongroupIdio.seata/groupIdartifactIdseata-spring-boot-starter/artifactId/exclusionexclusiongroupIdio.seata/groupIdartifactIdseata-all/artifactId/exclusion/exclusions/dependencydependencygroupIdio.seata/groupIdartifactIdseata-spring-boot-starter/artifactIdversion1.7.0/version/dependency2.配置application.yml
seata:#事务组根据这个获取tc的五福的cluster名称tx-service-group: default_tx_group#事物模式data-source-proxy-mode: XA#注册中心registry:type: nacosnacos:application: seata-server #tc服务在nacos的服务名称server-addr: 127.0.0.1:8848username: nacospassword: nacosgroup: SEATA_GROUP#配置中心config:type: nacosnacos:server-addr: 127.0.0.1:8848username: nacospassword: nacosgroup: SEATA_GROUP #nacos配置中心的Groupdata-id: seataServer.properties #nacos配置中心的data-id此时集成完成在业务方法上加上 GlobalTransactional注解使用即可
示例
3.XA模式
XA 规范 是 X/Open 组织定义的分布式事务处理DTPDistributed Transaction Processing标准
XA 规范 描述了全局的事务管理器与局部的资源管理器之间的接口。 XA规范 的目的是允许的多个资源如数据库应用服务器消息队列等在同一事务中访问这样可以使 ACID 属性跨越应用程序而保持有效。
XA 规范 使用两阶段提交2PCTwo-Phase Commit来保证所有资源同时提交或回滚任何特定的事务。
XA 规范 在上世纪 90 年代初就被提出。目前几乎所有主流的数据库都对 XA 规范 提供了支持。
DTP模型定义如下角色
AP即应用程序可以理解为使用DTP分布式事务的程序RM资源管理器可以理解为事务的参与者一般情况下是指一个数据库的实例MySql通过资源管理器对该数据库进行控制资源管理器控制着分支事务TM事务管理器负责协调和管理事务事务管理器控制着全局事务管理实务生命周期并协调各个RM。全局事务是指分布式事务处理环境中需要操作多个数据库共同完成一个工作这个工作即是一个全局事务。DTP模式定义TM和RM之间通讯的接口规范叫XA简单理解为数据库提供的2PC接口协议基于数据库的XA协议来实现的2PC又称为XA方案。 案例解释
应用程序AP持有订单库和商品库两个数据源。应用程序AP通过TM通知订单库RM和商品库RM来创建订单和减库存RM此时未提交事务此时商品和订单资源锁定。TM收到执行回复只要有一方失败则分别向其他RM发送回滚事务回滚完毕资源锁释放。TM收到执行回复全部成功此时向所有的RM发起提交事务提交完毕资源锁释放。
3.2 前提
支持XA 事务的数据库。Java 应用通过 JDBC 访问数据库。
3.3 整体机制
在 Seata 定义的分布式事务框架内利用事务资源数据库、消息服务等对 XA 协议的支持以 XA 协议的机制来管理分支事务的一种 事务模式。 执行阶段 可回滚业务 SQL 操作放在 XA 分支中进行由资源对 XA 协议的支持来保证 可回滚持久化XA 分支完成后执行 XA prepare同样由资源对 XA 协议的支持来保证 持久化 即之后任何意外都不会造成无法回滚的情况 完成阶段 分支提交执行 XA 分支的 commit分支回滚执行 XA 分支的 rollback
3.4 Seata XA模式优势确定
优势
业务无侵入和 AT 一样XA 模式将是业务无侵入的不给应用设计和开发带来额外负担。支持多种数据库 XA 协议被主流关系型数据库广泛支持不需要额外的适配即可使用支持多种语言强一致性
缺点
会有阻塞模式 降低性能
3.5 案例讲解
seata: data-source-proxy-mode: XA # 开启数据源代理的XA模式1.首先设计两个服务一个订单order8801 一个库存stock8802
2.stock库存表为 sql:
CREATE TABLE t_stock (id int NOT NULL AUTO_INCREMENT,product_id int DEFAULT NULL,count int DEFAULT NULL,money int DEFAULT NULL,PRIMARY KEY (id)
);3.order订单表为 sql:
CREATE TABLE t_order (id int NOT NULL AUTO_INCREMENT,product_id int DEFAULT NULL COMMENT 商品编号,count int DEFAULT NULL COMMENT 数量,PRIMARY KEY (id)
) ;4.订单服务通过OpenFegin远程调用库存服务然后库存服务减库存订单服务生成订单完成基本的调用以后我们给订单服务添加异常
GlobalTransactional
Service
public class OrderServiceImpl implements OrderService {Resourceprivate OrderMapper orderMapper;Resourceprivate StockClient stockClient;Overridepublic void create() {// 减库存stockClient.decrement();// 添加异常int i 1/0;// 创建订单orderMapper.create();}
}执行
4. AT模式
4.1 前提
基于支持本地 ACID 事务的关系型数据库。Java 应用通过 JDBC 访问数据库。
4.1 整体机制
两阶段提交协议的演变
阶段一RM的工作
注册分支事务记录undo-log数据快照执行业务sql并提交报告事务状态
阶段二提交时RM的工作
删除undo-log即可
阶段二回滚时RM的工作
根据undo-log恢复数据到更新前 4.2 案例讲解
seata: data-source-proxy-mode: AT 4.2.1 没有事物案例
1.订单服务通过OpenFegin远程调用库存服务然后库存服务减库存订单服务生成订单完成基本的调用以后我们给订单服务添加异常
Service
public class OrderServiceImpl implements OrderService {Resourceprivate OrderMapper orderMapper;Resourceprivate StockClient stockClient;Overridepublic void create() {// 减库存stockClient.decrement();// 添加异常int i 1/0;// 创建订单orderMapper.create();}
}2.此时我们会发现访问接口出现异常情况但是库存减少订单没有增加此时已经出现了分布式事务的问题 3.订单表没有增加数据 4.2.2 通过Seata的AT模式解决分布式事务
1.在对应的微服务数据库上加上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)
) ENGINEInnoDB AUTO_INCREMENT1 DEFAULT CHARSETutf8;2.需要在业务方法上添加GlobalTransactional注解
GlobalTransactional
Service
public class OrderServiceImpl implements OrderService {Resourceprivate OrderMapper orderMapper;Resourceprivate StockClient stockClient;Overridepublic void create() {// 减库存stockClient.decrement();// 添加异常int i 1/0;// 创建订单orderMapper.create();}
}3.把8801和8802都跑起来当然Nacos和Seata都要进行启动这个时候我们进行访问Order的REST接口http://localhost:8801/order/create我们就会发现此时已经解决了分布式事务问题。
库存没有减少 订单也没有增加
4.那么为了验证undo_log表用于存储回滚的数据我们在OrderServiceImpl上异常位置添加断点同时以debug方式来启动8801订单服务 然后访问接口http://localhost:8801/order/create程序会卡在断点上此时我们来查看undo_log表和库存表此时我们会发现库存确实减少了但是在undo_log表中出现了快照记录了当前修改前的数据这个数据就是用于回滚的数据
库存减少 undo_log表记录快照 放行以后库存数量回复回滚生效 此时我们就验证了AT事务的执行过程。
5.TCC模式
首先我们先来了解常规的TCC模式。
5.1什么是TCC
TCC 是分布式事务中的二阶段提交协议它的全称为 Try-Confirm-Cancel即资源预留Try、确认操作Confirm、取消操作Cancel他们的具体含义如下
Try对业务资源的检查并预留Confirm对业务处理进行提交即 commit 操作只要 Try 成功那么该步骤一定成功Cancel对业务处理进行取消即回滚操作该步骤回对 Try 预留的资源进行释放。
TCC 是一种侵入式的分布式事务解决方案以上三个操作都需要业务系统自行实现对业务系统有着非常大的入侵性设计相对复杂但优点是 TCC 完全不依赖数据库能够实现跨数据库、跨应用资源管理对这些不同数据访问通过侵入式的编码方式实现一个原子操作更好地解决了在各种复杂业务场景下的分布式事务问题。 5.2 Seata的TCC模式
Seata TCC 模式跟通用型 TCC 模式原理一致。
5.3 TCC和AT区别
AT 模式基于 支持本地 ACID 事务 的 关系型数据库
一阶段 prepare 行为在本地事务中一并提交业务数据更新和相应回滚日志记录。二阶段 commit 行为马上成功结束自动 异步批量清理回滚日志。二阶段 rollback 行为通过回滚日志自动 生成补偿操作完成数据回滚。
相应的TCC 模式不依赖于底层数据资源的事务支持
一阶段 prepare 行为调用 自定义 的 prepare 逻辑。二阶段 commit 行为调用 自定义 的 commit 逻辑。二阶段 rollback 行为调用 自定义 的 rollback 逻辑。
所谓 TCC 模式是指支持把 自定义 的分支事务纳入到全局事务的管理中。 5.4 特点
侵入性比较强并且需要自己实现相关事务控制逻辑在整个过程基本没有锁性能较强
具体使用案例https://seata.io/zh-cn/blog/integrate-seata-tcc-mode-with-spring-cloud.html
5.5.TCC存在的问题
1.空回滚以及解决方案
空回滚指的是在一个分布式事务中在没有调用参与方的 Try 方法的情况下TM 驱动二阶段回滚调用了参与方的 Cancel 方法。 如上图所示全局事务开启后参与者 A 分支注册完成之后会执行参与者一阶段 RPC 方法如果此时参与者 A 所在的机器发生宕机网络异常都会造成 RPC 调用失败即参与者 A 一阶段方法未成功执行但是此时全局事务已经开启Seata 必须要推进到终态在全局事务回滚时会调用参与者 A 的 Cancel 方法从而造成空回滚。
要想防止空回滚那么必须在 Cancel 方法中识别这是一个空回滚Seata 是如何做的呢
Seata 的做法是新增一个 TCC 事务控制表包含事务的 XID 和 BranchID 信息在 Try 方法执行时插入一条记录表示一阶段执行了执行 Cancel 方法时读取这条记录如果记录不存在说明 Try 方法没有执行。
2.幂等问题以及解决方案
幂等问题指的是 TC 重复进行二阶段提交因此 Confirm/Cancel 接口需要支持幂等处理即不会产生资源重复提交或者重复释放。 如上图所示参与者 A 执行完二阶段之后由于网络抖动或者宕机问题会造成 TC 收不到参与者 A 执行二阶段的返回结果TC 会重复发起调用直到二阶段执行结果成功。
Seata 是如何处理幂等问题的呢
同样的也是在 TCC 事务控制表中增加一个记录状态的字段 status该字段有 3 个值分别为
tried1committed2rollbacked3
二阶段 Confirm/Cancel 方法执行后将状态改为 committed 或 rollbacked 状态。当重复调用二阶段 Confirm/Cancel 方法时判断事务状态即可解决幂等问题。
3.悬挂问题以及解决方案
悬挂指的是二阶段 Cancel 方法比 一阶段 Try 方法优先执行由于允许空回滚的原因在执行完二阶段 Cancel 方法之后直接空回滚返回成功此时全局事务已结束但是由于 Try 方法随后执行这就会造成一阶段 Try 方法预留的资源永远无法提交和释放了 如上图所示在执行参与者 A 的一阶段 Try 方法时出现网路拥堵由于 Seata 全局事务有超时限制执行 Try 方法超时后TM 决议全局回滚回滚完成后如果此时 RPC 请求才到达参与者 A执行 Try 方法进行资源预留从而造成悬挂。
Seata 是怎么处理悬挂的呢
在 TCC 事务控制表记录状态的字段 status 中增加一个状态
suspended4
当执行二阶段 Cancel 方法时如果发现 TCC 事务控制表有相关记录说明二阶段 Cancel 方法优先一阶段 Try 方法执行因此插入一条 status4 状态的记录当一阶段 Try 方法后面执行时判断 status4 则说明有二阶段 Cancel 已执行并返回 false 以阻止一阶段 Try 方法执行成功。
6.SAGA模式
Saga模式是SEATA提供的长事务解决方案在Saga模式中业务流程中每个参与者都提交本地事务当出现某一个参与者失败则补偿前面已经成功的参与者一阶段正向服务和二阶段补偿服务都由业务开发实现。
Saga模式是SEATA提供的长事务解决方案。也分为两个阶段
一阶段直接提交本地事务二阶段成功则什么都不做失败则通过编写补偿业务来回滚
Saga模式优点 事务参与者可以基于事件驱动实现异步调用吞吐高 一阶段直接提交事务无锁性能好 不用编写TCC中的三个阶段实现简单
缺点
软状态持续时间不确定时效性差没有锁没有事务隔离会有脏写
为什么需要Saga
之前我们学习的Seata分布式三种操作模型中所使用的的微服务全部可以根据开发者的需求进行修改但是在一些特殊环境下比如老系统封闭的系统无法修改同时没有任何分布式事务引入那么AT、XA、TCC模型将全部不能使用为了解决这样的问题才引用了Saga模型。
比如事务参与者可能是其他公司的服务或者是遗留系统无法改造可以使用Saga模式。 Saga模式是Seata提供的长事务解决方案提供了异构系统的事务统一处理模型。在Saga模式中所有的子业务都不在直接参与整体事务的处理只负责本地事务的处理而是全部交由了最终调用端来负责实现而在进行总业务逻辑处理时在某一个子业务出现问题时则自动补偿全面已经成功的其他参与者这样一阶段的正向服务调用和二阶段的服务补偿处理全部由总业务开发实现。 6.总结
在当前的技术发展阶段不存一个分布式事务处理机制可以完美满足所有场景的需求。
一致性、可靠性、易用性、性能等诸多方面的系统设计约束需要用不同的事务处理机制去满足。
Seata 项目最核心的价值在于构建一个全面解决分布式事务问题的 标准化 平台。
基于 Seata上层应用架构可以根据实际场景的需求灵活选择合适的分布式事务解决方案。 XA 模式的加入补齐了 Seata 在 全局一致性 场景下的缺口形成 AT、TCC、Saga、XA 四大 事务模式 的版图基本可以满足所有场景的分布式事务处理诉求。