引言:分布式事务是分布式数据库的基础性功能,在2017年上海MySQL嘉年华(IMG)和中国数据库大会(DTCC2018)中作者都对银联UPSQL Proxy的分布式事务做了简要介绍,受限于交流形式难以做全面细致的探讨,借由本文进一步展开。

UP-2PC是面向分布式数据库的由中国银联自主研发的针对MySQL的2PC分布式事务实现,以UPSQL Proxy(分布式式数据库代理)作为事务管理器,UPSQL(MySQL银联定制版本)为资源管理器。

由于MySQL在5.7中彻底解决了xa prepare的高可用问题以及连接关闭自动回滚等问题,使得MySQL的xa真正可用。

分布式事务

如果了解分布式事务与2PC,并考虑过如下议题(不必认可),可略过本节内容:

• TCC是应用层的2PC实现

• 2PC的实现难点在于事务管理器(TM)的异常处理

• 2PC与Paxos:Paxos用于相同数据的分布式高可用存储,2PC更擅长不同数据分布式存储的事务一致性

2PC协议(Prepare,Commit, Rollback)

首先我们要提到分布式事务模型:X/Open DTP(Distributed Transaction Processing) Reference Model。

• X/Open组织定义的一套分布式事务的标准,定义了规范和API接口,由厂商进行具体的实现

• X/Open DTP定义了三个组件: AP,TM,RM

• AP(Application Program):使用分布式事务的应用

• RM(Resource Manager):资源管理器,这里可以是一个DBMS系统,或者消息服务器管理系统,资源管理器必须实现XA定义的接口

• TM(Transaction Manager):事务管理器,负责协调和管理事务

通常把TM管理的分布式事务称为全局事务,把RM管理的事务称为本地事务。

两阶段提交,是X/Open DTP的一个实现,主要在事务结束动作前加入了一个Prepare阶段:

• Prepare阶段:Prepare成功的数据,后续Commit一定会成功。

• Commit/Rollback阶段:提交或回滚数据

图例:2PC协议

DTP很早就指出资源管理器的类型不单单是数据库,还可以是消息服务。

TCC协议(Try, Confirm, Cancel)

TCC编程: 将业务逻辑拆解为Try、Confirm和Cancel三个阶段,实际上两阶段提交的变种。业务场景如:锁单、下单和取消。

TCC编程需要保证幂等性,即:可以被不断调用接口(直至符合预期),因而需要保证多次调用不会导致异常影响(如重复扣款等)。2PC的主要问题

2PC协议,主要是在如下场景的处理较为复杂:

• 同步阻塞、应答超时

• TM宕机恢复

2PC比较:数据库VS应用

比较容易取得共识的结论:不同业务系统之间使用2PC。

那剩下的问题就简单了, 相同业务系统之间是使用数据访问层2PC还是TCC?一般而言,基于研发成本考虑,会建议:新系统由数据库层来实现统一的分布式事务。

但对热点数据,例如商品(票券等)库存,建议使用TCC方案,因为TCC的主要优势正是可以避免长时间锁定数据库资源进而提高并发性。

2PC与Paxos

有一种广为流传的观点:"2PC到3PC到Paxos到Raft",即认为:

• 2PC是Paxos的残次版本

• 3PC是2PC的改进

上述2个观点都是我所不认同的,更倾向于如下认知:

• 2PC与Paxos解决的问题不同:2PC是用于解决数据分片后,不同数据之间的分布式事务问题;而Paxos是解决相同数据多副本下的数据一致性问题。例如,UP-2PC的数据存储节点可以使用MGR来管理统一数据分片的高可用

• 3PC只是2PC的一个实践方法:一方面并没有完整解决事务管理器宕机和资源管理器宕机等异常,反而因为增加了一个处理阶段让问题更加复杂

MySQL的2PC语法(XA)

MySQL提供的2PC语法(MySQL XA)如下:

XA {START|BEGIN} xid [JOIN|RESUME]
XA END xid [SUSPEND [FOR MIGRATE]]
XA PREPARE xid
XA COMMIT xid [ONE PHASE]
XA ROLLBACK xid
XA RECOVER [CONVERT XID]

