分布式事务

什么是分布式事务
在分布式系统下,一个业务跨越多个服务或数据源,每个服务都是一个分支事务,要保证所有分支事务最终状态一致,这样的事务就是分布式事务。
如何解决分布式事务
1.分析产生的原因:跨服务,跨数据源的业务导致的
2.理解理论基础:思考解决分布式事务的基本思路
3.弄清原理:了解Seata框架,学习Seata原理
4.动手实践:使用seata框架,解决分布式事务问题

Seata框架

一.理论基础

1.CAP定理

Consisency(一致性):用户访问分布式系统中的任意节点,得到的数据必须一致
Partition(分区):因为网络故障或其他原因导致分布式系统中的部分节点与其他节点失去连接,形成独立分区。
Tolerance(容错):在集群出现分区时,整个系统也要持续对外提供服务
总结:
在分布式系统中,一定会出现分区的问题(P)
当分区出现时,系统的一致性(C )和可用性(A)就无法同时满足
思考:elasticsearch集群是CP还是AP
是CP

2.BASE理论

BASE理论是对CAP的一种解决思路,包含三个思想:

1.Basically Available (基本可用):分布式系统在出现故障时,允许损失部分可用性,即保证核心可用。

2.Soft State(软状态):在一定时间内,允许出现中间状态,比如临时的不一致状态。

3.Eventually Consistent(最终一致性):虽然无法保证强一致性,但是在软状态结束后,最终达到数据一致。

而分布式事务最大的问题是各个子事务的一致性问题,因此可以借鉴CAP定理和BASE理论:

AP模式:各子事务分别执行和提交,允许出现结果不一致,然后采用弥补措施恢复数据即可,实现最终一致。

CP模式:各个子事务执行后互相等待,同时提交,同时回滚,达成强一致。但事务等待过程中,处于弱可用状态

无论是AP还是CP各个子事务之间都需要通信,需要一个协调者,帮助分布式事务中,各个子事务进行通信和 感知彼此的状态

在这里插入图片描述
总结:

简述BASE理论三个思想:
基本可用
软状态
最终一致
解决分布式事务的思想和模型:
1.全局事务:整个分布式事务
2.分支事务:分布式事务中包含的每个子系统的事务
3.最终一致思想:各分支事务分别执行并提交,如果有不一致的情况,再想办法恢复数据
4.强一致思想:各分支事务执行完业务不要提交,等待彼此结果。而后统一提交或回滚

二.初识Seata

1.Seata的架构

Seata是 2019 年 1 月份蚂蚁金服和阿里巴巴共同开源的分布式事务解决方案。致力于提供高性能和简单易用的分布式事务服务,为用户打造一站式的分布式解决方案。
官网地址:http://seata.io/,其中的文档、播客中提供了大量的使用说明、源码分析。

Seata事务管理中有三个重要的角色:
TC (Transaction Coordinator) - 事务协调者:维护全局和分支事务的状态,协调全局事务提交或回滚。
TM (Transaction Manager) - 事务管理器:定义全局事务的范围、开始全局事务、提交或回滚全局事务。
RM (Resource Manager) - 资源管理器:管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。
Seata提供了四种不同的分布式事务解决方案:
XA模式:强一致性分阶段事务模式,牺牲了一定的可用性,无业务侵入
TCC模式:最终一致的分阶段事务模式,有业务侵入
AT模式:最终一致的分阶段事务模式,无业务侵入,也是Seata的默认模式
SAGA模式:长事务模式,有业务侵入

2.部署TC服务

参考Seata的部署和集成

3.微服务集成Seata

3.1首先,引入seata相关依赖:
<!--seata-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
            <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>
        </dependency>
3.2然后,配置application.yml,让微服务通过注册中心找到seata-tc-server:
seata:
  registry:
    type: nacos
    nacos:
      server-addr: localhost:8848
      namespace: ""
      group: DEFAULT_GROUP
      application: seata-tc-server
      username: nacos
      password: nacos
  tx-service-group: seata-demo #  事务组名称
  service:  #事务组与cluster的映射关系
    vgroup-mapping:
      seata-demo: SH

三.动手实践

1.XA模式

XA模式的优点是什么?
事务的强一致性,满足ACID原则。
常用数据库都支持,实现简单,并且没有代码侵入
XA模式的缺点是什么?
因为一阶段需要锁定数据库资源,等待二阶段结束才释放,性能较差
依赖关系型数据库实现事务

在这里插入图片描述
实现XA模式:
Seata的starter已经完成了XA模式的自动装配,实现非常简单,步骤如下:
1.修改application.yml文件(每个参与事务的微服务),开启XA模式:

seata:  
	 data-source-proxy-mode: XA # 开启数据源代理的XA模式

2.给发起全局事务的入口方法添加@GlobalTransactional注解,本例中是OrderServiceImpl中的create方法:

@Override
@GlobalTransactional
public Long create(Order order) {    
// 创建订单    
orderMapper.insert(order);    
// 扣余额 ...略 
// 扣减库存 ...略    
return order.getId();
}

3.重启服务并测试

2.AT模式

AT模式同样是分阶段提交的事务模型,不过缺弥补了XA模型中资源锁定周期过长的缺陷。

阶段一RM的工作:
注册分支事务
记录undo-log(数据快照)
执行业务sql并提交
报告事务状态
阶段二提交时RM的工作:
删除undo-log即可
阶段二回滚时RM的工作:
根据undo-log恢复数据到更新前
在这里插入图片描述
简述AT模式与XA模式最大的区别是什么?
XA模式一阶段不提交事务,锁定资源;AT模式一阶段直接提交,不锁定资源。
XA模式依赖数据库机制实现回滚;AT模式利用数据快照实现数据回滚。
XA模式强一致;AT模式最终一致

