事务在触发器中结束。批处理已中止。_Introduction to DDIA & 6.824(八):分布式事务...

dd1d6f76a527dc0ea4c728ccce4d315c.png

Uniform consensus protocol

什么是事务?事务是DBMS中在逻辑上的一个最小工作单元,它可能包含了一系列指令。分布式事务则是尝试在多节点的环境下,实现这个语义。核心问题是ACID中的A、I的问题。

  • 原子性问题:
    • 在分布式事务中,这要求参与事务的所有节点,要么全部执行Commit操作,要么全部执行Abort操作。
    • 原子提交问题:参与事务的所有节点,需要在“执行Commit还是Abort”这一点上达成一致(其实就是共识)
    • 原子提交协议:解决原子提交问题的算法协议。2PC和3PC,属于原子提交协议两种不同的具体实现
  • 原子提交协议和共识协议:
    • 目的相同:
      • 共识问题,解决的是如何在分布式系统中的多个节点之间就某个提议达成共识。
      • 原子提交问题,解决的是参与分布式事务的所有节点在“执行Commit还是Abort”这一点上达成共识。
    • 范围不同:前者要求所有节点达成共识,后者要求大多数未故障的节点达成共识

为了解决原子提交问题,并提供一个完整的分布式事务解决方案,很多人提供了很多不同的设计。

分布式事务中,很少直接讨论ACID的一致性问题。相关问题:

外部一致性,主要是解决在非lock base并发隔离的场景下,事务的可见顺序遵守外部可见的提交顺序,TrueTime、TSO就是解决此类问题的,HLC可以解决read-after-write,但是无法保证不相关事务间的可见顺序(比如先后提交的两个事务T1、T2,保证同时并发执行读事务R3,禁止出现T2 R3 T1这样的时序)。这个问题本质上是多版本并发控制(mvcc)的问题,与paxos无关。 https://www. zhihu.com/question/2758 45393/answer/385101903

问题研究

02e13c410701dca7627ad9a59bf6b17c.png

Atomic Commit问题:

  • Safety
    • Stability:一旦一个节点进入comitted或者aborted状态,这个节点就一直停留在这个状态上;
    • Consistency:不存在有一个节点是 committed 状态而另外一个节点是 aborted 状态
  • Liveness
    • Non-Triviality:如果在整个协议执行过程中整个网络是正常的,那么
      • (A) 如果所有 RM(Resource Manager,即事务的参与方) 都进入 prepared 状态,那么所有的 RM 最终将会进入 committed 状态,同时
      • (B) 如果任意 RM 进入 aborted 状态,那么所有的 RM 最终将会进入 aborted 状态。
    • Non-Blocking:如果任意时刻,节点网络在足够长(long enough)的时间内保持正常, 那么在这些节点上运行的 RM 最终要么进入 committed 状态要么进入 aborted 状 态。

网络模型:部分同步

故障模型:crash-recovery model

网络模型、故障模型相关内容见:Yannick:Introduction to DDIA & 6.824(六):线性化与多副本一致性

Two-Phase Commit

b07f4191d271d2d0093a780e09aa83c5.png

以上是简单的2PC的操作示意图,图中引入了一个协调者(Coordinator、Transaction Manager)的角色。

当应用程序开始提交事务时,协调者开始阶段1(Prepare):

发送一个准备请求给事务中的参与者,询问是否可以提交。协调者然后跟踪参与者的回应:

  • 如果所有参与者都应答”是“,表示它们已经准备好提交。
  • 如果任何参与者回答了”否“,则协调者在阶段2(commit)中向所有节点发送放弃请求。

协调者接下来在阶段2(commit)发出提交请求,提交才开始执行。

一些情况:

  • 如果参与者在2PC期间失败,那么协调者将中断事务提交,事务被认为失败;
  • 如果在第二阶段发送提交时失败,协调者将无限期重试。
  • 协调者做了提交的决定之后,这个决定也是不可撤销的
  • 协调者故障:协调者故障会发生不可预期的错误,因此:
    • WAL保证协调者的崩溃恢复
    • 需要保证协调者的稳定性

2PC 描述

  1. 启动一个分布式事务时,协调者生成一个全局唯一的事务ID。
  2. 应用在每个参与者上启动单节点事务,并在单节点事务上捎带上这个全局事务ID。所有的读写都是在这些单节点事务中各自完成的。如果在这个阶段出现任何问题(例如,节点崩溃或请求超时),则协调者或任何参与者都可以中止。
  3. 当应用准备提交时,协调者向所有参与者发送一个准备请求,并打上全局事务ID的标记。如果任意一个请求失败或超时,则协调者向所有参与者发送针对该事务ID的中止请求。
  4. 参与者收到准备请求时,需要确保在任意情况下都的确可以提交事务。这包括将所有事务数据写入磁盘(出现故障,电源故障,或硬盘空间不足都不能是稍后拒绝提交的理由)以及检查是否存在任何冲突或违反约束。通过向协调者回答“是”,节点承诺,只要请求,这个事务一定可以不出差错地提交。换句话说,参与者放弃了中止事务的权利,但没有实际提交。
  5. 当协调者收到所有准备请求的答复时,会就提交或中止事务作出明确的决定(只有在所有参与者投赞成票的情况下才会提交)。协调者必须把这个决定写到磁盘上的事务日志中,如果它随后就崩溃,恢复后也能知道自己所做的决定。这被称为提交点(commit point)
  6. 一旦协调者的决定落盘,提交或放弃请求会发送给所有参与者。如果这个请求失败或超时,协调者必须永远保持重试,直到成功为止。没有回头路:如果已经做出决定,不管需要多少次重试它都必须被执行。如果参与者在此期间崩溃,事务将在其恢复后提交——由于参与者投了赞成,因此恢复后它不能拒绝提交。
来自DDIA-Ch9 系统承诺

2PC需要注意的地方:

  • 参与者prepare阶段回复yes后,参与者无法在协调者不参与(发送abort请求)的情况下进行回滚
  • 在决定commit之后,所有的节点都需要执行commit的操作,不断重试直到成功

存在2PC可能卡住并等待协调者恢复的情况。这样就很坑爹了。

2PC 缺陷

2caea8c10df227ec3d87837918d6a23a.png

2PC的核心问题在于对容错性的支持太差,对故障模型要求的假设过高。

  • 协议不支持针对TM的容错。任何阶段,只要TM发生了crash,整个流程就会被block。
  • 协议不支持crash-recovery以下故障模型的容错。TM崩溃重启后,如果没能保存崩溃前事务的进行情况,存在协议无法进行的情况。
一个例子:
第一阶段,协调者C发送事务提议给A和B,A反馈no,B反馈yes
第二阶段,根据上一阶段反馈的信息,协调者C发送中止事务信息,当协调者C刚给A发送了事务中止信息后就挂掉了,A接收到中止信息,回滚时候后,也挂掉了。这时,新的协调者D(omission fail model),无法获知A的事务是提交了还是回滚了,这样B无论是提交事务还是回滚事务都有可能造成数据不一致。
这时,整个系统卡住了,没有办法确定B应该选择提交事务还是回滚事务。整个系统失去了liveness。需要人工介入求知A的提交情况。
  • 崩溃后潜在的致命风险。理论上,协调者在向参与者发送提交或中止的决定前,必须先将这个决定持久化。来保证协调者的崩溃恢复性。但是实践中总是存在崩溃时信息未保存的情况。

Three-Phase Commit

目标:

  • 解决2PC中卡住并等待协调者恢复的情况
  • 解决2PC中,协议无法进行的情况

前提假设:

  • 同步模型(网络延迟有界,节点响应时间有限),相对于2PC提高了约束
  • 故障模型:omission failure可以忍受,相对于2PC降低了对故障威力的要求
  • 需要一个perfect failure detector,即一个可靠的机制来判断一个节点是否已经崩溃。
    • 通常是通过超时来判断的
    • 但是很显然,超时并不能满足这个要求
  • 当超时时,就会选择abort

描述:

  • 新增了PreCommit Phase。当propose阶段有任何人回复no,那么就会跳过PreCommit直接Abort。
  • 进入PreCommit阶段意味着所有的参与者进入了prepared的状态。当此时收到PreCommit请求,返回肯定回复。
  • 最后由coordinator统一进行提交:协调者只有确保所有参与者都从PREPARED状态到达PRE-COMMIT状态后才会发送COMMIT消息。

3PC并非是一个完美的解决方案,它牺牲了safety来提高liveness

  • 相对2PC,可以处理Propose之后,TM宕机带来的阻塞问题:超时了直接回滚即可。
  • 无法处理网络分区问题。即3PC的前提假设不成立,或发生了溢出上界的网络延迟,这个时候程序的safety就无法保证了。

31becb436116dbccb6da717d4d488efb.png

Paxos Commit

Trivial Paxos Commit

  • 2PC瓶颈点是Coordinator。提高Coordinator的容错性,可以为Coordinator建立Paxos集群。
  • 缺点:不够高效,2PC 的 prepare 阶段和 Paxos 的 prepare 阶段分开来执行了,导致存在多余的信息传递

Paxos Commit 和 2PC

9190da811ed6eda24d1b04ab86d7fc42.png
  • 本质上Paxos Commit是把2PC中TM的角色变成了Paxos中的Leader proposer + acceptor
    • Leader proposer + acceptor本质上是RM节点承担的
    • 以此干掉了中心化的TM节点
    • 但是同时可以增加一些节点,来专门参与事务决议过程,这样整个系统就有了宕机容错性
  • Paxos Commit做了延迟优化。
  • Paxos Commit中的leader不能单方面的决定abort,leader必须要走一遍标准的Paxos协议的prepare+accept两阶段把abort值进行一遍决议。
  • 绝大部分时候都是正常运作的系统来说 Paxos Commit 的 extra cost 相较于2PC更高。
  • 2PC是Paxos commit中fault tolerance节点数为0的特化版本

