任何一种技术的出现都是为了解决某种场景的问题
在接入分布式事务之前,我们先看下传统方式下我们怎么保证数据的一致性。
以一个场景来举例说明:
比如电商网站中,用户下单付完钱之后,即创建订单。仓库接到用户订单开始进行配货处理,扣减库存
以上面的例子来看,依照传统的方式来处理
在我们的数据库中,存在有订单表、库存表。当下单支付成功之后会在订单表落一条数据(记录着订单信息以及购买的商品的数量、价格等)。购买成功之后,即把当前购买商品的可用库存扣减(避免超卖)
在传统的架构下,针对这段逻辑,为了保证事务一致性、原子性。我们通常这样书写代码,伪代码如下
//开启事务
begin transaction
//创建订单
createOrder()
//扣减库存
reducePorductQuantity()
//提交事务或事务回滚
commit/rollback
或者采用消息队列解耦的方式
//开启事务
begin transaction
//创建订单
createOrder()
//发送mq消息 异步扣减库存
sendMqMessage()
//提交事务或事务回滚
commit/rollback
我们需要知道的是,即便是被事务管理的方法,也是在方法执行完毕才提交事务/回滚事务
因此 对于mq异步解耦的方式 其实是有问题的:因为在事务提交/回滚之前 消息就已经发出了。假如创建订单失败了,其实我们不想做扣减库存的操作了,但是消息依然会发送出去,会导致最终的数据不一致现象。
对于 使用mq解耦上面这段逻辑,会在后面事务消息时专门说
随着系统架构的演进,架构演进使用了微服务,订单和库存分别演化成了两个独立的微服务,每个微服务维护着自己的数据库。
这时候,采用我们传统的做法可能不太行了:可能会出现库存扣减成功,但是订单创建失败等原因。
这种时候必须要保证数据的一致性。单数据源主要依赖单机事务来保证的,而多数据源下数据的一致性就要依赖分布式事务
分布式事务
分布式事务是指分布式架构中多个服务节点的数据的一致性。
X/OpenDTP事务模型
X/OpenDTP(X/Open Distributed Transaction Processing Reference Model)是X/Open组织定义的一套分布式事务标准,即定义了规范和API接口,由各个厂商进行具体的实现。
这个标准使用了二阶段提交2PC(Two Phase Commit)来保证事务的完整性。
X/OpenDTP 角色
在X/OpenDTP事务模型中,定义了三个角色:
- AP: application 应用程式,也就是业务层。哪些操作属于一个事务
- RM:Resource Manager,资源管理器。一般是数据库、消息队列等
- TM:Transaction Manager,事务协调者,负责接收来自用户程序发起的XA事务指令,并调度和协调参与事务的所有RM(数据库),确保事务正确完成。
对比单机单数据源服务来说,我们其实一直在使用着AP/RM,在分布式系统中为什么要引入TM(事务协调者)呢?
在分布式系统中,每个机器节点虽然都能够明确知道自己在进行事务操作过程中的结果是成功还是失败,但是无法直接获取到其他分布式节点的操作结果。因此当一个事务操作需要跨越多个分布式节点的时候,为了保证事务处理的ACID特性,就需要引入一个协调者来统一调度所有分布式节点的执行逻辑。
可能概念上听起来比较晦涩,这里以图的方式解释2PC
第一阶段:
协调者向所有的参与者发送事务内容,询问AP(应用程序)是否可以进行事务提交操作,并开始等待各参与者的响应
第二阶段
AP(应用程序)反馈事务执行情况
如果各参与者向协调者的响应均是事务执行成功,则协调者会会向参与者发送事务提交请求,随即各参与者提交事务
如果有参与者的响应是失败的,那么协调者会向各个参与各发起事务回滚操作。
尽管2PC可以解决分布式事务问题,但是同样存在些不足
-
性能问题
XA协议遵循强一致性。在事务执行过程中,只有当所有节点准备完毕,事务协调者提交后,各个节点占用的资源才会被释放。这会带来很明显的性能问题 -
单点问题
由于各节点需要事务协调者参与进行事务操作,如果事务协调者节点挂掉,参与者会收不到事务提交、回滚的通知,无法完成事务 -
网络原因导致各节点数据不一致
如果在2PC 第二阶段中,部分参与者可能因网络问题,无法收到事务协调者发起的事务提交请求,会导致部分节点进行了事务提交,而部分节点没有提交事务,进而导致了节点之间数据的不一致