分布式事务概述

概述

传统的单体应用一般采用的是数据库提供的事务一致性,通过数据库提供的提交以及回滚机制来保证相关操作的ACID,这些操作要么同时成功,要么同时失败。各个服务看到数据库中的数据是一致的,同时数据库的操作也是相互隔离的,最后数据也是在数据库中持久存储的。
在分布式环境下由于各个服务访问的数据是相互分离的, 服务之间不能靠数据库来保证事务一致性。 这就需要在应用层面提供一个协调机制,来保证一组事务执行要么成功,要么失败

CAP定理

分布式系统有三个指标。一致性、可用性、分区容错性,这三个指标不可能同时满足,这个结论就叫CAP定理。
为了方便对CAP理论的理解,我们结合电商系统中的一些业务场景来理解CAP。

整体执行流程如下:
1、商品服务请求主数据库写入商品信息(添加商品、修改商品、删除商品)
2、主数据库向商品服务响应写入成功。
3、商品服务请求从数据库读取商品信息。

分区容错性(Partition tolerance)

大多数分布式系统都分布在多个子网络。每个子网络就叫做一个区(partition)。分区容错的意思是,区间通信可能失败。
一般来说,分区容错无法避免,因此可以认为 CAP 的 P 总是成立。CAP 定理告诉我们,剩下的 C 和 A 无法同时做到。

一致性(Consistency)

一致性是指写操作后的读操作可以读取到最新的数据状态,当数据分布在多个节点上,从任意结点读取到的数据都是最新的状态。
如何实现一致性?
为了保证数据一致性会对资源暂时锁定,待数据同步完成释放锁定资源。
如果请求数据同步失败的结点则会返回错误信息,一定不会返回旧数据。

可用性(Availability)

可用性是指任何事务操作都可以得到响应结果,且不会出现响应超时或响应错误。
如何实现可用性?
由于要保证从数据库的可用性,不可将从数据库中的资源进行锁定。

任何系统最多满足其中两点,不可能同时满足三点。

  • 何时满足CA?
    保证可用性和一致性,放弃分区:除非不是分布式架构,或者应用在一个永不会通信故障的网络中(理想),只有个别场景符合,当前的互联网架构显然不符合使用
  • CA为什么不能同时满足?
    如果保证 G2 的一致性,那么 G1 必须在写操作时,锁定 G2 的读操作和写操作。只有数据同步后,才能重新开放读写。锁定期间,G2 不能读写,没满足可用性
    如果保证 G2 的可用性,那么势必不能锁定 G2,所以一致性不成立
  • 何时需要满足CP?
    放弃可用性,追求一致性和分区容错性,对一致性要求高的场景,我们的zookeeper其实就是追求的强一致,在服务节点间数据同步时,服务对外不可用。比如跨行转账,一次转账请求要等待双方银行系统都完成整个事务才算完成。
  • 何时满足AP?
    通常实现AP都会保证最终一致性,后面讲的BASE理论就是根据AP来扩展的,放弃一致性。这是很多分布式系统设计时的选择。上边的商品管理,完全可以实现AP,前提是只要用户可以接受所查询的到数据在一定时间内不是最新的即可。
    一些业务场景 比如:订单退款,今日退款成功,明日账户到账,只要用户可以接受在一定时间内到账即可。

Base理论

BASE是Basically Available(基本可用)、Soft state(软状态)和Eventually consistent(最终一致性)三个短语的简写,BASE是对CAP中一致性和可用性权衡的结果,是基于CAP定理逐步演化而来的,其核心思想是即使无法做到强一致性(Strong consistency),但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性(Eventual consistency)。

分布式事务的实现主要有以下方案:

  • 本地消息表
  • 消息队列事务消息
  • 2PC 方案
  • TCC 方案

本地消息表

分布式事务拆分为多个子系统的本地事务;分布式事务=A系统本地事务 + B系统本地事务 + 消息通知;
准备: A系统维护一张消息表log1,状态为未执行,B系统维护2张表,未完成表log2,已完成表log3,消息中间件用两个topic,topic1是A系统通知B要执行任务了,topic2是B系统通知A已经完成任务了

1.用户在A系统里领取优惠券,并往log1插入一条记录
2.由定时任务轮询log1,发消息给B系统
3.B系统收到消息后,先检查是否在log3中执行过这条消息,没有的话插入log2表,并进行发短信,发送成功后删除log2的记录,插入log3
4.B系统发消息给A系统
5.A系统根据id删除这个消息

我们假设网络中断:
1.在1处中断:此时我们插入优惠券和log1用的本地事务,即使发消息失败,有定时任务轮询,会再次发送
2.在2处中断:当B系统发短信后,通知A系统失败,因为A系统有定时任务轮询,会重复再发一次,所以B系统会先检查log3,如果已经执行过了,就不发短信了,再次给A系统发送执行完成的消息,