图例:MySQL XA语法

我们针对语法组合方式略微展开。

2PC的事务提交流程

2PC提交流程为:

XA {START|BEGIN} xid [JOIN|RESUME]
/*事务操作*/
XA END xid [SUSPEND [FOR MIGRATE]]
XA PREPARE xid
XA COMMIT xid

2PC的事务回滚流程

2PC回滚流程为:

XA {START|BEGIN} xid [JOIN|RESUME]
/*事务操作*/
XA END xid [SUSPEND [FOR MIGRATE]]
XA PREPARE xid /*可选*/
XA ROLLBACK xid

注意回滚时Prepare阶段不是必须的,所以可以在如下场景中可以省略Prepare阶段,减少网络交互:

• 本地事务未发生写操作

• 本地事务需要回滚

当然没有Prepare阶段也就意味着,其从两阶段退化为一阶段了。

2PC退化为一阶段提交

XA语法下退化为一阶段提交的流程为:

XA {START|BEGIN} xid [JOIN|RESUME]
/*事务操作*/
XA END xid [SUSPEND [FOR MIGRATE]]
XA COMMIT xid ONE PHASE

使用XA的一阶段提交(XA COMMIT xid ONE PHASE)的一个典型场景为在全局事务中只有该本地事务有写操作,在UP-2PC因为使用"最后参与者"方案,所以最后参与者也会使用XA一阶段提交。

2PC恢复处理

这里指当与MySQL的交互出现异常,例如与MySQL的会话断开、MySQL宕机等,如何获取并恢复处于Prepare状态下的2PC事务,并进行提交或回滚处理。

mysql> XA RECOVER;

UP-2PC技术实现

UP-2PC要解决的核心问题正是如何处理:事务管理器、资源管理器异常,以及可能的网络。

• 事务管理器(TM)突然宕机以及网络异常,导致协调信息丢失。典型的:异常发生后,处于Prepare状态的RM,正确进行后续处理(rollback和commit)

• 部分资源管理器(RM)突然宕机以及网络异常,导致的信息协调。典型的:全局事务内多个Prepare状态,此时若部分RM发生本异常,则TM如何处理,可选方案:

1.全局事务向请求者应答成功,由事务事务管理器后续提交异常RM

2.回滚所有RM,并向前端应答事务提交失败

通过上述探讨,可以发现2PC的主要难点在于:

• 事务状态信息维护:全局事务状态(TM)、本地事务状态(RM)

• 根据事务状态信息的异常处理策略

毫无疑问事务状态信息不丢失,是实现安全2PC的基础,由于本地事务状态信息可以通过MySQL高可用集群解决,所以我们仅需要:保证全局事务状态(TM)不丢失。

分布式事务的工程实践

在给出UP-2PC的方案之前,我们可以先了解下已有的分布式事务工程实践经验,“Distributed transactions in Spring, with and without XA”(Spring的分布式事务,使用或不用XA;Spring分布式事务的7种实现)是一篇不错的文章,很好总结了既有实践经验,这里仅对其中涉及XA的两种优化方案进行说明:

• 一阶段提交优化:如果全局事务只涉及一个RM,则不使用两阶段

• 最后参与者优化(Last Participant Support):选择一个RM(最后参与者)不使用两阶段提交,全局事务提交时,先对”最后参与者”进行(一阶段)提交.该策略又被称为:最后资源提交优化(Last Resource Commit Optimization)。

图例:最后参与者优化

乍看起来“最后参与者优化”方案效果有限,但事实上该方案直接指明了UP-2PC的实现路径,因为:

1.“一阶段提交优化”可以视为“最后参与者优化”的特例,即只全局事务只包含一个RM时两者等效

2.“最后参与者优化”指出了最后参与者在所有RM中最为重要,其事务提交结果决定着全局事务的结果,而前面也提到了全局事务状态是最重要的,所以我们自然推导出应该由最后参与者记录全局事务状态(我们称为xa-log)

