分布式事务基础

分布式事务基础

Posted by lijian on September 23, 2019

本地事务与分布式事务

当下互联网绝大部分公司都进行了数据库拆分和服务化(SOA),微服务。 在这种情况下,完成某一个业务功能可能需要横跨多个服务,操作多个数据库。 这就涉及到到了分布式事务,用需要操作的资源位于多个资源服务器上,而应用 需要保证对于多个资源服务器的数据的操作,要么全部成功,要么全部失败。 本质上来说,分布式事务就是为了保证不同资源服务器的数据一致性

本地事务只会操作一个数据库,分布式事务操作的是多个数据库,所以分布式事务相对本地事务最核心的 获取一个连接,使用连接来 commit , rollback 很明显不适用了

CAP 定理

布鲁尔定理,分布式计算领域公认的一个定理

在一个分布式系统(指互相连接并共享数据的节点的集合)中,当涉读写操作时,只能保证一致性(Consistence)、可用性(Availability)、分区容错性(Partition)三者中的两个,另外一个必须被牺牲

参考文章 : 分布式事务 CAP

BASE 理论

BASE 是 Basically Available(基本可用)、Soft state(软状态)和 Eventually consistent (最终一致性)三个短语的缩写。是对CAP中AP的一个扩展

基本可用 分布式系统在出现故障时,允许损失部分可用功能,保证核心功能可用。

软状态 允许系统中存在中间状态,这个状态不影响系统可用性,这里指的是CAP中的不一致。

最终一致 最终一致是指经过一段时间后,所有节点数据都将会达到一致。

BASE和 ACID 是相反的,它完全不同于ACID的强一致性模型,而是通过牺牲强一致性来获得可用性,并允许数据在一段时间内是不一致的,但最终达到一致状态,是柔性事务的理论支持

参考文章 : 分布式事务 BASE

一致性模型

  • 强一致性

数据更新成功后,任意时刻所有副本中的数据都是一致的,一般采用同步的方式实现。

  • 弱一致性

数据更新成功后,系统不承诺立即可以读到最新写入的值,也不承诺具体多久之后可以读到。

  • 最终一致性

弱一致性的一种形式,数据更新成功后,系统不承诺立即可以返回最新写入的值,但是保证最终会返回上一次更新操作的值

核心

  • XA

XA是由X/Open组织提出的分布式事务的规范。 XA规范主要定义了(全局)事务管理器(TM)和(局 部)资源管理器(RM)之间的接口。主流的关系型 数据库产品都是实现了XA接口的 基于数据库的XA协议本质上就是两阶段提交

文章:分布式事务-XA

  • RM

通常指的是 数据库,也可以是JMS 队列 等

  • TCC

Try阶段:尝试执行,完成所有业务检查(一致性),预留必须业务资源(准隔离性)

Confirm阶段:确认执行真正执行业务,不作任何业务检查,只使用Try阶段预留的业务资源,Confirm操作满足幂等性。要求具备幂等设计,Confirm失败后需要进行重试。

Cancel阶段:取消执行,释放Try阶段预留的业务资源 Cancel操作满足幂等性Cancel阶段的异常和Confirm阶段异常处理方案基本上一致。

  • 2pc

在这种情况下,TPM 将组织一个两阶段提交。在两阶段提交中,TPM 首先向每个 RM 发送一个 “准备” 消息,询问它是否准备就绪以及是否能够提交事务;如果它收到来自所有 RM 的确认应答,则将事务在其自己的事务日志中标记为已提交,然后指示所有 RM 提交事务。如果某个 RM 失败,则重新启动时它将向 TPM 询问有关失败时未处理的所有事务的状态,并提交它们或者对它们执行回滚操作。

两个阶段提交类似于社会上的结婚典礼 —— 牧师或神父询问双方 “您愿意让这个男人/女人作为您的丈夫/妻子吗?” 如果双方都回答是,则将宣布他们成为夫妻;否则,双方不能结婚。不管双方中的哪一方首先说 “我愿意”,在一方没有回答时,另一方决不能完成结婚

参考mysql 的事务模型来理解: MySQL 事务

  • 3pc

  • 可靠消息

将需要分布式处理的任务通过消息或者日志的方式来异步执行,消息或日志可以存到本地文件、数据库或消息队列,再通过业务规则进行失败重试,它要求各服务的接口是幂等的。

参考文章:分布式事务-基于可靠消息

  • mq 事务

有一些第三方的MQ是支持事务消息的,比如RocketMQ,他们支持事务消息的方式也是类似于采用的二阶段提交,但是市面上一些主流的MQ都是不支持事务消息的,比如 RabbitMQ 和 Kafka 都不支持。

以阿里的 RocketMQ 中间件为例,其思路大致为:

第一阶段Prepared消息,会拿到消息的地址。

第二阶段执行本地事务

第三阶段通过第一阶段拿到的地址去访问消息,并修改状态。

也就是说在业务方法内要想消息队列提交两次请求,一次发送消息和一次确认消息。如果确认消息发送失败了RocketMQ会定期扫描消息集群中的事务消息,这时候发现了Prepared消息,它会向消息发送者确认,所以生产方需要实现一个check接口,RocketMQ会根据发送端设置的策略来决定是回滚还是继续发送确认消息。这样就保证了消息发送与本地事务同时成功或同时失败。

WX20190923-155543@2x.png

这个的事务模型其实和MySQL的两阶段提交是一样的,写 redo prepare (预发送消息), 写 binlog (业务操作),写redo commit(确认发送消息),如果 业务操作完成但是确认发送消息失败,会自动commit 消息.

最后

能不用分布式事务就不用,如果非得使用的话,结合自己的业务分析,看看自己的业务比较适合哪一种,是在乎强一致,还是最终一致即可

参考项目

基于可靠消息参考项目

最大努力通知参考项目

tcc 参考项目

ps

主要参考来源

https://yq.aliyun.com/

https://www.ibm.com/developerworks/cn/

巨人的肩膀

再有人问你分布式事务,把这篇扔给他

分布式事务详解

大规模SOA系统中的分布式事务处理