实现最终事务一致要求:
预留资源成功理论上要求正式执行成功,如果执行失败会进行重试,要求业务执行方法实现幂等,每次执行的结果不变;
优点:开发简单,多个本地事务的结合,因此资源锁定时间短,性能好
缺点:代码侵入,需要写很多代码保证消息可靠,时效性差,取决于MQ消息发送是否及时

MQ事务消息

有一些第三方的MQ是支持事务消息的,比如RocketMQ,他们支持事务消息的方式也是类似于采用的二阶段提交,但是市面上一些主流的MQ都是不支持事务消息的,比如 RabbitMQ 和 Kafka 都不支持。
以阿里的 RocketMQ 中间件为例,其思路大致为:
第一阶段Prepared消息,会拿到消息的地址。
第二阶段执行本地事务,第三阶段通过第一阶段拿到的地址去访问消息,并修改状态。
也就是说在业务方法内要想消息队列提交两次请求,一次发送消息和一次确认消息。如果确认消息发送失败了RocketMQ会定期扫描消息集群中的事务消息,这时候发现了Prepared消息,它会向消息发送者确认,所以生产方需要实现一个check接口,RocketMQ会根据发送端设置的策略来决定是回滚还是继续发送确认消息。这样就保证了消息发送与本地事务同时成功或同时失败。
优点: 实现了最终一致性,不需要依赖本地数据库事务。
缺点: 实现难度大,主流MQ不支持,没有.NET客户端,RocketMQ事务消息部分代码也未开源。

两阶段提交方案(2PC)

有一个事务管理器的概念,负责协调多个数据库的事务,两阶段指的是:准备阶段、提交阶段
准备阶段完成资源操作,提交阶段提交事务。准备阶段如果任何其中一个数据库回答不 ok,那么就回滚事务。
提供强一致性保障,在事务执行过程中,所有的资源都是被锁定的,这种情况只适合执行时间确定的短事务

问题

  • 单点故障,如果在提交阶段节点故障,会导致数据不一致
  • 全局锁,锁定时间长
  • 性能差

    JMS框架实现了2PC方案,JMS是一个微服务框架

三阶段

三阶段相比二阶段多了一个步骤,但是依然不能解决数据一致性问题。所以以上两种方案都不推荐使用

TCC方案

TCC模式可以解决2PC中资源锁定和阻塞问题,减少资源锁定时间。
TCC 的全称是:Try、Confirm、Cancel。
Try 阶段:这个阶段说的是对各个服务的资源做检测和预留。
Confirm 阶段:这个阶段说的是在各个服务中执行实际的操作。
Cancel 阶段:如果任何一个服务的业务方法执行出错,那么这里就需要进行补偿,就是执行已经执行成功的业务逻辑的回滚操作。(把那些执行成功的回滚)

优点

Try、Confirm、Cancel这三个阶段都是独立事务,互不影响,每一阶段都会提交本地事务并释放锁。如果事务执行失败,就执行补偿操作,不是回滚。这样就避免了资源长期锁定和阻塞。性能比较好

缺点

  • TCC方案是严重依赖于你自己写代码来回滚和补偿了,会造成补偿代码巨大。数据库也要相应设计。需要些代码实现try、commit、cancel接口
  • cancel动作有可能失败

比如说我们,一般来说跟钱相关的,跟钱打交道的,支付、交易相关的场景,我们会用 TCC,严格保证分布式事务要么全部成功,要么全部自动回滚,严格保证资金的正确性,保证在资金上不会出现问题。

以转账举例:


服务A需要减100,服务B需要加100
两个服务都实现了Try、Confirm、Cancel接口
A、B服务对应的数据库在设计表时需要加一个临时金额字段
当业务应用执行一个转账操作时,先调用A、B两服务的Try接口,A服务临时金额-100,B服务临时金额+100
Try执行完后再执行A、B的Confirm接口,执行真正的操作,A扣减余额,B增加余额
如果Confirm接口调用过程中出现异常,就执行A、B的Cancel接口,也就是回滚操作
事务协调器:负责调用Confirm、Cancel接口,因为逻辑控制比较麻烦,提交回滚操作交给事务协调器去做

Saga

暂无...

参考:
http://www.ruanyifeng.com/blog/2018/07/cap.html(CAP定理)
https://www.bilibili.com/video/BV1eK4y1e79N?p=4
https://www.cnblogs.com/hakuci/p/5301729.html (DTC)
http://servicecomb.apache.org/cn/docs/distributed-transactions-saga-implementation/

posted @ 2020-08-08 19:03  .Neterr  阅读(443)  评论(0编辑  收藏  举报