1. 分布式事务先容
传统单体应用场景下,系统的数据保存在一个数据库实例中,通常场景的关系数据库都能自动提供事务保证,并且这种情况下的事务称为当地事务,能保证原子性、一致性、隔离性、持久性(ACID 特性)。 当地事务
@Transational 大多数场景下,我们的应用都只需要操作单一的数据库,这种情况下的事务称之为当地事务 (Local Transaction)。当地事务的ACID特性是数据库直接提供支持。
分布式事务典型场景
当下互联网发展如火如荼,绝大部分公司都举行了数据库拆分和服务化 (SOA) 。在这种情况下,完成某一个业务功能可能需要横跨多个服务,操作多个数据库。这就涉及到到了分布式事务,用需要操作的资源位于多个资源服务器上,而应用需要保证对于多个资源服务器的数据的操作,要么全部成功,要么全部失败。本质上来说,分布式事务就是为了保证不同资源服务器的数据一致性。 跨库事务
跨库事务指的是,一个应用某个功能需要操作多个库,不同的库中存储不同的业务数据。一个服务同时操作2个库的情况:
分库分表
通常一个库数据量比较大或者预期未来的数据量比较大,都会举行程度拆分,也就是分库分表。如 下图,将数据库 B 拆分成了2个库:
对于分库分表的情况,一般开辟职员都会使用一些数据库中间件来低落 sql 操作的复杂性。如,对 于 sql : insert into user(id,name) values (1," 张三 "),(2," 李四 ") 。这条 sql 是操作单库的语法,单库情 况下,可以保证事务的一致性。 但是由于现在举行了分库分表,开辟职员盼望将 1 号记录插入分库 1 , 2 号记录插入分库 2 。所以数 据库中间件要将其改写为 2 条 sql ,分别插入两个不同的分库,此时要保证两个库要不都成功,要不都失败,因此基本上所有的数据库中间件都面临着分布式事务的问题 服务化
Service A 完成某个功能需要直接操作数据库,同时需要调用 Service B 和 Service C ,而 Service B 又同时操作了2 个数据库, Service C 也操作了一个库。需要保证这些跨服务的对多个数据库的操作要不都成功,要不都失败,实际上这可能是最典型的分布式事务场景。 常见分布式事务解决方案
1 、 seata 阿里分布式事务框架 2 、消息队列 3 、 saga 4 、 XA 他们有一个共同点,都是 “ 两阶段 (2PC) ” 。 “ 两阶段 ” 是指完成整个分布式事务,分别成两个步调完成。 实际上,这四种常见的分布式事务解决方案,分别对应着分布式事务的四种模式: AT 、 TCC 、 Saga 、 XA ; 2PC两阶段提交协议:
2PC( 两阶段提交, Two-Phase Commit) 顾名思义,分为两个阶段: Prepare 预处理阶段 和 Commit 提交阶段 Prepare :提交事务请求 基本流程如下图: 1. rollback 请求 和谐者向所有到场者发送 Rollback 请求。 2. 事务回滚 到场者收到 Rollback 后,使用 Prepare 阶段的 Undo 日志实行事务回滚,完成后释放事务实行期占用的所有资源。 3. 反馈结果 到场者实行事务回滚后向和谐者发送 Ack 相应。 4. 中断事务 吸取到所有到场者的 Ack 相应后,完成事务中断 2PC问题
1. 同步阻塞 到场者在等待和谐者的指令时,其实是在等待其他到场者的相应,在此过程中,到场者是无法举行其他操作的,也就是阻塞了其运行。 倘若到场者与和谐者之间网络异常导致到场者一直收不到和谐者信息,那么会导致到场者一直阻塞下去。 2. 单点 在 2PC 中,一切请求都来自和谐者,所以和谐者的职位是至关重要的,如果和谐者宕机,那么就会使到场者一直阻塞并一直占用事务资源。 如果和谐者也是分布式,使用选主方式提供服务,那么在一个和谐者挂掉后,可以选取另一个和谐者继续后续的服务,可以解决单点问题。但是,新和谐者无法知道上一个事务的全部状态信息( 比方已等待 Prepare 相应的时长 等 ) ,所以也无法顺遂处理上一个事务。 3. 数据不一致 Commit 事务过程中 Commit 请求 /Rollback 请求可能因为和谐者宕机或和谐者与到场者网络问题丢失,那么就导致了部分到场者没有收到 Commit/Rollback 请求,而其他到场者则正常收到实行了 Commit/Rollback 操作,没有收到请求的到场者则继续阻塞。这时,到场者之间的数据就不再一致了。 当到场者实行 Commit/Rollback 后会向和谐者发送 Ack ,然而和谐者不论是否收到所有的到场者的 Ack ,该事务也不会再有其他补救步调了,和谐者能做的也就是等待超时后像事务发起者返回一个“ 我不确定该事务是否成功” 。 4. 环境可靠性依赖 和谐者 Prepare 请求发出后,等待相应,然而如果有到场者宕机或与和谐者之间的网络中断,都会导致和谐者无法收到所有到场者的相应,那么在 2PC 中,和谐者会等待一定时间,然后超时后,会触发事务中断,在这个过程中,和谐者和所有其他到场者都是出于阻塞的。这种机制对网络问题常见的现实环境来说太苛刻了。 2. AT 模式先容
AT 模式是一种无侵入的分布式事务解决方案。阿里 Seata 框架,实现了该模式。在 AT 模式下,用户只 需关注自己的“ 业务 SQL” ,用户的 “ 业务 SQL” 作为一阶段, Seata 框架会自动天生事务的二阶段提交和回滚操作。 一阶段:
Seata 会拦截 " 业务 SQL" ,解析 SQL 语义 查询 “ 业务 SQL” 要更新的业务数据,在业务数据被更新前,将其保存成 “before image” 实行 “ 业务 SQL” ,更新业务数据 查询更新后的数据,将其保存成 “after image” 将 before image 和 after image 保存至 Undo Log 表中 天生行锁 以上操作全部在一个数据库事务内完成,如许保证了一阶段操作的原子性。 二阶段(提交):
- 因为 “业务SQL” 在一阶段已经提交至数据库,所以 Seata 框架只需将一阶段保存的快照数据和行锁删掉,完成数据清理即可。
二阶段(回滚):
- 起首要校验脏写,对比“数据库当前业务数据”和 “after image”
- 如果两份数据完全一致就说明没有脏写,可以还原业务数据。
- 如果不一致就说明有脏写,出现脏写就需要转人工处理。
- 用“before image”还原业务数据
- 删除快照数据和行锁
3. Seata是什么?
Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。 Seata 将为用户提供了 AT 、 TCC 、 SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。 AT 模式是阿里首推的模式。 Seata的三大脚色
在 Seata 的架构中,一共有三个脚色: TC (Transaction Coordinator) - 事务和谐者 :维护全局和分支事务的状态,驱动全局事务提交或回滚。 TM (Transaction Manager) - 事务管理器 :定义全局事务的范围:开始全局事务、提交或回滚全局事务。 RM (Resource Manager) - 资源管理器 :管理分支事务处理的资源,与 TC 交谈以注册分支事务和陈诉分支事务的状态,并驱动分支事务提交或回滚。 其中, TC 为单独部署的 Server 服务端, TM 和 RM 为嵌入到应用中的 Client 客户端。 在 Seata 中,一个分布式事务的生命周期如下:
1.TM 请求 TC 开启一个全局事务。 TC 会天生一个 XID 作为该全局事务的编号。 XID ,会在微服务的调用链路中流传,保证将多个微服务的子事务关联在一起。 当一进入事务方法中就会天生XID ,global_table 就是存储的全局事务信息 , 2.RM 请求 TC 将当地事务注册为全局事务的分支事务,通过全局事务的 XID 举行关联。 当运行数据库操作方法,branch_table 存储事务到场者 3.TM 请求 TC 告诉 XID 对应的全局事务是举行提交还是回滚。 4.TC 驱动 RM 们将 XID 对应的自己的当地事务举行提交还是回滚。
4. Seata快速开始
https://seata.io/zh-cn/docs/ops/deploy-guide-beginner.html 官网文档
Seata Server(TC)环境搭建
下载安装包 https://github.com/seata/seata/releases 下载 1.3.0 版本
Server 端存储模式( store.mode )支持三种:
- file:(默认)单机模式,全局事务会话信息内存中读写并持久化当地文件root.data,性能较高 (默认)
- db:(5.7+)高可用模式,全局事务会话信息通过db共享,相应性能差些
- redis: Seata-Server 1.3及以上版本支持,性能较高,存在事务信息丢失风险,请提前设置得当当前场景的redis持久化设置
使用db模式举行seata-server存储
打开 config/file.conf 修改 mode="db" 修改数据库连接信息( URL\USERNAME\PASSWORD)
创建数据库 seata_server 新建表: 可以去 seata 提供的资源信息中下载 https://github.com/seata/seata/tree/master/script/server/db
Nacos(注册&设置中心)设置
将 Seata Server 注册到 Nacos ,并设置到设置中心 打开 conf/registry.conf ,修改 conf 目录下的 registry.conf 设置: 下载整个 script 文件夹,复制到 bin 同级目录下
修改 /seata/script/config-center/config.txt 文件
打开D:\seata-server-1.3.0\seata\script\config-center\nacos 现在,修改nacos-config.sh
留意:mysql5.7url可以添加useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true
mysql8.0url都要添加
双击启动nacos-config.sh文件(必须安装git)
目的:将seata设置信息加载到Nacos设置中心(太多了,不太好创建)
启动seata-server
在 bin 目录下,双击 seata-server.bat ,默认端口 8091
搭建分布式事务微服务场景 Seata Client 创建两个数据库 seata_stock 与 seata_order 分别在 seata_stock 下创建 stock_tab 与 seata_order 下 order_tab - # 库存表
- CREATE TABLE `stock_tab` (
- `id` int(11) NOT NULL AUTO_INCREMENT, #库存ID
- `product_id` int(11) NULL DEFAULT NULL, #商品ID
- `count` int(11) NULL DEFAULT 0, #商品数量
- PRIMARY KEY (`id`) USING BTREE
- ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE =
- utf8_general_ci ROW_FORMAT = Compact;
- CREATE TABLE `undo_log` (
- `id` bigint(20) NOT NULL AUTO_INCREMENT,
- `branch_id` bigint(20) NOT NULL,
- `xid` varchar(100) NOT NULL,
- `context` varchar(128) NOT NULL,
- `rollback_info` longblob NOT NULL,
- `log_status` int(11) NOT NULL,
- `log_created` datetime NOT NULL,
- `log_modified` datetime NOT NULL,
- PRIMARY KEY (`id`),
- UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
- ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
- # 订单表
- CREATE TABLE `order_tab` (
- `id` int(11) NOT NULL AUTO_INCREMENT,
- `product_id` int(11) NULL DEFAULT 0 COMMENT '商品id',
- `total_amount` int(11) NULL DEFAULT 0 COMMENT '总金额',
- `status` int(255) NULL DEFAULT NULL COMMENT '0->待付款;1->待发货',
- PRIMARY KEY (`id`) USING BTREE
- ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE =
- utf8_general_ci ROW_FORMAT = Compact;
- CREATE TABLE `undo_log` (
- `id` bigint(20) NOT NULL AUTO_INCREMENT,
- `branch_id` bigint(20) NOT NULL,
- `xid` varchar(100) NOT NULL,
- `context` varchar(128) NOT NULL,
- `rollback_info` longblob NOT NULL,
- `log_status` int(11) NOT NULL,
- `log_created` datetime NOT NULL,
- `log_modified` datetime NOT NULL,
- PRIMARY KEY (`id`),
- UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
- ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
复制代码 业务场景: 用户下单,整个业务逻辑由三个微服务构成: 订单服务:根据采购需求创建订单。 库存服务:对给定的商品扣除库存数量 运行代码发现:使用当地事务,订单插入失败,库存却成功更新。。。。。。 AT模式 seata
在客户端项目pom中引入seata
- <dependency>
- <groupId>com.alibaba.cloud</groupId>
- <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
- <version>2.1.3.RELEASE</version>
- </dependency>
复制代码 设置客户端( order 与 stock)application.yml
tx-service-group: 事务组名称 需要与设置的 config.text 中 service.vgroupMapping.my_test_tx_group=default 保持一致 在 service 中添加 @GlobalTransactional 注解( TM 方) 运行测试。。。。。。 如果报错:请在添加订单与修改库存的业务中打印 xid 查询是否保持一致
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |