分布式事务的实现思想

26 篇文章 0 订阅
6 篇文章 0 订阅

分布式事务的实现思想

参考地址:《从银行转账失败到分布式事务:总结与思考》

分布式事务的基本概念与本地事务类似,都保证了 ACID 特性(见[本篇第二章](# 二. 事务的特性))。随着数据的规模越来越大,就出现了对业务的解构,包括数据层面的关系型数据库的垂直、水平分表,以及服务层面的拆分,将一个大服务拆分为后单独部署,甚至同时也将数据库独立出来。这时候本地数据库事务就不能满足多个数据库、异构系统的原子性、持久性了,需要使用分布式事务的方法。通常,分布式事务只需要保证原子性,通过保证原子性来保证应用层面的一致性,由本地事务保证隔离性和持久性。
从 CAP 特性上考虑,由于分布式事务存在网络分割的情况,所以一定需要满足分区容忍性,剩下的需要在一致性 (Consistency)可用性 (Available) 之间做权衡。下面提到各种分布式事务的实现方法与协议,都是需要在一致性与可用性之间权衡的。

1. 二阶段提交协议 (2PC)

二阶段提交 (Two-phase Commit Protocol) 是非常经典的强一致性、中心化的原子提交协议,协议中有两类节点:一个中心化协调者 (Coordinator) 节点N 个参与者 (Cohort) 节点
2PC 每一次事务提交都分为两个阶段:

  1. 协调者询问所有的参与者是否可以提交事务,所有参与者向协调者投票;
  2. 协调者根据参与者的投票结果,做出是否事务可以全局提交的决定,并通知所有参与者执行该决定。
    2PC流程

2PC 提交流程中,参与者不能改变自己的投票结果。此外,2PC 可以全局提交的前提,是所有参与者都同意提交事务。只要有一个参与者投票,选择放弃事务,则全局事务必须被放弃。
2PC全流程

2PC 的优缺点:

  • 优点:
    • 强一致性。只要节点或者网络最终恢复正常,协议就能保证顺利结束;
    • 部分关系型数据库(如 Oracle)、框架直接支持;
  • 缺点:
    • 容错能力较差:比如节点宕机、超时的情况下,无法确定流程的状态,只能不断重试;
    • 性能较差,交互消息多,受最慢节点影响。
    • 访问共享资源你的时候,发生冲突和死锁的概率增高。随着数据库节点增多,这种趋势越来越严重。

2. 三阶段提交协议 (3PC)

相较于两阶段提交协议,三阶段提交协议 (3PC) 解决了阻塞问题,将两个阶段扩展为三个阶段,增加了超时机制。虽然解决了 2PC 情况下的阻塞问题,但一次提交要传递六次消息,延时很大。

3. TCC

TCC 是 Try, Commit, Cancel 的缩写,保证强一致性的同时,最大限度提高系统的可伸缩性与可用性
假设一个完整的业务包含一组子业务:

  • Try:完成所有子业务检查,预留必要的业务资源,实现与其他事务的隔离;
  • Confirm:使用 Try 阶段预留的业务资源,真正执行业务;同时满足幂等性,支持重试;
  • Cancel:释放 Try 资源预留的业务资源,同样满足幂等性。

一次完整的交易,由一系列微交易的 Try 操作组成,如果所有 Try 操作都成功,最后由微交易框架统一 Confirm,否则统一 Cancel,这样实现了类似 2PC 的强一致性。TCC 的特点有:

  • Try 操作兼具资源操作与准备能力,没有单独的准备阶段 (Prepare),这样降低了提交协议的成本;
  • Try 操作由业务层保证原子性,这样也可以灵活选择业务资源的锁定粒度,而不是锁住整个资源,提高了整体的并发度;
  • TCC 的所有子业务都要事先自己的 Confirm, Cancel 操作,实现相应的补偿逻辑,所以需要较高的开发成本。

4. 基于消息的分布式事务

基于异步消息的事务机制,可以分为主事务从事务两部分。主事务本地先行提交,然后通过消息通知各个从事务,从事务收到主事务发来的消息后,各自进行本地提交。
由上面可以得知,这是一种异步事务机制,虽然只保证最终一致性,但可用性非常高,不会因为故障发生阻塞。
实现异步消息的事务机制有本地消息表事务消息两种方式,两种方式都可以保证主事务的提交与消息发送两者之间的原子性。下面以转账操作为例,转账操作分为四步:用户 A 扣钱,发送消息,用户 B 接受消息,用户 B 扣钱。其中前两步肯定是原子性的,本地消息表和事务消息的解决方案分别如下:

4.1 本地消息表

本地消息表的含义如字面意思,将消息存到本地数据库中,通过本地事务保证消息的存入。依旧是上面的转账例子,伪代码如下:

begin transaction:
	update user set account = account - 100 where userId = '00000000';
	insert into message(userId, amount, status) values ('00000000', 100, 1);
commit transaction

主事务通过这种本地数据库事务的方式,保证数据库中扣除 A 的操作,和向数据库中存入消息(类似于消费流水信息)的操作是原子性的。然后从事务定时的从数据库中拉取消息,然后执行。

4.2 事务消息

事务消息的部分见《Kafka 篇》第二章事务部分

将事务消息与本地消息表对比,事务消息不依赖于本地数据库存储消息,而是通过消息中间件保证本地事务与消息的原子性。但是实现了事务的消息队列比较少,不能通用化使用。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值