UP-2PC核心方案

UP-2PC核心方案,包含3点:

1.利用RM节点分布式存全局事务状态信息(xa-log)

2.使用最后参与者协议

3.由最后参与者记录全局事务状态信息(xa-log)

xa-log中记录了全局事务id所涉及的所有资源管理器节点列表,和对应的本地xid,以及全局事务状态(commit|rollback)。

这里仅对全局事务需要进行commit做下说明,如果最后参与者commit成功,则认为全局事务commit成功,若最后参与者commit成功而其他资源管理器异常,则交由异常处理程序轮询xa-log表完成其他资源管理器的xa commit操作。

最后参与者的选择策略:如果第一个本地事务参与者如果发生了写操作,则选择其为最后参与者;否则在其他发生了写操作的参与中随机选择。该选择策略实现了最后参与者的分布式随机选择,进而保证xa-log的分布式随机分布。

图例:UP-2PC实现

如图所示,UP-2PC的实现要点在于:

1.全局事务提交,则由最后参与者记录xa-log

2.最后参与者提交前,需要保证其他参与则处于prepare状态

3.在所有参与者事务提交结束后,删除xa-log

UP-2PC异常处理

在确保全局事务状态(xa-log)安全保存后,就可以针对2PC的各种异常进行处理。

不考虑TM异常,将异常按时间线进行区分:

1.全局事务commit之前:因为没有执行本地xa和xa-log的相关操作,则按事务提交失败处理

2.全局事务执行commit阶段:

– xa-log记录与prepare阶段异常:则回滚所有本地事务

– 最后参与者commit异常:则查询xa-log,如xa-log有全局事务的com mit记录,则视为事务提交成功;没有则视为提交失败;查询结果不确定 (如最后参与者宕机等),则事务状态不确定,可以考虑断开与AP连接(即不应答AP事务状态), 或保持连接直至得到查询结果未知。

– 其他参与者commit异常:依然应答全局事务成功,交由后续异步commit。

其他异常及异常处理: 以xa-log记录为准,对比xa-log与RM的xa-id信息进行对比分析进行提交或回滚操作。

需要指出的是由于TM是集群多机部署,所以xaid在编码时需要能区分不同TM节点信息。

与TDSQL分布式事务实现的区别

在已公开的同类产品中TDSQL与UP-2PC的解决思路最为接近,这里做下简单对比。

图例:TDSQL与UP-2PC对比

注:示例来自TDSQL的公开介绍材料,为简化描述略作修改

UP-2PC相对TDSQL的分布式事务解决方案主要区别在于UP-2PC采用了最后参与者策略,由最后参与者进行xa-log记录。

优点:减少启用一个数据库会话、一次网络交互、减少一个事务、退化一个2PC事务为一阶段

缺点:业务数据库用户需要具有xa-log的读写权限,因而具有运维风险,特别是在复用数据库实例的情况下

一次进一步展开思考,TDSQL不使用最后参与者策略的理由可能如下:

• 作为云上产品,应尽可能避免运维风险

• XA业务场景占比较少,性能影响有限

• 方便将来xa-log由其他方式存储

UP-2PC性能优化

在实现UP-2PC方案后,XA分布式事务的性能影响大约为30%(依赖场景),虽然符合预期,但也说明需要进一步优化。

这里给出UP-2PC两个性能优化方案:

• 本地事务启动优化

• MySQL XA协议优化

本地事务启动优化

对于属于不可重复读的全局事务,我们可以延迟开启本地事务以减少资源锁定时间。

本地事务启动优化的核心策略:

1.全局事务为不可重复读

2.本地事务只有发生了写才需要开启本地XA事务

针对如下业务场景:

/*事务隔离级别为read-commited或read-uncommited*/
Begin;
select db1 and db2; //1st
update db1;
update db2;
select db1 and db2; //2nd
Commit;

我们推演下全局事务管理器对上述语句的处理逻辑:

1.当第一次执行查询操作时,由于db1和db2都未发生写操作,则不必开启本地事务,在查询结束后可以将本地事务会话归还连接池。