AT模式的脏写问题
在事务1回滚之前事务2更新了数据
在这里插入图片描述

在这里插入图片描述

AT模式的优点:
一阶段完成直接提交事务,释放数据库资源,性能比较好
利用全局锁实现读写隔离
没有代码侵入,框架自动完成回滚和提交
AT模式的缺点:
两阶段之间属于软状态,属于最终一致
框架的快照功能会影响性能,但比XA模式要好很多

2.1实现AT模式

  1. AT模式中的快照生成、回滚等动作都是由框架自动完成,没有任何代码侵入,因此实现非常简单。

  2. 导入课前资料提供的Sql文件:seata-at.sql,其中lock_table导入到TC服务关联的数据库,undo_log表导入到微服务关联的数据库:

/*
 Navicat Premium Data Transfer

 Source Server         : local
 Source Server Type    : MySQL
 Source Server Version : 50622
 Source Host           : localhost:3306
 Source Schema         : seata_demo

 Target Server Type    : MySQL
 Target Server Version : 50622
 File Encoding         : 65001

 Date: 20/06/2021 12:39:03
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for undo_log
-- ----------------------------
DROP TABLE IF EXISTS `undo_log`;
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;

-- ----------------------------
-- Records of undo_log
-- ----------------------------



-- ----------------------------
-- Table structure for lock_table
-- ----------------------------
DROP TABLE IF EXISTS `lock_table`;
CREATE TABLE `lock_table`  (
  `row_key` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `xid` varchar(96) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `transaction_id` bigint(20) NULL DEFAULT NULL,
  `branch_id` bigint(20) NOT NULL,
  `resource_id` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `table_name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `pk` varchar(36) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `gmt_create` datetime NULL DEFAULT NULL,
  `gmt_modified` datetime NULL DEFAULT NULL,
  PRIMARY KEY (`row_key`) USING BTREE,
  INDEX `idx_branch_id`(`branch_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;


SET FOREIGN_KEY_CHECKS = 1;
  1. 修改application.yml文件,将事务模式修改为AT模式即可:
seata:  
	data-source-proxy-mode: AT # 开启数据源代理的AT模式
  1. 重启服务并测试

3.TCC模式

TCC模式与AT模式非常相似,每阶段都是独立事务,不同的是TCC通过人工编码来实现数据恢复。需要实现三个方法:
Try:资源的检测和预留;
Confirm:完成资源操作业务;要求 Try 成功 Confirm 一定要能成功。
Cancel:预留资源释放,可以理解为try的反向操作。

TCC模式原理
在这里插入图片描述

TCC模式的每个阶段是做什么的?
Try:资源检查和预留
Confirm:业务执行和提交
Cancel:预留资源的释放
TCC的优点是什么?
一阶段完成直接提交事务,释放数据库资源,性能好
相比AT模型,无需生成快照,无需使用全局锁,性能最强
不依赖数据库事务,而是依赖补偿操作,可以用于非事务型数据库
TCC的缺点是什么?
有代码侵入,需要人为编写try、Confirm和Cancel接口,太麻烦
软状态,事务是最终一致
需要考虑Confirm和Cancel的失败情况,做好幂等处理

3.2改造account-service服务,利用TCC实现分布式事务

TCC 模式 和 AT 模式可以混用

需求如下:

1.修改account-service,编写try、confirm、cancel逻辑
2.try业务:添加冻结金额,扣减可用金额
3.confirm业务:删除冻结金额
4.cancel业务:删除冻结金额,恢复可用金额
5.保证confirm、cancel接口的幂等性
6.允许空回滚
7.拒绝业务悬挂

TCC的空回滚和业务悬挂

当某分支事务的try阶段阻塞时,可能导致全局事务超时而触发二阶段的cancel操作。在未执行try操作时先执行了cancel操作,这时cancel不能做回滚,就是空回滚。

对于已经空回滚的业务,如果以后继续执行try,就永远不可能confirm或cancel,这就是业务悬挂。应当阻止执行空回滚后的try操作,避免悬挂

在这里插入图片描述

业务分析
为了实现空回滚、防止业务悬挂,以及幂等性要求。我们必须在数据库记录冻结金额的同时,记录当前事务id和执行状态,为此我们设计了一张表:
CREATE TABLE `account_freeze_tbl` (
  `xid` varchar(128) NOT NULL,
  `user_id` varchar(255) DEFAULT NULL COMMENT '用户id',
  `freeze_money` int(11) unsigned DEFAULT '0' COMMENT '冻结金额',
  `state` int(1) DEFAULT NULL COMMENT '事务状态,0:try,1:confirm,2:cancel',
  PRIMARY KEY (`xid`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;

在这里插入图片描述
声明TCC接口
TCC的Try、Confirm、Cancel方法都需要在接口中基于注解来声明,语法如下:

@LocalTCC
public interface TCCService {    
/**     
* Try逻辑,@TwoPhaseBusinessAction中的name属性要与当前方法名一致,用于指定Try逻辑对应的方法     */    
@TwoPhaseBusinessAction(name = "prepare", commitMethod = "confirm", rollbackMethod = "cancel")    
void prepare(@BusinessActionContextParameter(paramName = "param") String param);    /**     
* 二阶段confirm确认方法、可以另命名,但要保证与commitMethod一致      
*      
* @param context 上下文,可以传递try方法的参数     
* @return boolean 执行是否成功     
*/    
boolean confirm (BusinessActionContext context);    
/** 
 * 二阶段回滚方法,要保证与rollbackMethod一致     
*/    
boolean cancel (BusinessActionContext context);
}

4.SAGA模式

四.高可用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值