2PC的隔离性

b07f4191d271d2d0093a780e09aa83c5.png
注意图中RM上灰色的线

由于2PC中涉及到对相关内容的锁控制,而事务结束时,相关内容的锁才释放。因此两个并发的事务如果涉及到相同的内容,那么锁就会迫使两个事务以串行的方式执行。

但是类似幻读性质的隔离性可能没有办法通过2PC实现和完成。

实践中的分布式事务

分布式事务类型

  • 数据库内部的分布式事务:分布式数据库支持数据库节点之间的内部事务;所有参与事务的节点都运行相同的数据库软件
  • 异构分布式事务:参与者是两种或以上不同技术:例如来自不同供应商的两个数据库,甚至是非数据库系统(如微服务)。跨系统的分布式事务必须确保原子提交,尽管系统可能完全不同。
    • 很多互联网公司中的分布式事务解决的问题通常是「如何保证我的调用链正确执行,且结果恰好执行一次」。这本质上是原子提交问题的副作用

异构分布式事务背景

fedf2c0936e6dfd65b38cb0e3ce8c823.png

Business涉及到的两个服务(子业务系统),分别使用了自己的DB。如何保证Business调用时,多个RPC的状态一致(一起完成、有错回滚)。

角色:

  • Business:事务管理器(Transaction Manager),2PC中的coordinator,通常被应用程序引用。
  • Storage:资源管理者(Resource Manager),所有参与事务的节点

XA protocol

X/Open XA,是对分布式事务处理的规范,使用两阶段协议来保证分布式事务问题。spec见 。是数据库、消息中间件为了对分布式事务进行支持,而需要实现的接口。

XA是一个与事务协调者连接的C API。Java中是Java事务API(JTA, Java Transaction API)

问题:

  • 性能问题
  • 兼容问题。需要各个服务依赖的数据库都支持XA。

2PC优化版本

以下内容见:两阶段提交的工程实践

步骤:

  • 预处理阶段:
    • 发送计划:协调者向若干参与者发送SQL请求或执行计划,一个sharding即对应一个参与者,
    • 保存事务上下文:针对这个事务,在每个参与者中会维护一个通过事务ID索引的事务上下文,用于维护行锁、redo数据等,有必要的情况(redolog过多)下,这个阶段可能会异步的持久化redolog。
  • Prepare阶段:协调者收到客户端提交事务的请求,向各个参与者发送prepare命令,命令中携带了当前事务的参与者列表,参与者收到prepare命令后,将事务的redolog、参与者列表、prepare日志持久化后,向协调者和其他参与者发送prepare成功的消息。
  • Commit阶段:
    • 协调者收到所有参与者应答prepare成功的消息后,即向客户端返回事务提交成功
    • 对于每个参与者,当它确认所有参与者都prepare成功后,将本地事务提交并释放行锁等资源,并异步的持久化一条commit日志,然后向其他参与者发送commit成功的消息。
  • Finish阶段:对于每个参与者,当它确认所有参与者都commit成功后,将本地事务上下文释放,并异步的持久化一条finish日志。

和2PC比较:

  • 协议全程只有 一次RPC(Prepare RPC)交互延迟+一次日志持久化(Prepare日志)延迟。
  • 预处理阶段的redolog、Commit日志、Finish日志为异步持久化,不影响事务延迟;
  • Prepare日志为同步持久化,需要等待持久化成功才能发送应答。
  • 参与者之间的Prepare状态与Commit状态的查询,不影响事务延迟,而协调者只需要等待所有参与者的Prepare应答后即可向客户端返回,
  • 对读事务的影响:各个参与者上的事务,要等所有参与者Prepare成功后才能提交和释放行锁;可能出现协调者先应答了客户端,客户端再来读取时,一些sharding上的行锁还未释放(即事务还未提交),读事务需要等待直到事务提交。
两阶段提交的工程实践

评价

  • 持久化代替RPC:
    • 本质上是将commit动作前移到了prepare阶段。释放锁相关动作仍放在commit阶段。
    • 因此需要持久化来保证很多东西,比如回滚能力等
  • 通过持久化 + 参与者之间的直接沟通来规避TM的宕机问题。
  • 问题在于,持久化不一定是靠谱的;这个方案其实并没有严格证明safety不受损坏
  • 主要针对非异构分布式事务

TCC

Try-Confirm-Cancel 与 prepare、commit和abort对应。是一种业务层面的2PC实现机制。控制流程与2PC一样。但是Try Confirm Cancel RPC的实现没有具体的spec和指导规范。