2.在遇到第一次写入操作时,分别开启对应的本地事务管理器。

3.在后续查询时,由于已持有本地事务连接,则使用该连接进行查询操作。

而实际的场景中,可能操作如下:

Begin;
select db1 and db2; 
update db1;
select db1 and db2;
Commit;

即全局事务内,查询的节点数量多于更新的节点数量,通过本方案只会对写入数据库节点开启本地XA事务,从而避免了不必要的事务开启操作。

MySQL XA协议优化

在实现UP-2PC的过程中我们一直有这样一个设计期望:在全局事务只涉及一个本地事务,我们能否对该事务不启用xa协议?

在我们实现“本地事务启动优化”后,这个问题视乎解决了,因为让最后参与者不使用XA协议就可以轻松实现了上述目标,但最后参与者不使用XA协议可能会导致我们无法解决后面提及的分布式死锁问题。

于是我们转换思路,对我们的设计目标的本质进行剖析,即XA一阶段提交与传统事务提交有什么区别?

答案:多了一个xa end。

于是XA协议优化方案也就呼之欲出,即省略掉不必须要的XA END。

图例:UP-2PC优化方案

如图所示,通过减少了xa end阶段,减少了交互次数自然提高了系统健壮性,也提高了性能。

分布式事务带来的新问题

在实现了基于2PC的分布式事务,对外提供统一事务管理特征,也会引入一些新问题,例如:

• 分布式事务死锁

• 对外XA协议支持(外部2PC)

• Savepoint支持

这里仅对部分解决方案进行说明。

分布式事务死锁

由于分布式事务管理多个RM,所以必然会带来分布式资源竞争和分布式死锁。例如:

• 会话a 和 b 开启分布式事务

• time 1: a   write db1.resource1

• time 2: b   write db2.resource2

• time 3: a   write db2.resource2 -> 锁:a 等待b的 db2.resource2

• time 4: b   write db1.resource1 -> 锁:b 等待a的 db1.resource1

在时间点4, a和b发生了资源循环等待,从而出现了分布式死锁。

如何解决分布式死锁呢,这里提供一种死锁检测方法:

• 扩展innodb_trx表,增加事务xid的记录

• TM层确保同一全局事务所对应的本地事务xid相同

这样就可以对所有全局事务的锁信息进行分析,并发现分布式死锁。

下表为扩展innodb_trx的效果,由于代码修改较为简单这里就不累述。

另一种方法是在本地事务中加入事务时间信息,在RM层根据时间先后顺序,保证只会存在单一时序的资源等待,从而避免死锁发生。

对外XA协议支持(外部2PC)

如果我们将应用UP-2PC的数据库集群视为一个整体,则实现分布式事务的过程为内部2PC,对外部应用提供2PC协议支持(即支持XA协议),则称为外部2PC。数据集群的内部外部2PC,可以与MySQL的内部外部2PC对应理解。

该逻辑较为简单,即在外部执行XA PREPARE时,我们在xa-log中记录全局事务状态为Prepare即可,需要指出的是:外部2PC下,内部的分布式事务无法使用前述的最后参与者优化方案。

图例:外部两XA协议实现

总结

本文介绍了UP-2PC的具体实现,涵盖了核心解决方案与异常处理策略、性能优化、带来的新问题与解决方案。

