Seata

二阶段提交

二阶段提交协议(Two-phase Commit,即 2PC)是常用的分布式事务解决方案,即将事务的提交过程分为两个阶段来进行处理。

两个阶段分别为:

  • 准备阶段 prepare
  • 提交阶段 commit

参与的角色:

  • 事务协调者(事务管理器):事务的发起者
  • 事务参与者(资源管理器):事务的执行者

准备阶段(投票阶段)

这是两阶段的第一段,这一阶段只是准备阶段,由事务的协调者发起询问参与者是否可以提交事务,但是这一阶段并未提交事务

  1. 协调者向所有参与者发送事务内容,询问是否可以提交事务,并等待答复;
  2. 各参与者执行事务操作,将 undo 和 redo 信息记入事务日志中(但不提交事务);
  3. 如参与者执行成功,给协调者反馈同意,否则反馈中止。

提交阶段

这一段阶段属于 2PC 的第二阶段(提交执行阶段),协调者发起正式提交事务的请求,当所有参与者都回复同意时,则意味着完成事务

  1. 协调者节点向所有参与者节点发出正式提交 (commit) 的请求;
  2. 参与者节点正式完成操作,并释放在整个事务期间内占用的资源;
  3. 参与者节点向协调者节点发送 ack 完成消息;
  4. 协调者节点收到所有参与者节点反馈的 ack 完成消息后,完成事务。

但是,如果任意一个参与者节点在第一阶段返回的消息为终止,或者协调者节点在第一阶段的询问超时之前无法获取所有参与者节点的响应消息时,那么这个事务将会被回滚。

协调者节点向所有参与者节点发出回滚操作 (rollback) 的请求;

  1. 参与者节点利用阶段 1 写入的 undo 信息执行回滚,并释放在整个事务期间内占用的资源;
  2. 参与者节点向协调者节点发送 ack 回滚完成消息;
  3. 协调者节点受到所有参与者节点反馈的 ack 回滚完成消息后,取消事务。

不管最后结果如何,第二阶段都会结束当前事务

2PC 的缺点

二阶段提交看起来确实能够提供原子性的操作,但是不幸的是,二阶段提交还是有几个缺点的:

  • 性能问题:执行过程中,所有参与节点都是事务阻塞型的。当参与者占有公共资源时,其他第三方节点访问公共资源不得不处于阻塞状态;
  • 可靠性问题:参与者发生故障。协调者需要给每个参与者额外指定超时机制,超时后整个事务失败。协调者发生故障。参与者会一直阻塞下去。需要额外的备机进行容错;
  • 数据一致性问题:二阶段无法解决的问题:协调者在发出 commit 消息之后宕机,而唯一接收到这条消息的参与者同时也宕机了。那么即使协调者通过选举协议产生了新的协调者,这条事务的状态也是不确定的,没人知道事务是否被已经提交;
  • 实现复杂:牺牲了可用性,对性能影响较大,不适合高并发高性能场景。

2PC 的优点

尽量保证了数据的强一致,适合对数据强一致要求很高的关键领域。(其实也不能 100% 保证强一致)

三阶段提交(3PC)

三阶段提交协议,是二阶段提交协议的改进版本,三阶段提交有两个改动点。

  • 在协调者和参与者中都引入超时机制;
  • 在第一阶段和第二阶段中插入一个准备阶段。保证了在最后提交阶段之前各参与节点的状态是一致的。

也就是说,除了引入超时机制之外,3PC 把 2PC 的准备阶段再次一分为二。这样三阶段提交就有 CanCommit、PreCommit、DoCommit 三个阶段。

阶段一:CanCommit 阶段

3PC 的 CanCommit 阶段其实和 2PC 的准备阶段很像。协调者向参与者发送 commit 请求,参与者如果可以提交就返回 Yes 响应,否则返回 No 响应。

事务询问:协调者向所有参与者发出包含事务内容的 canCommit 请求,询问是否可以提交事务,并等待所有参与者答复;

  • 响应反馈:参与者收到 canCommit 请求后,如果认为可以执行事务操作,则反馈 yes 并进入预备状态,否则反馈 no。

