目录一、分布式事务二、理论基础三、初识seata一、分布式事务事务的ACID原则原子性(A): 事务中的所有操作,要么全部成功,要么全部失败。一致性©: 要保证数据库内部完整性约束、声明性约束。隔离性(I): 对同一资源操作的事务不能同时发生。持久性(D): 对数据库做的一切修改将永久保存,不管是否出现故障。分布式服务案例微服务下单业务,在下单时会调用订单服务,创建订单并写入数据库。然后订单服务调用账户服务和库存服务:账户服务负责扣减用户余额库存服务负责扣减商品库存分布式服务的事务问题在分布式系统下,一个业务跨越多个服务或数据源,每个服务都是一个分支事务,要保证所有分支事务最终状态一致,这样的事务就是分布式事务。二、理论基础CAP定理分布式系统有三个指标:Consistency(一致性)Availability(可用性)Partition tolerance(分区容错性)分布式系统无法同时满足这三个指标,这个结论就叫做CAP定理。CAP定理-ConsistencyConsistency(一致性): 用户访问分布式系统中的任意节点,得到的数据必须一致。CAP定理-AvailabilityAvailability(可用性): 用户访问集群中的任意健康节点,必须能得到响应,而不是超时或拒绝。CAP定理-Partition tolerancePartition(分区): 因为网络故障或其它原因导致分布式系统中的部分节点与其它节点失去连接,形成独立分区。Tolerance(容错): 在集群出现分区时,整个系统也要持续对外提供服务。总结:简述CAP定理内容?分布式系统节点通过网络连接,一定会出现分区问题§当分区出现时,系统的一致性©和可用性(A)就无法同时满足思考: elasticsearch集群是CP还是AP?ES集群出现分区时,故障节点会被剔除集群,数据分片会重新分配到其它节点,保证数据一致。因此是低可用性,高一致性,属于CPBASE理论BASE理论是对CAP的一种解决思路,包含三个思想:Basically Available(基本可用): 分布式系统在出现故障时,允许损失部分可用性,即保证核心可用。Soft State(软状态): 在一定时间内,允许出现中间状态,比如: 临时的不一致状态。Eventually Consistent(最终一致性): 虽然无法保证强一致性,但是在软状态结束后,最终达到数据一致。而分布式事务最大的问题是各个子事务的一致性问题,因此可以借鉴CAP定理和BASE理论:AP模式: 各子事务分别执行和提交,允许出现结果不一致,然后采用弥补措施恢复数据即可,实现最终一致。CP模式: 各个子事务执行后互相等待,同时提交,同时回滚,达成强一致。但事务等待过程中,处于弱可用状态。分布式事务模型解决分布式事务,各个子系统之间必须能感知到彼此的事务状态,才能保证状态一致,因此需要一个事务协调者来协调每一个事务的参与者(子系统事务)。这里的子系统事务,称为分支事务;有关联的各个分支事务在一起称为全局事务。总结:简述BASE理论三个思想:基本可用软状态最终一致解决分布式事务的思想和模型:全局事务: 整个分布式事务分支事务: 分布式事务中包含的每个子系统的事务最终一致思想: 各分支事务分别执行并提交,如果有不一致的情况,再想办法恢复数据强一致思想: 各分支事务执行完业务不要提交,等待彼此结果。而后统一提交或回滚。三、初识seata初识SeataSeata是2019年1月份蚂蚁金服和阿里巴巴共同开源的分布式事务解决方案。致力于提供高性能和简单易用的分布式事务服务,为用户打造一站式的分布式解决方案。Seata提供了四种不同的分布式事务解决方案:XA模式: 强一致性分阶段事务模式,牺牲了一定的可用性,无业务侵入TCC模式: 最终一致的分阶段事务模式,有业务侵入AT模式: 最终一致的分阶段事务模式,无业务侵入,也是Seata的默认模式SAGA模式: 长事务模式,有业务侵入Seata架构Seata事务管理中有三个重要的角色:TC(Transaction Coordinator)-事务协调者: 维护全局和分支事务的状态,协调全局事务提交或回滚。TM(Transaction Manager)-事务管理器: 定义全局事务的范围,开启全局事务、提交或回滚全局事务RM(Resource Manager)-资源管理器: 管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。部署TC服务下载seata-server包在非中文目录解压缩这个zip包,其目录结构如下:修改conf目录下的registry.conf文件:registry { # 注册中心类型 file、nacos、eureka、redis、zk、consul、etcd3、sofa type”nacos” nacos { application”seata-tc-server” serverAddr”127.0.0.1:8848” group”DEFAULT_GROUP” namespace”” cluster”SH” username”nacos” password”nacos” } } config{ # 配置中心 file、nacos、appollo、zk、consul、etcd3 type”nacos” nacos { serverAddr”127.0.0.1:8848” namespace”” group”SEATA_GROUP” username”nacos” password”nacos” dataId”seataServer.properties” } }seataServer.properties内容如下:# 数据存储方式,db代表数据库 store.modedb store.db.datasourcedruid store.db.dbTypemysql store.db.driverClassNamecom.mysql.jdbc.Driver store.db.urljdbc:mysql://127.0.0.1:3306/seata?useUnicodetruerewriteBatchedStatementstrue store.db.userroot store.db.password123456 store.db.minConn5 store.db.maxConn30 store.db.globalTableglobal_table store.db.branchTablebranch_table store.db.queryLimit100 store.db.lockTablelock_table store.db.maxWait5000 # 事务、日志等配置 server.recovery.committingRetryPeriod1000 server.recovery.asynCommittingRetryPeriod1000 server.recovery.rollbackingRetryPeriod1000 server.recovery.timeoutRetryPeriod1000 server.maxCommitRetryTimeout-1 server.maxRollbackRetryTimeout-1 server.rollbackRetryTimeoutUnlockEnablefalse server.undo.logSaveDays7 server.undo.logDeletePeriod86400000 # 客户端与服务端传输方式 transport.serializationseata transport.compressornone # 关闭metrics功能,提高性能 metrics.enabledfalse metrics.registryTypecompact metrics.exporterListprometheus metrics.exporterPrometheusPort9898seata-tc-server.sql内容如下: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_status_gmt_modified (status,gmt_modified), KEY idx_transaction_id (transaction_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; 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) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;启动TC服务进入bin目录,运行其中的seata-server.bat即可,启动成功后,seata-server应该已经注册到nacos注册中心了。微服务集成Seata首先,引入seata相关依赖:然后,配置application.yml,让微服务通过注册中心找到seata-tc-server:总结:nacos服务名称组成包括?namespacegroupserviceNameclusterseata客户端获取tc的cluster名称方式?以tx-group-service的值为key到vgroupMapping中查找XA模式原理XA规范是X/Open组织定义的分布式事务处理(DTP, Distributed Transaction Processing 分布式事务处理)标准,XA规范描述了全局的TM与局部的RM之间的接口,几乎所有主流的数据库都对XA规范提供了支持。![图片13](https://i-blog.csdnimg.cn/direct/ef42c66f90f9453eb51d172d1856a2b1.png 图片13)seata的XA模式seata的XA模式做了一些调整,但大体相似:RM一阶段的工作:注册分支事务到TC执行分支业务sql但不提交报告执行状态到TCTC二阶段的工作:TC检测各分支事务执行状态如果都成功,通知所有RM提交事务如果都失败,通知所有RM回滚事务RM二阶段工作:接收TC指令,提交或回滚事务总结:XA模式的优点是什么?事务的强一致性,满足ACID原则常用数据库都支持,实现简单,并且没有代码侵入XA模式的缺点是什么?因为一阶段需要锁定数据库资源,等待二阶段结束才释放,性能较差依赖关系型数据库实现事务实现XA模式Seata的starter已经完成了XA模式的自动装配,实现非常简单,步骤如下:修改application.yml文件(每个参与事务的微服务),开启XA模式:seata: />重启服务并测试AT模式原理AT模式同样是分阶段提交的事务模型,不过却弥补了XA模型中资源锁定周期过长的缺陷。阶段一RM的工作:注册分支事务记录undo-log(数据快照)执行业务sql并提交报告事务状态阶段二提交时RM的工作:删除undo-log即可阶段二回滚时RM的工作:根据undo-log恢复数据到更新前AT模式原理例如: 一个分支业务的sql是这样的: update tb_account set moneymoney-10 where id1总结:简述AT模式与XA模式最大的区别是什么?XA模式一阶段不提交事务,锁定资源;AT模式一阶段直接提交,不锁定资源。XA模式依赖数据库机制实现回滚;AT模式利用数据快照实现数据回滚。XA模式强一致;AT模式最终一致AT模式的脏写问题AT模式的写隔离总结:AT模式的优点:一阶段完成直接提交事务,释放数据库资源,性能比较好利用全局锁实现读写隔离没有代码侵入,框架自动完成回滚和提交AT模式的缺点:两阶段之间属于软状态,属于最终一致框架的快照功能会影响性能,但比XA模式要好很多实现AT模式AT模式中的快照生成、回滚等动作都是由框架自动完成,没有任何代码侵入,因此实现非常简单。CREATE TABLE lock_table ( row_key varchar(128) NOT NULL, xid varchar(128) NOT NULL, transaction_id bigint, branch_id bigint, resource_id varchar(256) , table_name varchar(32) , pk varchar(36) , gmt_create datetime, gmt_modified datetime, status int NOT NULL DEFAULT 0 COMMENT 0:locked ,1:rollbacking, PRIMARY KEY (row_key), KEY idx_status (status) ) ENGINEInnoDB DEFAULT CHARSETutf8; CREATE TABLE undo_log( id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT increment id, branch_id BIGINT(20) NOT NULL COMMENT branch transaction id, xid VARCHAR(128) NOT NULL COMMENT global transaction id, context VARCHAR(128) NOT NULL COMMENT undo_log context,such as serialization, rollback_info LONGBLOB NOT NULL COMMENT rollback info, log_status INT(11) NOT NULL COMMENT 0:normal status,1:defense status, log_created DATETIME NOT NULL COMMENT create datetime, log_modified DATETIME NOT NULL COMMENT modify datetime, PRIMARY KEY (id), UNIQUE KEY ux_undo_log (xid, branch_id) ) ENGINE InnoDB AUTO_INCREMENT 1 DEFAULT CHARSET utf8;lock_table表导入到TC服务关联的数据库,undo_log表导入到微服务关联的数据库。修改application.yml文件,将事务模式修改为AT模式即可:seata: >TCC模式原理TCC模式与AT模式非常相似,每阶段都是独立事务,不同的是TCC通过人工编码来实现数据恢复。需要实现三个方法:Try: 资源的检测和预留Confirm: 完成资源操作业务;要求Try成功Confirm一定要能成功Cancel: 预留资源释放,可以理解为Try的反向操作举例,一个扣减用户余额的业务。假设账户A原来余额是100,需要余额扣减30元。阶段一(Try): 检查余额是否充足,如果充足则冻结金额增加30元,可用余额扣除30阶段二: 假如要提交(Confirm),则冻结金额扣减30阶段三: 如果要回滚(Cancel),则冻结金额扣减30,可用余额增加30TCC的工作模型图:总结:TCC模式的每个阶段是做什么的?Try: 资源检查和预留Confirm: 业务执行和提交Cancel: 预留资源的释放TCC的优点是什么?一阶段完成直接提交事务,释放数据库资源,性能好相比AT模型,无需生成快照,无需使用全局锁,性能最强不依赖数据库事务,而是依赖补偿操作,可以用于非事务型数据库TCC的缺点是什么?有代码侵入,需要人为编写try、Confirm和Cancel接口,太麻烦软状态,事务是最终一致需要考虑Confirm和Cancel的失败情况,做好幂等处理TCC的空回滚和业务悬挂当某分支事务的try阶段阻塞时,可能导致全局事务超时而触发二阶段的cancel操作。在未执行try操作时先执行了cancel操作,这时cancel不能做回滚,就是空回滚。对于已经空回滚的业务,如果以后继续执行try,就永远不可能confirm或cancel,这就是业务悬挂。应当阻止执行空回滚后的try操作,避免悬挂。业务分析为了实现空回滚,防止业务悬挂,以及幂等性要求。我们必须在数据库记录冻结金额的同时,记录当前事务id和执行状态,为此我们设计了一张表:Try业务:记录冻结金额和事务状态到account_freeze表扣减account表可用金额Confirm业务:根据xid删除account_freeze表的冻结记录Cancel业务:修改account_freeze表,冻结金额为0,state为2修改account表,恢复可用金额如何判断是否空回滚?Cancel业务中,根据xid查询account_freeze,如果为null则说明try还没做,需要空回滚如何避免业务悬挂?Try业务中,根据xid查询account_freeze,如果已经存在则证明Cancel已经执行,拒绝执行try业务声明TCC接口TCC的try、Confirm、Cancel方法都需要在接口中基于注解来声明,语法如下:Saga模式Saga模式是seata提供的长事务解决方案。也分为两个阶段:一阶段: 直接提交本地事务二阶段: 成功则什么都不做;失败则通过编写补偿业务来回滚Saga模式优点:事务参与者可以基于事件驱动实现异步调用,吞吐高一阶段直接提交事务,无锁,性能好不用编写TCC中的三个阶段,实现简单缺点:软状态持续时间不确定,时效性差没有锁,没有事务隔离,会有脏写四种模式对比XAATTCCSAGA一致性强一致弱一致弱一致最终一致隔离性完全隔离基于全局锁隔离基于资源预留隔离无隔离代码侵入无无有,要编写三个接口有,要编写状态机和补偿业务性能差好非常好非常好场景对一致性、隔离性有高要求的业务基于关系型数据库的大多数分布式事务场景都可以对性能要求较高的事务。有非关系型数据库要参与的事务。业务流程长、业务流程多。参与者包含其它公司或遗留系统服务,无法提供TCC模式要求的三个接口TC的异地多机房容灾架构TC服务作为Seata的核心服务,一定要保证高可用和异地容灾。模拟异地容灾的TC集群计划启动两台Seata的tc服务节点:节点名称ip地址端口号集群名称seata127.0.0.18091SHseata2127.0.0.18092HZ之前已经启动了一台seata服务,端口是8091,集群名为SH。将seata目录复制一份,命名为seata2。修改seata2/conf/registry.conf内容如下:registry { # 注册中心类型 file、nacos、eureka、redis、zk、consul、etcd3、sofa type”nacos” nacos { application”seata-tc-server” serverAddr”127.0.0.1:8848” group”DEFAULT_GROUP” namespace”” cluster”HZ” username”nacos” password”nacos” } } config{ # 配置中心 file、nacos、appollo、zk、consul、etcd3 type”nacos” nacos { serverAddr”127.0.0.1:8848” namespace”” group”SEATA_GROUP” username”nacos” password”nacos” dataId”seataServer.properties” } }进入seata2/bin目录,然后运行命令:seata-server.bat -p 8092将事务组映射配置到nacos将tx-service-group与cluster的映射关系都配置到nacos配置中心。新建一个配置:配置的内容如下:# 事务组映射关系 service.vgroupMapping.seata-demoSH service.enableDegradefalse Service.disableGlobalTransactionfalse # 与TC服务的通信配置 transport.typeTCP transport.serverNIO transport.heartbeattrue transport.enableClientBatchSendRequestfalse transport.threadFactory.bossThreadPrefixNettyBoss transport.threadFactory.workerThreadPrefixNettyServerNIOWorker transport.threadFactory.serverExecutorThreadPrefixNettyServerBizHandler transport.threadFactory.shareBossWorkerfalse transport.threadFactory.clientSelectorThreadPrefixNettyClientSelector transport.threadFactory.clientSelectorThreadSize1 transport.threadFactory.clientWorkerThreadPrefixNettyClientWorkderThread transport.threadFactory.bossThreadSize1 transport.threadFactory.workerThreadSizedefault transport.shutdown.wait3 # RM配置 client.rm.asyncCommitBufferLimit10000 client.rm.lock.retryInterval10 client.rm.lock.retryTimes30 client.rm.lock.retryPolicyBranchRollbackOnConflicttrue client.rm.reportRetryCount5 client.rm.tableMetaCheckEnablefalse client.rm.tableMetaCheckInterval60000 client.rm.sqlParserTypedruid client.rm.reportSuccessEnablefalse client.rm.sagaBranchRegisterEnablefalse # TM配置 client.tm.commitRetryCount5 client.tm.rollbackRetryCount5 client.tm.defaultGlobalTransactionTimeout60000 client.tm.degradeCheckfalse client.tm.degradeCheckAllowTimes10 client.tm.degradeCheckPeriod2000 # undo日志配置 client.undo.dataValidationtrue client.undo.logSerializationjackson client.undo.onlyCareUpdateColumnstrue client.undo.logTableundo_log client.undo.compress.enabletrue client.undo.compress.typezip client.undo.compress.threshold64k client.log.exceptionRate100微服务读取nacos配置需要修改每一个微服务的application.yml文件,让微服务读取nacos中的client.properties文件:seata: config: type: nacos nacos: server-addr: 127.0.0.1:8848 username: nacos password: nacos group: SEATA-GROUP >