基于两阶段提交的分布式事务实现(UP-2PC)的更多相关文章

  1. 关于分布式事务、两阶段提交、一阶段提交、Best Efforts 1PC模式和事务补偿机制的研究 转载

    1.XA XA是由X/Open组织提出的分布式事务的规范.XA规范主要定义了(全局)事务管理器(Transaction Manager)和(局部)资源管理器(Resource Manager)之间的接 ...

  2. 分布式事务、XA、两阶段提交、一阶段提交

    本文原文连接:http://blog.csdn.net/bluishglc/article/details/7612811 ,转载请注明出处! 1.XA XA是由X/Open组织提出的分布式事务的规范 ...

  3. XA: 事务和两阶段提交

    本文原文连接:http://blog.csdn.net/bluishglc/article/details/7612811 ,转载请注明出处! 1.XA XA是由X/Open组织提出的两阶段提交协议, ...

  4. 全网最牛X的!!! MySQL两阶段提交串讲

    目录 一.吹个牛 二.事务及它的特性 三.简单看下两阶段提交的流程 四.两阶段写日志用意? 五.加餐:sync_binlog = 1 问题 六.如何判断binlog和redolog是否达成了一致 七. ...

  5. 分布式事务(一)两阶段提交及JTA

    原创文章,同步发自作者个人博客 http://www.jasongj.com/big_data/two_phase_commit/ 分布式事务 分布式事务简介 分布式事务是指会涉及到操作多个数据库(或 ...

  6. 分布式事务专题笔记(二)分布式事务解决方案之 2PC(两阶段提交)

    个人博客网:https://wushaopei.github.io/    (你想要这里多有) 前面已经了解了分布式事务的基础理论,以理论为基础,针对不同的分布式场景业界常见的解决方案有2PC.TCC ...

  7. MySQL binlog 组提交与 XA(分布式事务、两阶段提交)【转】

    概念: XA(分布式事务)规范主要定义了(全局)事务管理器(TM: Transaction Manager)和(局部)资源管理器(RM: Resource Manager)之间的接口.XA为了实现分布 ...

  8. 分布式事务 & 两阶段提交 & 三阶段提交

    可以参考这篇文章: http://blog.csdn.net/whycold/article/details/47702133 两阶段提交保证了分布式事务的原子性,这些子事务要么都做,要么都不做. 而 ...

  9. 分布式事务 spring 两阶段提交 tcc

    请问分布式事务一致性与raft或paxos协议解决的一致性问题是同一回事吗? - 知乎 https://www.zhihu.com/question/275845393 分布式事务11_TCC 两阶段 ...

随机推荐

  1. Golang 之 key-value LevelDB

    时常会在应用中用到数据库功能,象 Key-Value 性质的.直接搬个 Redis,Mysql嫌大,好在有 LevelDB,直接编进应用中. 有关什么是 LevelDB 以及 LevelDB 的特性, ...

  2. java8 Lambda表达式的10个例子(转)

    原文:http://jobar.iteye.com/blog/2023477 Java8中Lambda表达式的10个例子 例1 用Lambda表达式实现Runnable接口 Java代码 收藏代码// ...

  3. bootstrap-海棠

    12 缩略图和警告框 <p class='alert alert-info'>这个是警告组<button class='close' data-dismiss='alert'> ...

  4. centOS下JDK1.8的安装

    1.下载JDK1.8 http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html 有两种格式 ...

  5. Android 内存优化[转]

    一. Android的内存机制 Android的程序由Java语言编写,所以Android的内存管理与Java的内存管理相似.程序员通过new为对象分配内存,所有对象在java堆内分配空间:然而对象的 ...

  6. sqlplus 简单的几个查询语句

    sqlplus 是用来和oracle进行交互的工具,可以在电脑端使用,也可以在服务器使用 常用命令    show        显示SQL Plus中的信息 connect        先无条件断 ...

  7. mysql 数据库查询

    myspl   三部分  文件  服务  界面 查看数据库命令   show databases 创建数据库  create database  库名 删除数据库   drop database   ...

  8. Java NIO学习-详细内容(一)

    一.三大类 1.Channels FileChannel DatagramChannel SocketChannel ServerSocketChannel 2.Selector与SelectionK ...

  9. EDM模板制作规范

    为了保证最大的兼容性,在制作HTML的email页面时,请严格按照规范来书写: 1.页面宽度推荐500px,最大不要超过750px: 2.制作HTML的email页面时,不使用css+div来布局,最 ...

  10. 在Linux下启动Java服务的脚本

    #!/bin/sh #该脚本为Linux下启动java程序的通用脚本.即可以作为开机自启动service脚本被调用, #也可以作为启动java程序的独立脚本来使用. # #Author: tudaxi ...