Seata-特性


  • Seata-组件
  • 2PC
  • Seata-AT
  • Seata-XA
  • Seata-TCC
  • Seata-SAGA:业务流(最终一致性,不保证事务的隔离)
  • Seata-集成

Seata-组件

  • 核心组件:TC 为单独部署的 Server 服务端,TM 和 RM 为嵌入到应用中的 Client 客户端
    • TC (Transaction Coordinator) - 事务协调者:维护全局和分支事务的状态,驱动全局事务提交或回滚
    • TM (Transaction Manager) - 事务管理器:定义全局事务的范围,开始全局事务、提交或回滚全局事务
    • RM (Resource Manager) - 资源管理器 :管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚
  • 分布式事务的生命周期
    • TM 请求 TC 开启一个全局事务。TC 会生成一个 XID 作为该全局事务的编号。XID会在微服务的调用链路中传播,保证将多个微服务的子事务关联在一起
    • RM 请求 TC 将本地事务注册为全局事务的分支事务,通过全局事务的 XID 进行关联
    • TM 请求 TC 告诉 XID 对应的全局事务是进行提交还是回滚
    • TC 驱动 RM 们将 XID 对应的自己的本地事务进行提交还是回滚
  • 使用 @GlobalTransactional 开启分布式事务(XA模式需要同时使用@Transcational
  • 使用 @GlobalLock + select ... for update 实现全局事务的读隔离

2PC

  • 2PC 两阶段提交
    1. TM通知各个RM准备提交它们的事务分支。如果RM判断自己进行的工作可以被提交,那就对工作内容进行持久化,再给TM肯定答复;要是发生了其他情况,那给TM的都是否定答复
    2. TM根据阶段1各个RM prepare的结果,决定是提交还是回滚事务。如果所有的RM都prepare成功,那么TM通知所有的RM进行提交;如果有RM prepare失败的话,则TM通知所有RM回滚自己的事务分支
  • ACID:两阶段提交方案下全局事务的ACID特性,是依赖于RM的。一个全局事务内部包含了多个独立的事务分支,这一组事务分支要么都成功,要么都失败。各个事务分支的ACID特性共同构成了全局事务的ACID特性。也就是将单个事务分支支持的ACID特性提升一个层次到分布式事务的范畴
  • 2PC存在的问题
    • 同步阻塞问题:2PC 中的参与者是阻塞的。在第一阶段收到请求后就会预先锁定资源,一直到 commit 后才会释放
    • 单点故障:一旦协调者TM发生故障,参与者RM会一直阻塞下去。尤其在第二阶段,协调者发生故障,那么所有的参与者还都处于锁定事务资源的状态中,而无法继续完成事务操作
    • 数据不一致:若协调者第二阶段发送提交请求时崩溃,可能部分参与者收到commit请求提交了事务,而另一部分参与者未收到commit请求而放弃事务,从而造成数据不一致的问题


Seata-AT

  • AT(强一致性,适合并发量不高的场景):业务无侵入,是一种改进后的两阶段提交(一阶段提交释放资源,二阶段日志回滚补偿 - undo_log 表)
    1. 业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源(不会一直持有资源的锁)
      • 对业务sql进行解析,转换成undolog,并同时入库
    2. 提交异步化,非常快速地完成;回滚通过一阶段的回滚日志进行反向补偿
      • 分布式事务操作成功,则TC通知RM异步删除undolog
      • 分布式事务操作失败,TM向TC发送回滚请求,RM 收到协调器TC发来的回滚请求,通过 XID 和 Branch ID 找到相应的回滚日志记录,通过回滚记录生成反向的更新 SQL 并执行,以完成分支的回滚
        • 问题:如果事务进行期间,其他线程修改了这行数据,导致undo log中前置镜像的值和库表实际值不一致,就会导致回滚失败,其他事务也会被拒绝,必须人工介入修改库表数据后删除undo log
  • TM 位于事务最顶层:下游服务如果处理了异常导致上有TM所在的服务无法捕获异常,就会导致事务失效的情况
    • Feign的服务降级之后一定要重新抛出异常

Seata-XA

  • XA(强一致性,适用于中间件场景):一直持有资源的锁,不需要undo_log表
    • 配置方式:seata.data‐source‐proxy‐mode=XA
    • 从编程模型上,XA 模式与 AT 模式基本上完全一致,只是由于依赖本地事务(代理数据源-XADataSource),@GlobalTransactional必须配合@Transactional使用,否则无效
  • AT和XA模式数据源代理机制对比

Seata-TCC

  • TCC(Try-Confirm-Cance):最终一致性,适合高并发事务场景,比如金融领域,不会一直持有资源的锁
    • 侵入式的分布式事务解决方案(需要手动实现try, commit, cancel):完全不依赖数据库,能够实现跨数据库、跨应用资源管理
    • SEATA的TCC模式就是手工的AT模式,它允许你自定义两阶段的处理逻辑而不依赖AT模式的undo_log
  • 资源预留(Try)、确认操作(Confirm)、取消操作(Cancel)
    • Try:对业务资源的检查并预留
    • Confirm:对业务处理进行提交,即 commit 操作,只要 Try 成功,那么该步骤一定成功
    • Cancel:对业务处理进行取消,即回滚操作,该步骤回对 Try 预留的资源进行释放
  • 一阶段 prepare 行为;二阶段 commit 或 rollback 行为
    • try-commit:try 阶段首先进行预留资源,然后在 commit 阶段扣除资源
    • try-cancel:try 阶段首先进行预留资源,预留资源时扣减库存失败导致全局事务回滚,在 cancel 阶段释放资源
  • 空回滚、幂等、悬挂:Seata都已经封装好了,基于日志表 tcc_fence_log(对tcc的执行顺序进行日志记录,就可以识别这三个问题)
    • 空回滚:try 因为网络问题没有调用成功,按理说不需要 cancel,但是 cancel 了
      • 造成数据不一致:在try中扣减,在cancel增加,try没有执行,而cancel执行了
    • 幂等: TC 重复进行二阶段提交,Confirm/Cancel 接口需要支持幂等处理,即不会产生资源重复提交或者重复释放
      • 由于网络问题,事务提交后没有响应到服务端,导致服务端发动重试机制,产生重复提交,重复释放同理
    • 悬挂:try 方法被网络阻塞,导致空回滚,但是空回滚后 try 方法被成功执行了
  • Seata解决方案:在tcc_fence_log表中记录状态status tried:1 committed:2 rollbacked:3 suspended:4
    • try 执行后是 tried
      • 解决空回滚问题:当执行 cancel 时,检测到当前没有记录,就进行空回滚,并置为 suspended
        • 解决悬挂问题:try 执行时检测到现在是 suspended 状态,就放弃执行
    • confirm 执行后是 committed;cancel 执行后是 rollbacked
      • 解决幂等问题:当重复进行二阶段调用时,当前状态已经是二阶段状态了
    • 通过检查目前事务的状态,可以轻松避免空回滚、幂等、悬挂的问题
// @LocalTCC 适用于SpringCloud+Feign模式下的TCC,@LocalTCC一定需要注解在接口上
@LocalTCC
public interface OrderService {
	/* 
	 * 定义了分支事务的 resourceId,commit和 cancel 方法
	 * name = 该tcc的bean名称,全局唯一
	 * commitMethod = commit 为二阶段确认方法
	 * rollbackMethod = rollback 为二阶段取消方法
	 * BusinessActionContextParameter注解,传递参数到二阶段中
	 * useTCCFence:用于解决TCC幂等,悬挂,空回滚问题,需增加日志表tcc_fence_log
	 */
	@TwoPhaseBusinessAction(name = "prepareSaveOrder", commitMethod = "commit", rollbackMethod = "rollback", useTCCFence = true)
	boolean deduct(@BusinessActionContextParameter(paramName = "commodityCode") String commodityCode,  @BusinessActionContextParameter(paramName = "count") int count);

	boolean commit(BusinessActionContext actionContext);
	boolean rollback(BusinessActionContext actionContext);
}

Seata-集成

  • db存储模式+Nacos(注册&配置中心)方式部署
  • 将Seata Server注册到Nacos,修改conf/application.yml文件
    • 确保client与server的注册处于同一个namespace和group,不然会找不到服务
registry:
	# support: nacos, eureka, redis, zk, consul, etcd3, sofa
	type: nacos
	nacos:
		application: seata‐server
		server‐addr: 127.0.0.1:8848
		group: SEATA_GROUP
		namespace: 7e838c12‐8554‐4231‐82d5‐6d93573ddf32
		cluster: default
		username:
		password:
  • 配置Nacos配置中心地址,修改conf/application.yml文件
seata:
	config:
		# support: nacos, consul, apollo, zk, etcd3
		type: nacos
		nacos:
			server‐addr: 127.0.0.1:8848
			namespace: 7e838c12‐8554‐4231‐82d5‐6d93573ddf32
			group: SEATA_GROUP
			data‐id: seataServer.properties
			username:
			password:
  • 上传 seataServer.properties 到 Nacos (设定db模式)
store.mode=db
store.db.driverClassName=com.mysql.jdbc.Driver
store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&rewriteBatchedStatements=true
store.db.user=root
store.db.password=root
  • 配置事务分组
    • seataServer.properties 里的 service.vgroupMapping.default_tx_group=default 与 Seata Server 注册到 Nacos 的 registry.nacos.cluster=default 必须保持一致
    • 要与client配置的事务分组一致 seata.tx-service-group=default_tx_group(和service.vgroupMapping.的后缀对应)
  • 启动Seata Server
    • seata‐server.sh ‐p 8091 ‐h 127.0.0.1 ‐m db
      • -m 事务日志存储方式,支持file,db,redis,默认file
      • -n 指定seata-server节点ID

  • 事务分组如何找到后端Seata集群(TC)
    • 客户端中配置了事务分组,SpringBoot 通过 seata.tx-service-group 配置
    • 客户端会通过用户配置的配置中心去寻找 service.vgroupMapping .[事务分组配置项],取得配置项的值就是TC集群的名称,SpringBoot 通过 seata.service.vgroup-mapping.事务分组名=集群名称 配置
    • 拿到集群名称程序通过一定的前后缀+集群名称去构造服务名
    • 拿到服务名去相应的注册中心去拉取相应服务名的服务列表,获得后端真实的TC服务列表

文章作者: 钱不寒
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 钱不寒 !
  目录