假如有任何一个参与者向协调者发送了 No 响应,或者等待超时之后,协调者都没有接到参与者的响应,那么就执行事务的中断。

  1. 发送中断请求 :协调者向所有参与者发送 abort 请求;

中断事务 :参与者收到来自协调者的 abort 请求之后(或超时之后,仍未收到协调者的请求),执行事务的中断

阶段二:PreCommit 阶段

协调者根据参与者的反应情况来决定是否可以进行事务的 PreCommit 操作。根据响应情况,有以下两种可能。

假如所有参与者均反馈 yes,协调者预执行事务。
 

  1. 发送预提交请求 :协调者向参与者发送 PreCommit 请求,并进入准备阶段;
  2. 事务预提交 :参与者接收到 PreCommit 请求后,会执行事务操作,并将 undo 和 redo 信息记录到事务日志中(但不提交事务);

响应反馈 :如果参与者成功的执行了事务操作,则返回 ACK 响应,同时开始等待最终指令。

阶段三:doCommit 阶段

该阶段进行真正的事务提交,也可以分为以下两种情况。

进入阶段 3 后,无论协调者出现问题,或者协调者与参与者网络出现问题,都会导致参与者无法接收到协调者发出的 do Commit 请求或 abort 请求。此时,参与者都会在等待超时之后,继续执行事务提交。

执行提交

  1. 发送提交请求:协调接收到参与者发送的 ACK 响应,那么他将从预提交状态进入到提交状态。并向所有参与者发送 doCommit 请求;
  2. 事务提交:参与者接收到 doCommit 请求之后,执行正式的事务提交。并在完成事务提交之后释放所有事务资源;
  3. 响应反馈:事务提交完之后,向协调者发送 ack 响应;
  4. 完成事务:协调者接收到所有参与者的 ack 响应之后,完成事务。

中断事务:任何一个参与者反馈 no,或者等待超时后协调者尚无法收到所有参与者的反馈,即中断事务:

发送中断请求:如果协调者处于工作状态,向所有参与者发出 abort 请求;

  1. 事务回滚:参与者接收到 abort 请求之后,利用其在阶段二记录的 undo 信息来执行事务的回滚操作,并在完成回滚之后释放所有的事务资源;
  2. 反馈结果:参与者完成事务回滚之后,向协调者反馈 ACK 消息;
  3. 中断事务:协调者接收到参与者反馈的 ACK 消息之后,执行事务的中断。

优点

相比二阶段提交,三阶段提交降低了阻塞范围,在等待超时后协调者或参与者会中断事务。避免了协调者单点问题,阶段 3 中协调者出现问题时,参与者会继续提交事务。

缺点

数据不一致问题依然存在,当在参与者收到 preCommit 请求后等待 doCommit 指令时,此时如果协调者请求中断事务,而协调者无法与参与者正常通信,会导致参与者继续提交事务,造成数据不一致

TC数据库连接信息

## 采用db的存储形式store.mode=db## druid数据源store.db.datasource=druid## mysql数据库store.db.dbType=mysql## mysql驱动store.db.driverClassName=com.mysql.jdbc.Driver## TC的数据库urlstore.db.url=jdbc:mysql://127.0.0.1:3306/seata_server?useUnicode=true## 用户名store.db.user=root## 密码store.db.password=Nov2014

