事务:是应用程序中一系列严密的操作,所有操作必须成功完成,要么全部失败,ACID 特性。
本地事务:关系型数据库中,由一组SQL组成的一个执行单元,该单元要么整体成功,要么整体失败;它有一个缺点:仅支持单库事务,并不支持跨库事务。
分布式事务:指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。一个应用某个功能需要操作多个库,不同的库中存储不同的业务数据。是指一个业务需要同时操作多个数据库的情况下,而且必须保持ACID的特性。一般应用于微服务的多服务处理。
一个网站的访问量越来越大,按照商品、订单、用户、店铺等业务为单位进行数据库拆分,以及按照业务为单位提供服务接口。为了完成一个简单的业务功能,比如:购买商品后扣款,有可能需要横跨多个服务,涉及用户订单、商品库存、支付等多个数据库,而这些操作又需要在同一个事务中完,这就涉及到到了分布式事务。
分布式事务就是为了保证不同资源服务器的数据一致性。
CAP理论是在设计分布式系统的过程中,处理数据一致性问题时必须考虑的理论。CAP 是 Consistency、Availability、Partition tolerance 三个单词的缩写,分别表示一致性、可用性、分区容忍性。
Consistency(一致性):数据一致更新,所有数据变动都是同步的,更新操作成功并返回客户端完成后,所有节点在同一时间的数据完全一致,不能存在中间状态。
例如对于电商系统用户下单操作,库存减少、用户资金账户扣减、积分增加等操作必须在用户下单操作完成后必须是一致的。不能出现类似于库存已经减少,而用户资金账户尚未扣减,积分也未增加的情况。如果出现了这种情况,那么就认为是不一致的。
如果能时刻保证客户端看到的数据都是一致的,那么称之为强一致性。
如果允许存在中间状态,只要求经过一段时间后,数据最终是一致的,则称之为最终一致性。
此外,如果允许存在部分数据不一致,那么就称之为弱一致性。
Availability(可用性):好的响应性能,每个操作都必须以可预期的响应结束
Partition tolerance(分区容错性) :可靠性,即使出现单个组件无法可用,操作依然可以完成
定理:任何分布式系统只可同时满足二点,没法三者兼顾。
AP,实现 AP 都会保证最终一致性,这是很多分布式系统设计时的选择。一些业务场景比如:订单退款,今日退款成功,明日账户到账,只要用户可以接受在一定的时间内到账即可。BASE 理论就是根据 AP 来扩展的。
CP,追求强一致性,又比如跨行转账,一次转账请求要等待双方银行系统都完成整个事务才算完成。
CA,最常用的关系型数据就满足了 CA。
CAP 中的一致性要求 在任何时间查询每个结点数据都必须一致,它强调的是强一致性,但是最终一致性是允许可以在一段时间内每个结点的数据不一致,但是经过一段时间每个结点的数据必须一致,它强调的是最终数据的一致性。
BASE 是 Basically Available(基本可用)、Soft state(软状态)和 Eventually consistent (最终一致性)三个短语的缩写, 是对CAP中AP的一个扩展。
BASE 理论是对 CAP 中 AP 的一个扩展,通过牺牲强一致性来获得可用性,当出现故障允许部分不可用但要保证核心功能可用,允许数据在一段时间内是不一致的,但最终达到一致状态。满足BASE理论的事务,我们称之为“柔性事务”。
基本可用:分布式系统在出现故障时,允许损失部分可用功能,保证核心功能可用。如电商网站交易付款出现问题了,商品依然可以正常浏览。
软状态:由于不要求强一致性,所以BASE允许系统中存在中间状态(也叫软状态),这个状态不影响系统可用性,如订单的"支付中"、“数据同步中”等状态,待数据最终一致后状态改为“成功”状态。
最终一致:最终一致是指经过一段时间后,所有节点数据都将会达到一致。如订单的"支付中"状态,最终会变 为“支付成功”或者"支付失败",使订单状态与实际交易结果达成一致,但需要一定时间的延迟、等待。
刚性事务满足CAP的CP理论
柔性事务满足BASE理论(基本可用,最终一致),AP的扩展
分布式事务的实现主要有以下 6 种方案:
2PC 方案
TCC 方案,事务补偿
本地消息表
MQ事务
Saga事务
最大努力通知方案
Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式
Seata分为三大模块,分别是 TM、RM 和 TC
如果一阶段提交成功,这时候就表明了所有节点本地事务提交都完成了,Seata是不会在回滚事务的,所谓的二阶段提交,只不过是异步的删除各节点本地的undo log,如果一阶段发生异常,表明其中某个节点本地事务提交失败或者发起节点没有达到预期处理要回滚自己的事务,这时候所有的节点可能有的已经提交本地事务成功,有的还没有,这时候的二阶段回滚是通过undo log在本地又执行了一遍事务操作,将数据还原,这里的回滚并不是借助数据库实现,而是借助Seata实现的。
TC (Transaction Coordinator) - 事务协调者:
seata-server, 维护全局和分支事务的状态,驱动全局事务提交或回滚。
TM (Transaction Manager) - 事务管理器:
定义全局事务的范围:开始全局事务、提交或回滚全局事务。
RM (Resource Manager) - 资源管理器:
一台MySQL服务器就是一个RM, 管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。
AT,Auto Transaction,Seata 框架会自动生成事务的二阶段提交和回滚操作。AT 模式是无侵入的分布式事务解决方案,适用于不希望对业务进行改造的场景,需要添加undo_log表
性能:高,基于支持本地 ACID 事务的关系型数据库
模式:AP,存在数据不一致的中间状态
难易程度:简单,自动生成事务的二阶段提交和回滚操作,seata解析反向sql进行回滚
使用要求:所有服务和数据库必须有自己的管理权,因为要创建undo_log表
应用场景:高并发互联网应用,允许数据出现短时的不一致,可通过对账程序或者补录来保证最终一致性
TCC是Try-尝试、Confirm-确认、Cancel-取消Try尝试阶段,对资源进行锁定。
Confirm确认阶段,对资源进行确认,完成操作Cancel取消阶段,对资源进行还原,取消操作。
在代码与数据表中扩展字段,实现对数据资源的锁定。
TCC方案其实是两阶段提交的一种改进。分成了Try、Confirm、Cancel三个操作;就是手工的AT模式,提交和回滚需要手动,它允许你自定义两阶段的处理逻辑而不依赖AT模式的undo_log,不依赖于底层数据资源的事务支持,可能需要在表中添加格外的字段,预制操作,严重依赖回滚和补偿代码;手动编写接口,很少使用
性能:好
模式:AP,存在数据不一致的中间状态
难易程度:复杂,seata只负责全局事务的提交和回滚指令,具体的操作处理需要编码实现
使用要求:服务和数据库必须有自己的管理权,因为可能要修改表
应用场景:高并发互联网应用,允许数据出现短时不一致,TCC 模式是高性能分布式事务解决方案,适用于核心系统等对性能有很高要求的场景,比如说跟钱打交道的,支付、交易相关的场景,大家会用 TCC方案
Saga模式是SEATA提供的长事务解决方案,在Saga模式中,业务流程中每个参与者都提交本地事务,当出现某一个参与者失败则补偿前面已经成功的参与者,一阶段正向服务和二阶段补偿服务都由业务开发实现。
性能:不一定,取决于第三方服务
模式:AP,存在数据不一致的中间状态
难易程度:复杂,提交和回滚需要编排,SEATA提供的Saga模式是基于状态机引擎来实现的
使用要求:需要引入状态机机制,类似于工作流,但无法保证隔离性
应用场景:
业务流程长、业务流程多,参与者包含其它公司或遗留系统服务,无法提供 TCC 模式要求的三个接口
与第三方交互时,考虑使用,例如调用支付宝的支付接口-->出现异常--->调用支付宝的退款接口
在 Seata 定义的分布式事务框架内,利用事务资源(数据库、消息服务等)对 XA 协议的支持,以 XA 协议的机制来管理分支事务的一种 事务模式。
基于数据库的XA协议来实现2PC又称为XA方案。
分为准备和提交两个阶段
第一阶段,事务协调者向事务参与者发送 prepare 请求,事务参与者收到请求后,如果可以提交事务,回复 yes,否则回复 no。
第二阶段,如果所有事务参与者都回复了 yes,事务协调者向所有事务参与者发送 commit 请求,否则发送 rollback 请求。
两阶段,执行语句,不会真正提交,依赖于数据库自带特性实现;XA模式是分布式强一致性的解决方案,但性能低而使用较少。XA协议包括两阶段提交(2PC)和三阶段提交(3PC)两种实现
性能:性能低
模式:CP,保证强一致性
难易程度:简单,基于数据库的自带特性实现,无需修改表
使用要求:支持XA方案的关系型数据库(主流都支持)
应用场景:金融行业,并发量不大,但数据很重要的场景
目前使用的流行度情况是:AT
> TCC
> Saga
Seata分布式事务AT、TCC、SAGA、XA模式选型主要是根据不同的业务进行相应的选型,如SAGA,需要调用第三方的支付的场景的时候,可以使用这个方案,SAGA是形成一定的事件流,反向的回滚的时候按照反向的事件流进行回滚。
# 库存表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;