摘要
1、什么是事务、分布式事务,分布式事务的场景
2、CAP理论,及BASE理论,柔性事务
3、分布式事务解决模型:2PC TCC (概念、区别)
4、2PC解决方案:XA、AT(角色、流程、实战、缺点、区别)
5、TCC解决方案:三种异常处理、Hmily实现
1、事务、分布式事务及场景
一般利用数据可事务特性,为数据库事务。数据库和应用在一个服务器,则为本地事务。
事务则需要有ACID性质。
- 原子性、一致性、隔离性(一个事务不能看到其他事务的中间状态)、持久性
而分布式事务则和分布式架构相关,当服务被拆分,并通过网络进行协作,则一个事务就涉及到多个服务以及远程调用。此时为分布式事务。
场景:
- 微服务架构中,即需要跨JVM进程。无论是多个服务访问一个实例,还是多个服务多个实例,本地事务都无法解决。
- 单个系统访问多个数据库实例,就需要进行不同的数据库连接
2、CAP
分布式事务控制目标
场景:mysql一主一从,商品写主读从。
一致性、可用性、分区容错性
consistency:写操作后的读(任意节点读)可以读到最近状态。
- 在从同步数据的过程中,将从锁住,同步完再允许查询。 需要1写响应有延迟;2资源锁定与释放;3同步失败则返回错误信息而不能返回旧数据
availability:任何事务操作都可以得到响应,且不会响应错误或超时。
- 需要能够立即响应并非错误、非超时,可以允许旧数据。需要1同步时不能锁定;2返回旧数据或者默认值
partitio tolerance:分布式各个节点在不同的子网,就形成了网络分区,彼此需要通过网络进行交互,当网络通信失败时,仍能够提供服务。
- 单个节点挂了不影响其他节点,同步时不影响读写操作。需要1添加主备从备节点、避免主或从挂了;2异步进行数据从主到从的同步
三个特性不能共存。一般选择AP,达到最终一致性即可。
- AP,通常实现AP都会保证最终一致性
- CP,zookeeper追求的就是强一致
- CA,不进行分区,本地事务隔离级别即可。
BASE理论与柔性事务
在AP中,满足1 基本可用 2 软状态 3 最终一致。即满足base,为柔性事务。
- 软状态: 中间状态– 当同步过程中来了查询,给出“支付中”状态,一致后再返回“成功”。
3、2PC
3.1 2PC概念
两阶段提交协议,prepare准备阶段和commit提交阶段。
包含 事务管理器和事务参与者(数据库实例)。管理器决定整个事务的提交和回滚,参与者负责本地事务的提交和回滚。
过程:
- prepare: 管理器向每个实例发送prepare消息,每个实例写本地的undo(修改前的数据)和redo(修改后的数据)日志。此时没有提交。
- commit: 管理器收到参与者的失败或超时,则向每个参与者发送rollback消息。否则向每个实例发送commit。每个参与者进行执行指令并释放锁。
3.2 2PC解决方案
3.2.1 XA方案
规范数据库实现2pc协议的分布式事务处理模型DTP。
定义了角色:AP RM TM,TM 和 RM 通讯接口为XA。即数据库提供的2pc接口协议,基于该协议的2pc实现为xa方案。
缺点:
1、资源锁需要事务的两个阶段结束才能释放
2、本地数据库需要支持XA协议
3.2.2 Seata方案
Seata是提供AT和TCC模式的分布式事务解决方案。
与XA区别:
- 1、XA是两阶段后释放锁,而AT模式(2pc)第一阶段则提交释放锁
- 2、AT是应用层的中间件,对业务0侵入。
实现:
- 三个角色,TC TM RM。
- TC负责,接收TM的全局事务提交或回滚指令,和RM通信协调分支事务。
- TM,开启全局事务,向TC发出提交或回滚指令。
- RM,分支注册、状态汇报、接收TC指令、驱动本地事务提交或回滚的执行。
- 具体的执行流程如下:
- 用户服务的 TM 向 TC 申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的XID。
- 用户服务的 RM 向 TC 注册 分支事务,该分支事务在用户服务执行新增用户逻辑,并将其纳入 XID 对应全局
事务的管辖。 - 用户服务执行分支事务,向用户表插入一条记录。
- 逻辑执行到远程调用积分服务时(XID 在微服务调用链路的上下文中传播)。积分服务的RM 向 TC 注册分支事
务,该分支事务执行增加积分的逻辑,并将其纳入 XID 对应全局事务的管辖。 - 积分服务执行分支事务,向积分记录表插入一条记录,执行完毕后,返回用户服务。
- 用户服务分支事务执行完毕。
- TM 向 TC 发起针对 XID 的全局提交或回滚决议。
- TC 调度 XID 下管辖的全部分支事务完成提交或回滚请求。
- 详解流程:
- 每个RM使用DataSourceProxy连接数据库,其目的是使用ConnectionProxy,使用数据源和数据连接代理的目 的就是在第一阶段将undo_log和业务数据放在一个本地事务提交,这样就保存了只要有业务操作就一定有 undo_log。
- 在第一阶段undo_log中存放了数据修改前和修改后的值,为事务回滚作好准备,所以第一阶段完成就已经将分 支事务提交,也就释放了锁资源。
- TM开启全局事务开始,将XID全局事务id放在事务上下文中,通过feign调用也将XID传入下游分支事务,每个 分支事务将自己的Branch ID分支事务ID与XID关联。
- 第二阶段全局事务提交,TC会通知各各分支参与者提交分支事务,在第一阶段就已经提交了分支事务,这里各各参与者只需要删除undo_log即可,并且可以异步执行,第二阶段很快可以完成。
- 第二阶段全局事务回滚,TC会通知各各分支参与者回滚分支事务,通过 XID 和 Branch ID 找到相应的回滚日志,通过回滚日志生成反向的 SQL 并执行,以完成分支事务回滚到之前的状态,如果回滚失败则会重试回滚操作。
实战step:
- 下载,解压并启动seata服务器 /bin/seata-server.bat -p 8888 -m file 。端口和文件方式存储信息。(TC)
- 添加discover-server子模块,discover-server基于Eureka实现。
- 添加微服务子模块,子模块pom中引入spring-cloud-alibaba-seata(包含了RM TM),并配置TC的地址:registry.conf、file.conf中:
在file.conf中更改service.vgroup_mapping.[springcloud服务名]-fescar-service-group = “default”,并修改 service.default.grouplist =[seata服务端地址]
- 创建代理数据源(配置mysql连接)
Seata的RM通过DataSourceProxy才能在业务代码的事务提交时,通过这个切
入点,与TC进行通信交互、记录undo_log等。每个RM使用DataSourceProxy连接数据库,其目的是使用ConnectionProxy,使用数据源和数据连接代理的目 的就是在第一阶段将undo_log和业务数据放在一个本地事务提交,这样就保存了只要有业务操作就一定有 undo_log。- 代码部分细节:
FeignClient
、@GlobalTransactional
、@Transactional
将@GlobalTransactional注解标注在全局事务发起的Service实现方法上,开启全局事务: GlobalTransactionalInterceptor会拦截@GlobalTransactional注解的方法,生成全局事务ID(XID),XID会在整个分布式事务中传递。 在远程调用时,spring-cloud-alibaba-seata会拦截Feign调用将XID传递到下游服务。
4、TCC
4.1 TCC相关解决方案
tcc-transaction、
Hmily、
ByteTcc、
EasyTransaction
4.2 TCC的三种异常处理
- 空回滚
没有执行try就执行了cancel - 幂等
cancel和commit会重试 - 悬挂
RPC 调用分支事务try时,先注册分支事务,再执行RPC调用,如果此时 RPC 调用的网络发生拥堵, 通常 RPC 调用是有超时时间的,RPC 超时以后,TM就会通知RM回滚该分布式事务,可能回滚完成后,RPC 请求 才到达参与者真正执行,而一个 Try 方法预留的业务资源,只有该分布式事务才能使用,该分布式事务第一阶段预 留的业务资源就再也没有人能够处理了,对于这种情况,我们就称为悬挂,即业务资源预留后没法继续处理。
转账最终方案:
1 | //A |
4.3 Hmily实现TCC事务
新增配置类接收application.yml中的Hmily配置信息,并创建HmilyTransactionBootstrap Bean
A账户
1 | //DAO 略 |
- B账户 略
4.4 TCC与2PC对比
- 2PC通常在DB层面,TCC在应用层面
- 2PC无侵入性,TCC自定义数据操作粒度,锁冲突降低,提高吞吐,但业务逻辑每个分支都需要实现TCC三个方法,且需要按照网络状态、系统故障等不同的失败原因实现不同的回滚策略。
本文链接: https://satyrswang.github.io/2021/02/04/分布式事务-上/
版权声明: 本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。转载请注明出处!