这些RPC基本的语义如下:

  • Try:TM要求RM进行业务检查,预留资源(一阶段)
    • 完成所有业务检查(一致性)
    • 预留必须业务资源(准隔离性)
  • Confirm:真正进行业务执行。(二阶段)
    • 真正执行业务
    • 不作任何业务检查
    • 只使用Try阶段预留的业务资源
    • Confirm操作满足幂等性
  • Cancel:回归操作(二阶段)
    • 释放Try阶段预留的业务资源
    • Cancel操作满足幂等性
    • Cancel操作需要注意cancel了一个没有try过的事务
  • 问题
    • 并发控制:多事务对同一个资源的控制,需要业务仔细定义
    • 空回滚:主动cancel了一个没有try过得的事务
      • Cancel 执行时发现没有对应的事务 xid 或主键时,需要返回回滚成功。
    • 悬挂问题:按序调用try、cancel,但是由于网络问题,cancel先处理而try后处理。导致事务没有被释放
      • Cancel 空回滚返回成功之前先记录该条事务 xid 或业务主键,标识这条记录已经回滚过,Try 接口先检查这条事务xid或业务主键如果已经标记为回滚成功过,则不执行 Try 的业务操作。
    • 幂等控制:Try、Cancel操作的重试
      • 事务 xid 或业务主键判重来控制

SAGA

在2PC & TCC 基础上加入了补偿操作。

概念:

  • 由一系列的本地事务构成
  • 消息/事件驱动:每个本地事务结束后,发布消息/事件,来出发下一个本地事务执行
  • 补偿模块:如果本地事务执行失败后,需要调用相关的补偿事务。
    • 一般这种补偿模块是执行模块的逆操作
    • 补偿模块也需要做成幂等的

使用场景:

  • 业务流程长
  • legacy system,没有办法实现TCC的接口

问题:

  • prepare 阶段的资源没能加锁,隔离性会有问题。
    • 「宁可长款,不可短款」

实现注意:

  • 事务编排:AWS Step Functions
  • 事务执行:
    • Event driven
    • 中心节点调度

aaaa5c9ceaf8bcabca30b09d5f12f8e2.png
event driven

0035818c63551245040b5ebae27527a6.png
centralized coordinator

References

  • 数据库事务原子性、一致性是怎样实现的? - 知乎 https://www.zhihu.com/question/30272728
  • 关于一致性协议的一些对比和总结 - 陈宗志的文章 - 知乎 https://zhuanlan.zhihu.com/p/31119178
  • 漫话分布式系统共识协议: 2PC/3PC篇 - 范斌的文章 - 知乎 https://zhuanlan.zhihu.com/p/35298019
  • consensus-on-transaction-commit https://lamport.azurewebsites.net/video/consensus-on-transaction-commit.pdf
    • http://duanple.blog.163.com/blog/static/70971767201103051639551/
  • 一致性, 两阶段提交和事务提交的发展史(译) - 陈宗志的文章 - 知乎 https://zhuanlan.zhihu.com/p/31072379
  • 一些关于 Distributed System 的话题(四)Distributed Txn(1/n) - 邓明华的文章 - 知乎 https://zhuanlan.zhihu.com/p/143401073
  • ON THE RELATIONSHIP BETWEEN THE ATOMIC COMMITMENT AND CONSENSUS PROBLEMS https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.54.3710&rep=rep1&type=pdf
  • 分布式事务提交协议: 2PC/3PC - grakra的文章 - 知乎 https://zhuanlan.zhihu.com/p/34522695
  • 分布式系统理论基础 - 一致性、2PC和3PC - bangerlee的文章 - 知乎 https://zhuanlan.zhihu.com/p/21994882
  • 分布式事务:2PC、3PC、SAGA、TCC - bluesky的文章 - 知乎 https://zhuanlan.zhihu.com/p/142136446
  • 分布式事务 Seata Saga 模式首秀以及三种模式详解 | Meetup#3 回顾 - 金融级分布式架构的文章 - 知乎 https://zhuanlan.zhihu.com/p/78269431
    • 基于 Seata Saga 设计更有弹性的金融应用 - 金融级分布式架构的文章 - 知乎 https://zhuanlan.zhihu.com/p/90187282
  • 蚂蚁金服分布式事务开源以及实践 | SOFA 开源一周年献礼
    • https://www.sofastack.tech/activities/sofa-channel-12/
    • https://www.sofastack.tech/activities/sofa-channel-4/
    • https://www.sofastack.tech/activities/sofa-channel-10/
  • SEATA https://seata.io/zh-cn/docs/dev/mode/at-mode.html
  • Saga Pattern | Application Transactions Using Microservices – Part I
  • Saga Pattern | Application Transactions Using Microservices – Part II
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值