CREATE TABLE `storage`  (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`num` bigint(11) NULL DEFAULT NULL COMMENT '数量',
`create_time` datetime(0) NULL DEFAULT NULL,
`price` bigint(10) NULL DEFAULT NULL COMMENT '单价,单位分',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Compact;
INSERT INTO `storage` VALUES (1, '分布式事务', 1000, '2021-12-25 22:32:40', 100);

CREATE TABLE `undo_log`  (
`branch_id` bigint(20) NOT NULL COMMENT 'branch transaction id',
`xid` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'global transaction id',
`context` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci 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(6) NOT NULL COMMENT 'create datetime',
`log_modified` datetime(6) NOT NULL COMMENT 'modify datetime',
UNIQUE INDEX `ux_undo_log`(`xid`, `branch_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = 'AT transaction mode undo table' ROW_FORMAT = Compact;

spring:application:    ## 指定服务名称,在nacos中的名字name: seata-storage## 客户端seata的相关配置seata:  ## 是否开启seata,默认trueenabled: trueapplication-id: ${spring.application.name}  ## seata事务组的名称,一定要和config.tx(nacos)中配置的相同tx-service-group: ${spring.application.name}-tx-group  ## 配置中心的配置config:    ## 使用类型nacostype: nacos    ## nacos作为配置中心的相关配置,需要和server在同一个注册中心下nacos:      ## 命名空间,需要server端(registry和config)、nacos配置client端(registry和config)保持一致namespace: 7a7581ef-433d-46f3-93f9-5fdc18239c65      ## 地址server-addr: localhost:8848      ## 组, 需要server端(registry和config)、nacos配置client端(registry和config)保持一致group: SEATA_GROUP      ## 用户名和密码username: nacospassword: nacosregistry:type: nacosnacos:      ## 这里的名字一定要和seata服务端中的名称相同,默认是seata-serverapplication: seata-server      ## 需要server端(registry和config)、nacos配置client端(registry和config)保持一致group: SEATA_GROUPnamespace: 7a7581ef-433d-46f3-93f9-5fdc18239c65username: nacospassword: nacosserver-addr: localhost:8848

Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。

  • 对业务无侵入:即减少技术架构上的微服务化所带来的分布式事务问题对业务的侵入;
  • 高性能:减少分布式事务解决方案所带来的性能消耗。

官方文档:https://seata.io/zh-cn/index.html

Seata 的几种术语:

  • TC(Transaction Coordinator):事务协调者。管理全局的分支事务的状态,用于全局性事务的提交和回滚;
  • TM(Transaction Manager):事务管理者。用于开启、提交或回滚事务;
  • RM(Resource Manager):资源管理器。用于分支事务上的资源管理,向 TC 注册分支事务,上报分支事务的状态,接收 TC 的命令来提交或者回滚分支事务。

AT 模式

Seata 目前支持多种事务模式,分别有 AT、TCC、SAGA 和 XA 。文章篇幅有限,今天只讲常用的 AT 模式。

AT 模式的特点就是对业务无入侵式,整体机制分二阶段提交(2PC)

  • 一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。
  • 二阶段:提交异步化,非常快速地完成;回滚通过一阶段的回滚日志进行反向补偿。

在 AT 模式下,用户只需关注自己的业务 SQL。用户的业务 SQL  作为一阶段,Seata 框架会自动生成事务的二阶段提交和回滚操作。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Seata的高可用模式是通过TC使用db模式共享全局事务会话信息,使用非file的seata支持的第三方注册中心和配置中心来共享全局配置的方式来实现的。 Seata支持的第三方注册中心有nacos 、eureka、redis、zk、consul、etcd3、sofa、custom,支持的第三方配置中心有nacos 、apollo、zk、consul、etcd3、custom。seata官方主推的应该是nacos(毕竟是一家的),但是由于本人平常使用的注册中心一直是zk,因此首先考虑通过zk作为注册中心来实现高可用模式。 环境准备 zk环境准备 本地已安装zk的可以忽略,如果本地未安装,先在本地安装zk,具体安装自行百度。 PS: 此处如果使用的是远程zk服务器,则本地可以只下载,不安装。 数据库环境准备 1、创建数据库seata 2、执行源码(version1.2.0)script -> server -> db 下面的mysql.sql文件,建立global_table,branch_table,lock_table表。 配置信息导入zk 1、打开本地seata源码(版本1.2.0) 2、编辑script -> config-center -> config.txt文件,修改store.mode=db,修改store.db相关的数据库连接信息,其它默认即可 3、进入script -> config-center ->zk,执行 sh zk-config.sh -h 127.0.0.1 -p 2181 -z /usr/local/zookeeper-3.4.14(-z 后面的参数为你本地zk目录) 上面命令会将config.txt中的配置信息写入zk的/seata节点下。 启动tc-server 1、编辑conf下面的registry.conf文件,修改registry.type=zk,修改config.type=zk,修改registry.zk及config.zk信息,如下: 注意:config的zk配置没有cluster属性。 2、启动server,在本地seata安装目录bin目录下执行./seata-server.sh -m db (此处也可以直接编译本地源码然后启动Server模块下的Server类)。 不出意外的话,启动会报错,错误信息是从zk读取配置的时候反序列化出问题。 错误原因:序列化问题,由于使用seata自带的zk-config.sh脚本向zk写入配置信息的时候,采用的序列化方式相当于String.getBytes(),而框架读取配置的时候使用的是org.101tec包中的Zkclient客户端,反序列化使用的是该包下面的SerializableSerializer序列化类,使用的ObjectOutputStream进行反序列化,和序列化方式不一致。 该问题在1.3.0版本中解决,解决方式是seata支持序列化方式配置,支持自定义序列化方式,同时提供默认序列化实现类DefaultZkSerializer,反序列化实现为new String()。 到此处,1.2.0版本无法进行下去,由于目前1.3.0正式版本还未出,只能拉取最新的开发分支源码,本地编译打包1.3.0-SNAPSHOT版本。 后续版本切为1.3.0-SNAPSHOT(20200701),删除原zk配置信息重新导入1.3版本的config.txt文件信息。 本地源码编译后直接Idea启动Server类。启动成功。 PS:启动日志里面会有一些getConfig失败的报错,这些不用管,这些新的配置参数是1.3版本新增的,由于当前是SNAPSHOT版本,还不完善。 PS: 如果遇到getServerCharset 空指针异常,这个主要是MySQLMySQL驱动版本不一致引起的,看https://blog.csdn.net/zcs20082015/article/details/107087589 服务启动 配置修改 简单处理,这里不再建新的模块,直接使用zhengcs-seata-storage模块作为演示。 1、修改POM,引入zkclient,修改seata版本 2、修改application.yml,将注册和配置类型改为zk 另外需要注意的是seata.tx-service-group配置参数要和zk导入的配置信息相关参数保持一致,否则会找不到server集群 启动服务 1、引入全局事务 2、启动 测试 基本功能测试 单元测试用例: 手动插入异常 执行用例: 基本功能是没问题的,更详细全面的测试这里就不介绍了,大家自行尝试。 高可用测试 上面的单机版肯定无法满足高可用,tc-server一旦宕掉,整个全局事务会无法进行回滚,同时会在seata库下面的事务表里面留下事务记录(正常处理成功后会被删除)。 seata的高可用是通过多个tc-server实例组成的集群来实现的。 启动多个tc-server实例: 通过-p参数修改启动接口,同时勾选Allow parallel run,开启多个实例。 然后启动客服端服务: 从启动日志可以看出,客户端会同时向所有几点注册TM和RM。 执行测试用例: 那,如果在数据已提交,异常退出之前把对应的tc-server节点停掉,会怎么样呢?答案是:全局事务回滚。大家自行尝试一下。 还有一种情况,如果客户端在执行过程中中断了,会怎么样? 如果客户端是单节点部署,那么: 首先,seata库下面的事务处理表里面有遗留事务处理记录,然后你会发现tc-server端日志里面会持续刷上述日志,tc-server会持续的尝试回滚该事务。最遗憾的是:哪怕客户端服务重启,也不会回滚该事务!!! 不过还好的是,这种情况毕竟是特例,如果客户端服务是集群部署,那么seata是可以正常完成事务回滚的。 结语 从上面的情况来看,起码seata对于简单的分布式事务场景的高可用支撑是没问题的,但是seata毕竟还是一个新框架,在实际的复杂的业务场景下会否出现什么问题,其实应该说出现什么问题都是可能的,这个需要实践和时间才能出真知了。 另外,seata目前没有提供控制台,没有服务集群配套的HA机制,这个不知道什么时候会出,拭目以待,seata应该会是一个很不错的分布式事务解决方案。   参考资料 https://seata.io/zh-cn/docs/ https://github.com/seata/seata ———————————————— 版权声明:本文为CSDN博主「zhengcs已被占用」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/zcs20082015/article/details/107092936

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值