- 本地事务: 就是传统的单机事务
- 事务的4大特性ACID:
- C一致性:修改数据后 前后的数据总和要保持一致
- A原子性:事务中的所有操作要么全部成功,要么全部失败
- D隔离性:对同一资源操作的事务不能同时发生
- I持久性:对数据的修改是永久性的
- 分布式事务
- 就是值不是在单个服务或单个数据库的架构下(分布式架构)下产生的事务
- CAD理论
- 分布式系统有3个指标:(3个指标不能同时满足)
- A可用性:用户访问服务中的任意健康节点,必须能得到响应,而不是超时或拒绝
- C一致性:用户访问分布式的任意节点,得到的数据必须是一致
- P分区容错性:因为网络故障或其他的原因导致分布式中部分节点是去连接,形成独立分区
- p是不可避免的;所以C和A不能同时错存在 只能是AP或者CP
- 分布式系统有3个指标:(3个指标不能同时满足)
- BASE理论:
- base理论是对CAP的一种解决思路:包含三个思想
- basiclly avaliable(基本可用):分布式系统出现故障时,允许损失部分可用性,保证系统的核心功能可用
- soft state(软状态):在一定的时间内,允许出现一定的数据不一致的中间状态
- eventually consistent(最终一致):虽然无法保证强一致性:但是在软状态结束后,能保证数据最终一致
- 解决分布式事务的思路:
- AP:各个子事务分别提交和执行,允许出现结果不一致,最终采取补救措施,实现数据一致
- CP:各个子事务执行后,互相等待,同时提交,同时回滚,达到数据的强一致,在等待过程中,处于弱可用状态
- 不管那种解决方式:都需要一个事务的协调者TC,协调各个子事务之间的通信,状态
- base理论是对CAP的一种解决思路:包含三个思想
seata:
- 阿里开源的一款分布式事务一站式解决方案的框架
- seata的三个重要的角色
- TC(Transaction coordinator)事务协调者:维护全局事务和分支事务的状态,协调全局事务提交或者回滚
- TM(teansaction manager)事务管理器:定义全局事务的范围,开始全局事务的提交回滚
- RM(Resource manager)资源管理器:管理分支事务处理的资源,与TC交谈以及注册分支事务和报告分支事务的状态,并驱动分支事务提交或者回滚
- seata基于上述架构提供了4种分布式事务的解决方案
- XA模式:保证事务的强一致,牺牲了一定的可用性
- TCC:最终一直的分阶段事务模式,有业务侵入
- AT:最终一致的分布式事务模式,无侵入,也是seata的默认模式
- SAGA模式:长事务模式,有业务侵入
seata的代码实现
<!--seata--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-seata</artifactId><exclusions><!--版本较低,1.3.0,因此排除--><exclusion><artifactId>seata-spring-boot-starter</artifactId><groupId>io.seata</groupId></exclusion></exclusions></dependency><dependency><groupId>io.seata</groupId><artifactId>seata-spring-boot-starter</artifactId><!--seata starter 采用1.4.2版本--><version>${seata.version}</version></dependency>
配置TC的地址(事务的协调者就是seata服务端)
seata:registry: # TC服务注册中心的配置,微服务根据这些信息去注册中心获取tc服务地址type: nacos # 注册中心类型 nacosnacos:server-addr: 127.0.0.1:8848 # nacos地址namespace: "" # namespace,默认为空group: DEFAULT_GROUP # 分组,默认是DEFAULT_GROUPapplication: seata-tc-server # seata服务名称username: nacospassword: nacostx-service-group: seata-demo # 事务组名称service:vgroup-mapping: # 事务组与cluster的映射关系seata-demo: SHseata:data-source-proxy-mode: XA #这个可以选择AT模式
传统的XA接口模式 遵循2pc协议 2个阶段:
- 一阶段
- 事务的协调者通知每个事务的参与者执行本地事务
- 本地事务完成后通知事务的协调者,此时事务不提交,继续持有数据库锁
- 二阶段
- 如果第一阶段都成功:则通知所有的事务参与者提交事务
- 如果第一阶段没有全部成功:则通知所有的事务参与者回滚事务
- 一阶段
- seata的XA模式
- RM的一阶段
- 注册分支事务到TC
- 执行分支事务的sql 但是不提交
- 报告执行状态到TC
- TC的二阶段
- 检查各个分支事务的状态:
- 如果都成功:则通知所有的RM 提交事务
- 如果有失败:通知所有的RM 回滚事务
- 检查各个分支事务的状态:
- RM的二阶段:接收TC 指定,执行任务
- RM的一阶段
AT模式
- 在涉及事务的服务导入依赖,在方法上加上注解@globaltransaction 配置相关信息
- 在数据库创建3张表 全局事务表 :用于保存全局事务的信息;分支事务表 :用于保存分支事务的信息和全局锁表用户保存操作的数据的锁信息
- 在每一个修改的数据库都传建一个undo_log日志表 用来保存 修改前的数据(前置镜像)和修改后的数据 (后置镜像)
- 执行流程:
- 执行事务方法 :@globaltransaction等于一个拦截器 ,拦截器就会在全局事务表注册写入一条数据 保存XID ,Status ,applicacationnaId,和事务名字;
- 执行分支事务的时候,然后把 xid 分支id application 状态等信息存数据源会被seata的数据源代理,先解析执行的sql , 获取sql的表名,要操作的字段,以及条件,先去数据库 查询到修改之前的值作为前置镜像, 然后获取本地db锁 执行sql,提交执行必须获取全局锁才可以提交,保证了避免出现并发修改的问题, 提交后释放本地锁再次查询修改后的值作为后置镜像, 把XId 和分支id(branchid)和raollbackinfo(前置镜像和后置镜像)存入undo_log表 :这个表必须和修改的表在同一个数据库,保证是一个本地事务要么一起成功,要么一起失败
- 如果每一个分支事务都执行成功,则会删除undo_log的数据 和全局事务表,分支事务表,以及全局锁表的数据 这个时候就会释放全局锁 ,其他的线程就可以获取全局锁来操作
- 如果有一个分支事务没有成功:就会去undo_log表获取数据 先用后置镜像和修改的表 中数据对比是否一直,如果一致,就把把数据恢复到前置镜像的值,然后删除全局事务表的数据…等 释放全局锁资源 这样避免了数据中途有别的写操作(人工或seata以外的事务操作) 如果不一致就会报错,并记录异常,
- XA 和AT对比
- xa 保证了数据的强一致性; 但是有阻塞 性能不好
- at 没有阻塞,性能好, 但是有脏读, 软状态,终止数据一致
- 2者都必须依赖关系型数据库,以及jdbc才能实现
- TCC模式
- TCC模式与AT模式非常相似,每阶段都是独立事务,不同的是TCC通过人工编码来实现数据恢复。需要实现三个方法:
- Try:资源的检测和预留;
- Confirm:完成资源操作业务;要求 Try 成功 Confirm 一定要能成功。
- Cancel:预留资源释放,可以理解为try的反向操作。
- 流程分析:
举例,一个扣减用户余额的业务。假设账户A原来余额是100,需要余额扣减30元。
Try:资源检查和预留
- Confirm:业务执行和提交
- Cancel:预留资源的释放
TCC的优点是什么?
- 一阶段完成直接提交事务,释放数据库资源,性能好
- 相比AT模型,无需生成快照,无需使用全局锁,性能最强
- 不依赖数据库事务,而是依赖补偿操作,可以用于非事务型数据库
TCC的缺点是什么?
- 有代码侵入,需要人为编写try、Confirm和Cancel接口,太麻烦
- 软状态,事务是最终一致
需要考虑Confirm和Cancel的失败情况,做好幂等处理
4.4.SAGA模式
分布式事务内有多个参与者,每一个参与者都是一个冲正补偿服务,需要用户根据业务场景实现其正向操作和逆向回滚操作。
分布式事务执行过程中,依次执行各参与者的正向操作,如果所有正向操作均执行成功,那么分布式事务提交。如果任何一个正向操作执行失败,那么分布式事务会去退回去执行前面各参与者的逆向回滚操作,回滚已提交的参与者,使分布式事务回到初始状态。优点:
- 事务参与者可以基于事件驱动实现异步调用,吞吐高
- 一阶段直接提交事务,无锁,性能好
- 不用编写TCC中的三个阶段,实现简单
缺点:
- 软状态持续时间不确定,时效性差
- 没有锁,没有事务隔离,会有脏写
幂等处理;
比如事务失败多次尝试或者说多次调用方法得到的结果是一致的,这个只是一种说法,还有其他的地方可以用到这种思想
seata添加在nacos上的配置文件:
# 数据存储方式,db代表数据库store.mode=dbstore.db.datasource=druidstore.db.dbType=mysqlstore.db.driverClassName=com.mysql.jdbc.Driverstore.db.url=jdbc:mysql://192.168.200.130:3306/seata?useUnicode=true&rewriteBatchedStatements=truestore.db.user=rootstore.db.password=rootstore.db.minConn=5store.db.maxConn=30store.db.globalTable=global_tablestore.db.branchTable=branch_tablestore.db.queryLimit=100store.db.lockTable=lock_tablestore.db.maxWait=5000# 事务、日志等配置server.recovery.committingRetryPeriod=1000server.recovery.asynCommittingRetryPeriod=1000server.recovery.rollbackingRetryPeriod=1000server.recovery.timeoutRetryPeriod=1000server.maxCommitRetryTimeout=-1server.maxRollbackRetryTimeout=-1server.rollbackRetryTimeoutUnlockEnable=falseserver.undo.logSaveDays=7server.undo.logDeletePeriod=86400000# 客户端与服务端传输方式transport.serialization=seatatransport.compressor=none# 关闭metrics功能,提高性能metrics.enabled=falsemetrics.registryType=compactmetrics.exporterList=prometheusmetrics.exporterPrometheusPort=9898
设置seata的配置文件registry {# tc服务的注册中心类,这里选择nacos,也可以是eureka、zookeeper等type = "nacos"nacos {# seata tc 服务注册到 nacos的服务名称,可以自定义 spring.application.nameapplication = "seata-tc-server"serverAddr = "192.168.200.130:8848"group = "DEFAULT_GROUP"namespace = ""cluster = "SH"username = "nacos"password = "nacos"}}config {# 读取tc服务端的配置文件的方式,这里是从nacos配置中心读取,这样如果tc是集群,可以共享配置type = "nacos"# 配置nacos地址等信息nacos {serverAddr = "192.168.200.130:8848"namespace = ""group = "DEFAULT_GROUP"username = "nacos"password = "nacos"dataId = "seataServer.properties"}}
