链接:https://pan.baidu.com/s/1hzqzZGpfEydfqYREGfh64w?pwd=52jv
提取码:52jv
一、分布式事务
目录
链接:https://pan.baidu.com/s/1hzqzZGpfEydfqYREGfh64w?pwd=52jv 提取码:52jv
2.2 点开conf中的registry.conf 然后进行修改配置。
3.2 修改配置文件,在三个项目的配置文件末尾加上如下,然后在运行
1. 分布式服务案例
微服务下单业务,在下单时会调用订单服务,创建订单并写入数据库。然后订单服务调用账户服务和库存服务:
- 账户服务负责扣减用户余额
- 库存服务负责扣减商品库存
运行这个项目会发现,当顾客所要商品大于库存时,库存服务报错,但是账户服务依旧扣减余额,这样是肯定不行的!
2. 分布式服务的事务问题
在分布式系统下,一个业务跨越多个服务或数据源,每个服务都是一个分支事务,要保证所有分支事务最终状态一致,这样的事务就是分布式事务。
3. 理论基础
3.1 CAP定理
1998年,加州大学的计算机科学家 Eric Brewer 提出,分布式系统有三个指标:
- Consistency(一致性)
- Availability(可用性)
- Partition tolerance (分区容错性)
Eric Brewer 说,分布式系统无法同时满足这三个指标。 这个结论就叫做 CAP 定理。
3.1.1 CAP定理- Consistency
Consistency(一致性):用户访问分布式系统中的任意节点,得到的数据必须一致
当node01更新时会及时与node02做数据同步保持一致性。
3.1.2 CAP定理- Availability
Availability (可用性):用户访问集群中的任意健康节点,必须能得到响应,而不是超时或拒绝
3.1.3 CAP定理-Partition tolerance
Partition(分区):因为网络故障或其它原因导致分布式系统中的部分节点与其它节点失去连接,形成独立分区。
Tolerance(容错):在集群出现分区时,整个系统也要持续对外提供服务
因为网络原因 node02 与node03 失去联系,则被划分了独立的分区,这样会导致 如果node02更新数据的话会告知node01及时同步 但是无法告知node03 ,如果node03更新 也一样。这样就会导致数据的不一致性。如果想保持一致性,可以node03等待与node02的联系,等待恢复正常,用户再向node03发送请求时就需要等待。但是这样的话就违背了数据的可用性。所以说三者不能同时存在。
3.1.4 总结:
简述CAP定理内容?
- 分布式系统节点通过网络连接,一定会出现分区问题(P)
- 当分区出现时,系统的一致性(C)和可用性(A)就无法同时满足
思考:elasticsearch集群是CP还是AP?
- ES集群出现分区时,故障节点会被剔除集群,数据分片会重新分配到其它节点,保证数据一致。因此是低可用性,高一致性,属于CP
3.2 BASE理论
分区不可避免,所以需要在一致性和可用性之间做出一个选择,但是这两个都很重要,如果都想要怎么办呢?——>解决:BASE理论
3.2.1 BASE理论是对CAP的一种解决思路,包含三个思想:
- Basically Available (基本可用):分布式系统在出现故障时,允许损失部分可用性,即保证核心可用。
- Soft State(软状态):在一定时间内,允许出现中间状态,比如临时的不一致状态。
- Eventually Consistent(最终一致性):虽然无法保证强一致性,但是在软状态结束后,最终达到数据一致。
而分布式事务最大的问题是各个子事务的一致性问题,因此可以借鉴CAP定理和BASE理论:
- AP模式:各子事务分别执行和提交,允许出现结果不一致,然后采用弥补措施恢复数据即可,实现最终一致(C)。
- CP模式:各个子事务执行后互相等待,同时提交,同时回滚,达成强一致。但事务等待过程中,处于弱可用状态(A)。
3.2.2 分布式事务模型
解决分布式事务,各个子系统之间必须能感知到彼此的事务状态,才能保证状态一致,因此需要一个事务协调者来协调每一个事务的参与者(子系统事务)。
这里的子系统事务,称为分支事务;有关联的各个分支事务在一起称为全局事务
加入了一个协调者,将所有的业务服务的情况都告知它。他的作用是什么呢?就是当出现数据不一致性时,比如上面 的情况,他就会实行回滚操作 ,来保证数据的一致性。
3.2.3 总结:
简述BASE理论三个思想:
- 基本可用
- 软状态
- 最终一致
解决分布式事务的思想和模型:
- 全局事务:整个分布式事务
- 分支事务:分布式事务中包含的每个子系统的事务
- 最终一致思想:各分支事务分别执行并提交,如果有不一致的情况,再想办法恢复数据
- 强一致思想:各分支事务执行完业务不要提交,等待彼此结果。而后统一提交或回滚
二、初始Seata
1. Seata的架构
1.1 Seata事务管理中有三个重要的角色:
- TC (Transaction Coordinator) - 事务协调者:维护全局和分支事务的状态,协调全局事务提交或回滚。
- TM (Transaction Manager) - 事务管理器:定义全局事务的范围、开始全局事务、提交或回滚全局事务。
- RM (Resource Manager) - 资源管理器:管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。
1.2 Seata提供了四种不同的分布式事务解决方案:
- XA模式:强一致性分阶段事务模式,牺牲了一定的可用性,无业务侵入
- TCC模式:最终一致的分阶段事务模式,有业务侵入
- AT模式:最终一致的分阶段事务模式,无业务侵入,也是Seata的默认模式
- SAGA模式:长事务模式,有业务侵入
2. 部署TC服务
2.1 解压资料中的seata-server
2.2 点开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 、apollo、zk、consul、etcd3
type = "nacos"
nacos {
serverAddr = "127.0.0.1:8848"
namespace = ""
group = "SEATA_GROUP"
username = "nacos"
password = "nacos"
dataId = "seataServer.properties"
}
}
2.3 在nacos添加配置
特别注意,为了让tc服务的集群可以共享配置,我们选择了nacos作为统一配置中心。因此服务端配置文件seataServer.properties文件需要在nacos中配好。
下面是配置内容的代码:注意 配置数据库的信息改成自己的数据库信息。
# 数据存储方式,db代表数据库
store.mode=db
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.cj.jdbc.Driver
store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&rewriteBatchedStatements=true&serverTimezone=GMT%2B8
store.db.user=root
store.db.password=root
store.db.minConn=5
store.db.maxConn=30
store.db.globalTable=global_table
store.db.branchTable=branch_table
store.db.queryLimit=100
store.db.lockTable=lock_table
store.db.maxWait=5000
# 事务、日志等配置
server.recovery.committingRetryPeriod=1000
server.recovery.asynCommittingRetryPeriod=1000
server.recovery.rollbackingRetryPeriod=1000
server.recovery.timeoutRetryPeriod=1000
server.maxCommitRetryTimeout=-1
server.maxRollbackRetryTimeout=-1
server.rollbackRetryTimeoutUnlockEnable=false
server.undo.logSaveDays=7
server.undo.logDeletePeriod=86400000
# 客户端与服务端传输方式
transport.serialization=seata
transport.compressor=none
# 关闭metrics功能,提高性能
metrics.enabled=false
metrics.registryType=compact
metrics.exporterList=prometheus
metrics.exporterPrometheusPort=9898
2.4 创建数据库表
特别注意:tc服务在管理分布式事务时,需要记录事务相关数据到数据库中,需要提前创建好这些表。
新建一个名为seata(这里的数据库名要和你上面的配置内容中的数据库名一致!!)数据库,然后导入资料中的seata-tc-server.sql文件
2.5 启动TC服务
进入bin目录,运行其中的seata-server.bat 即可
启动成功后,seata-server应该已经注册到nacos注册中心了。
打开浏览器,访问nacos地址:http://localhost:8848,然后进入服务列表页面,可以看到seata-tc-server的信息:
3. 微服务集成Seata
3.1 引入seata相关依赖
<!--seata-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<!--1.3.0版本较低,因此排除-->
<exclusions>
<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>
<version>${seata.version}</version> # 可以直接1.4.2版本
</dependency>
3.2 修改配置文件,在三个项目的配置文件末尾加上如下,然后在运行
seata:
registry: # TC 服务注册中心的配置,微服务根据这些信息去注册中心获取tc服务地址
# 参考tc服务自己的registry.conf的配置
# 包括:地址、namespace、group、applicaiton-name、cluster
type: nacos
nacos: # tc
server-addr: 127.0.0.1:8848
namespace: ""
group: DEFAULT_GROUP
application: seata-tc-server # tc服务在nacos中的服务名称
username: nacos
password: nacos
tx-service-group: seata-demo # 事务组名称,根据这个获取tc服务的cluster名称
service:
vgroup-mapping: # 事务组与cluster的映射关系
seata-demo: SH
如果你报的如下错误,记得吧JDK改成JDK1.8 (file-project structure)
3.3 总结:
nacos服务名称组成包括?
- namespace + group + serviceName + cluster
seata客户端获取tc的cluster名称方式?
- 以tx-group-service的值为key到vgroupMapping中查找
三、动手实践
1. XA模式
XA 规范 是 X/Open 组织定义的分布式事务处理(DTP,Distributed Transaction Processing)标准,XA 规范 描述了全局的TM与局部的RM之间的接口,几乎所有主流的数据库都对 XA 规范 提供了支持。
1.1. seata的XA模式
seata的XA模式做了一些调整,但大体相似:
RM一阶段的工作:
- 注册分支事务到TC
- 执行分支业务sql但不提交
- 报告执行状态到TC
TC二阶段的工作:
- TC检测各分支事务执行状态
- 如果都成功,通知所有RM提交事务
- 如果有失败,通知所有RM回滚事务
RM二阶段的工作:
- 接收TC指令,提交或回滚事务
1.2 总结
XA模式的优点是什么?
- 事务的强一致性,满足ACID原则。
- 常用数据库都支持,实现简单,并且没有代码侵入
XA模式的缺点是什么?
- 因为一阶段需要锁定数据库资源,等待二阶段结束才释放,性能较差
- 依赖关系型数据库实现事务
1.3 实现XA模式
Seata的starter已经完成了XA模式的自动装配,实现非常简单,步骤如下:
1. 修改每个微服务的application.yml文件(每个参与事务的微服务),开启XA模式:
2. 给发起全局事务的入口方法添加@GlobalTransactional注解,本例中是OrderServiceImpl中的create方法。
3. 重启服务并测试,会发现当请求中的订单数量大于库存会出现回滚操作!!
2. AT模式
2.1 AT模式原理
AT模式同样是分阶段提交的事务模型,不过缺弥补了XA模型中资源锁定周期过长的缺陷。
阶段一RM的工作:
- 注册分支事务
- 记录undo-log(数据快照)
- 执行业务sql并提交
- 报告事务状态
阶段二提交时RM的工作:
- 删除undo-log即可
阶段二回滚时RM的工作:
- 根据undo-log恢复数据到更新前
2.2 总结AT和XA模式的区别
简述AT模式与XA模式最大的区别是什么?
- XA模式一阶段不提交事务,锁定资源;AT模式一阶段直接提交,不锁定资源。
- XA模式依赖数据库机制实现回滚;AT模式利用数据快照实现数据回滚。
- XA模式强一致;AT模式最终一致
2.3 AT模式的脏写问题
如何解决以上(丢失更新)问题呢?
全局锁:由TC记录当前正在操作某行数据的事务,该事务持有全局锁,具备执行权。
2.4 AT模式的写隔离
这么一看 感觉和XA模式一样了,都是锁定了资源?
但是本质是不一样的,XA模式锁定资源中是DB数据库锁,一旦锁定别人无法对数据库进行操作。
而全局锁只是记录我们这行事务,由seata进行管理,如果说不受seata管理的别人对id=1的其他字段进行CRUD操作,都可以,只是在对当下字段进行操作时需等待。锁定的资源范围很小。所以性能比XA好。。。
但是如果说不受seata管理的事务真就相对当下的“money”这个字段进行操作,也有可能出现“脏写问题” 但是出现这种可能性很小。因为大多数人都是根据自己的余额来买东西。。。
如果真的出现这种情况该怎么处理呢?????
因为这个AT模式一共保存了两个快照 一个是修改前的,一个是修改后的,当根据快照恢复数据的时候,会和当前数据库数据进行一个比对,如果发现修改后的数据与数据库的数据库的数据不一致时,就不会提交或回滚了,因为 这样会出现“丢失更新”的问题。所以需要释放全局锁,发送异常告诉人工。。
2.5 总结AT模式的优缺点
AT模式的优点:
- 一阶段完成直接提交事务,释放数据库资源,性能比较好
- 利用全局锁实现读写隔离
- 没有代码侵入,框架自动完成回滚和提交
AT模式的缺点:
- 两阶段之间属于软状态,属于最终一致
- 框架的快照功能会影响性能,但比XA模式要好很多
2.6 实现AT模式
AT模式中的快照生成、回滚等动作都是由框架自动完成,没有任何代码侵入,因此实现非常简单。
1. 导入资料钟大哥SQL文件:seata-at,其中lock_table导入到TC服务关联的数据库,undo_log表导入到微服务关联的数据库:
2. 修改application.yml文件,将事务模式修改为AT模式即可:
3. 重启服务并测试
3. TCC模式
以上两种模式可以看出都是加了锁,但是只要加了锁就会有一定的损耗。
3.1 TCC模式原理
TCC模式与AT模式非常相似,每阶段都是独立事务,不同的是TCC通过人工编码来实现数据恢复。需要实现三个方法:
- Try:资源的检测和预留;
- Confirm:完成资源操作业务;要求 Try 成功 Confirm 一定要能成功。
- Cancel:预留资源释放,可以理解为try的反向操作。
举例,一个扣减用户余额的业务。假设账户A原来余额是100,需要余额扣减30元。
TCC的工作模型图:
案例:改造account-service服务,利用TCC实现分布式事务
需求如下:
- 修改account-service,编写try、confirm、cancel逻辑
- try业务:添加冻结金额,扣减可用金额
- confirm业务:删除冻结金额
- cancel业务:删除冻结金额,恢复可用金额
- 保证confirm、cancel接口的幂等性
- 允许空回滚
- 拒绝业务悬挂
3.2 TCC的空回滚和业务悬挂
当某分支事务的try阶段阻塞时,可能导致全局事务超时而触发二阶段的cancel操作。在未执行try操作时先执行了cancel操作,这时cancel不能做回滚,就是空回滚。
对于已经空回滚的业务,如果以后继续执行try,就永远不可能confirm或cancel,这就是业务悬挂。应当阻止执行空回滚后的try操作,避免悬挂
3.3 业务分析
为了实现空回滚、防止业务悬挂,以及幂等性要求。我们必须在数据库记录冻结金额的同时,记录当前事务id和执行状态,为此我们设计了一张表:
4. SAGA模式
Saga模式是SEATA提供的长事务解决方案。也分为两个阶段:
- 一阶段:直接提交本地事务
- 二阶段:成功则什么都不做;失败则通过编写补偿业务来回滚
Saga模式优点:
- 事务参与者可以基于事件驱动实现异步调用,吞吐高
- 一阶段直接提交事务,无锁,性能好
- 不用编写TCC中的三个阶段,实现简单
缺点:
- 软状态持续时间不确定,时效性差
- 没有锁,没有事务隔离,会有脏写