全局事务服务(Global Transaction Service,简称 GTS)是阿里新推出的分布式事务处理方案。
1. GTS 的目标
GTS是一个面向互联网交易场景的分布式事务解决方案。
制约分布式事务的三个因素
分布式事务是互联网交易场景面临的关键问题之一。不同于搜索、社交、联机分析应用,电子商务、支付是典型的交易场景,数据的错误会带来严重的后果,对数据的一致性与可用性有很高的要求。互联网环境带来了海量的数据容量、连接数与访问量,单一数据库节点无法应对,成为整个系统的瓶颈。为解决单一数据库成为瓶颈的问题,通过数据拆分实现数据库能力的线性扩展。数据拆分是使用分库分表的方式,将数据存储在多个数据库节点,利用分布式数据库平台解决数据库瓶颈的问题。分布式数据库环境中,一个事务会跨越多个数据库,面临分布式事务处理的问题。
分布式事务解决方案面临应用灵活性、数据一致性、性能三者的挑战。目前已有多种成熟方案,每种方案都是对这三个方面做出的取舍。
相互制约的三个因素为:
应用灵活性:应用访问数据的方式是否需要修改,以及修改的程度。
一致性:数据是强一致,还是最终一致的(允许中间不一致的状态)。
系统性能:分布式事务对整体性能的影响。
现有分布式处理方案
现有成熟的分布式解决方案包括XA两阶段提交、可靠消息与TCC模式等类型。XA两阶段提交属于强一致事务,可靠消息与TCC模式属于柔性事务。
XA两阶段提交
XA 是指由 X/Open 组织提出的分布式事务处理的规范。XA规范主要定义了Transaction Manager(TM)和Resource Manager(RM)之间的接口,结构如下图所示。
XA协议的流程可大致分为三个步骤:
步骤1:APP向TM创建全局事务,TM向APP返回全局事务号。
步骤2:APP使用全局事务号,访问RM的资源(当RM为数据库时,资源访问就是SQL操作)。当RM第一次收到访问时,使用该全局事务号向TM注册,TM返回事务分支事务号。
步骤3:APP向TM发出全局事务提交请求,TM与参与事务的RM通信,进行提交处理,全部完成后,向APP返回结果。
TM与RM之间的提交处理,采用两阶段提交协议。TM在第一阶段对所有的参与事务的RM请求“预备”操作,达成关于分布式事务一致性的共识。事务参与者必须完成所有的约束检查,并且确保后续提交或放弃时所需要的数据已持久化。在第二阶段,根据之前达到的提交或放弃的共识,请求所有参事务的RM完成相应的操作。
提交事务的过程中需要在多个资源节点之间进行协调,而各节点对锁资源的释放必须等到事务最终提交时,所以两阶段提交在执行同样的事务时会比一阶段提交消耗更多的时间。当事务并发量达到一定数量时,就会出现大量事务积压甚至出现死锁,系统性能和处理吞吐量就会严重下滑。
可靠消息
可靠消息的一种可能实现的结构如下图。
说明:
业务处理服务在业务事务提交前,向实时消息服务请求发送消息,实时消息服务只记录消息数据,而不真正发送。
业务处理服务在业务事务提交后,向实时消息服务确认发送。只有在得到确认发送指令后,实时消息服务才真正发送消息。
业务处理服务在业务事务回滚后,向实时消息服务取消发送。
消息状态确认系统定期找到未确认发送或回滚发送的消息,向业务处理服务询问消息状态,业务处理服务根据消息ID或消息内容确定该消息是否有效。
通过消息进行事务异步的方式,可以保证业务数据操作和消息的发送同时执行成功或失败,保持了事务的最终一致性。
采用可靠消息的方式,在两个事务间实现分布式事务时,可以很好地满足事务最终一致性以及事务的回滚,但如果一个事务上下文中超过两个事务操作后,需要开发人员实现整个事务流程的操作日志的记录、每个事务分支的回滚以及整个流程的准确调度。
TCC模式
TCC模式为全局事务执行提供了一个框架,开发人员只需要实现每个事务分支的回滚,不需要记录整个事务流程的操作日志。TCC模式结构如下图。
说明:
一个完整的业务活动由一个主业务服务与若干从业务服务组成。
主业务服务负责发起并完成整个业务活动。
从业务服务提供TCC型业务操作。
业务活动管理器控制业务活动的一致性,它登记业务活动中的操作,并在业务活动提交时确认所有的TCC型操作的confirm操作,在业务活动取消时调用所有TCC型操作的cancel操作。
TCC业务包括两个阶段完成:
第一阶段:主业务服务分别调用所有从业务的 try 操作,并在活动管理器中登记所有从业务服务。当所有从业务服务的 try 操作都调用成功或者某个从业务服务的 try 操作失败,进入第二阶段。
第二阶段:活动管理器根据第一阶段的执行结果来执行 confirm 或 cancel 操作。如果第一阶段所有 try 操作都成功,则活动管理器调用所有从业务活动的 confirm操作。否则调用所有从业务服务的 cancel 操作。
小结
可靠消息与TCC模式通过避免XA两阶段提交对数据资源的长期锁定提升了性能,通过在数据库外部实现事务机制达到了最终一致性,但牺牲了应用灵活性,需要开发人员实现事务检查与回滚的细节,面临着花费大量精力保证应用正确性的问题。
GTS目标是在性能开销可接受的情况下,由GTS统一处理全局事务的故障恢复与并发控制,对应用开发屏蔽事务处理的细节,从而提升应用的灵活性与数据的一致性。
2. GTS的 技术路线
GTS采用基于XA架构优化的技术路线,在保留XA架构灵活性的优点下,通过将XA提交中的第一阶段与第二阶段解耦,将提交过程转换为第一阶段本地事务提交+第二阶段异步清理的方式,从而提供提升系统性能,同时通过在GTS内部维护应用级别的日志与锁信息,实现了全局事务的回滚与并发控制。
GTS方案认为XA性能低效的根本原因是采用了阻塞协议。在分布式事务提交的第一阶段等待最慢的一个事务分支完成,即使在不存在锁冲突的情况下,各事务分支的数据库连接依然会被挂起所占用的资源都不能够释放,以防止全局事务提交前释放资源所造成的数据不一致。对于业务流量极高的大规模互联网企业,难以接受 XA 两阶段提交协议所带来的巨大性能开销。
GTS架构包含的组件与XA完全相同,示意架构如下图。
GTS全局事务处理流程与XA一致,也包括全局事务注册、数据访问与全局事务提交三个步骤,但在第二步与第三步的内部处理上与XA不同:
第二步数据访问中,各事务分支完成数据操作的同时,会将全局事务信息(锁与日志信息)存储在当前数据库的表中。
第三步全局事务提交中,采用一阶段本地事务提交+二阶段异步清理的方式。首先对各数据库做本地事务的提交,并释放数据库连接等系统资源,然后,向TM发出全局事务提交请求,TM收到请求后,立即返回成功,TM后续实际工作是对各个数据库使用全局事务标识符进行全局事务信息的清理。
GTS与XA在全局事务的故障恢复处理与并发控制采用了不同的实现机制:
XA两阶段协议是基于数据库内核的日志与锁信息实现全局事务的回滚与并发控制。由于GTS一阶段本地事务提交中,会直接提交本地事务并释放连接,此时数据库内核的日志与锁表对全局事务不再有效。在第二步中,GTS会将日志和锁信息存储在表中,当事务本地提交后,日志和锁信息被持久化保存,用于实现全局事务的并发控制与故障恢复。
GTS的故障恢复只有UNDO操作没有REDO操作,日志表中存储了UNDO需要的信息,包括行记录标识、全局事务号、镜像查询语句、操作的前像与操作的后像。当发生故障时,对于已经本地提交的数据库,从UNDO表中找到修改的记录,记录的操作前像和操作后像,使用镜像查询语句从数据库中读取该记录的当前值。如果当前值与记录操作后像相同,则直接使用操作前像进行恢复,否则报警,进行人工处理。
GTS的全局锁表中存储了记录的加锁信息。封锁的粒度是行(记录),锁的类型包括共享锁和互斥锁,对于同一个记录,加锁的规则是共享锁与共享锁不冲突,共享锁与互斥锁冲突、互斥锁与互斥锁冲突。对插入(INSERT)、修改(UPDATE)、删除(DELETE)、更新模式的锁定查询(SELECT… FOR UPDATE) 操作加互斥锁。对于共享模式的锁定查询 (SELECT…LOCK IN SHARE MODE) 操作加共享锁。若没有锁冲突,在GTS锁表中,增加一行记录,表示加锁成功。
GTS的默认隔离级别为读未提交(脏数据),使用SELECT… FOR UPDATE和SELECT…LOCK IN SHARE MODE,可使查询隔离级别提升至读已提交。
3. GTS 的架构与处理流程
架构
下图描述了GTS一种可能的实现架构。
与XA架构相同,GTS架构由应用、事务管理器、资源管理器三个部分组成。资源管理器由事务分支处理模块、镜像查询构造模块、并发控制模块、恢复控制模块,以及存储在数据库中的GTS事务信息(GTS锁表与GTS日志表)等组成。
事务分支处理模块:是资源管理器的外部接口,并完成内部各模块的调用。
镜像查询构造模块:从Insert、Update、Delete语句,生成该操作对应记录集的镜像查询语句。例如table_name表包含两个字段column1和column2,column1为主键,则镜像查询语句为select column1, column2 from table_name where column1=v1。
并发控制模块:基于GTS事务锁表,维护读写并发控制。锁表定义如下:
恢复控制模块:基于GTS日志表,进行故障恢复。 日志表定义如下:
主要流程序列图
分别描述了insert/delete/update操作、读已提交操作、提交操作和回滚操作等四个操作的序列图(一种可能的实现方式)。
insert/delete/update操作流程序列图
读已提交操作流程序列图
提交操作流程序列图
回滚操作流程序列图
阿里官方案例
GTS产品网站给出了一个交易类事务中最典型的
A和B两个用户的数据分别位于一个DRDS实例的两个不同分库中,用50个进程并发进行 A转账给3,每个进程转账10次,每次转账金额在1到10之间随机生成,转账过程中模拟了3%的网络异常,使用GTS事务保证了A和B钱的总数不变。
从代码上可看出,只需增加一条开启GTS的sql语句,就将单机事务应用提升至分布式事务,体现出很好的应用灵活性。测试中转账事务执行500次,成功490次,失败10次。转账结束10秒后,查询账户金额总数正确。
2017云栖大会 GTS产品介绍中,给出了使用GTS与不使用事务(1PC)。下图,GTS比1PC的性能损耗在10%,远远小于2PC方式,表现出优异的性能。
4. GTS 的优势与约束
与基于消息队列与TCC补偿模式的分布式事务相比,在性能满足的情况下,GTS更好的应用灵活性与数据一致性:
灵活性:数据库应用基本实现零修改,同时,基于XA模型,可方便的支持消息队列数据库等多种RM。
数据一致性:GTS 的缺省事务隔离级别为读未提交,该模式下可以达到分布式事务的最大性能,但可能会读到脏数据。对于一致性要求高的应用,在性能允许的情况下,可以采用已提交读语句(for update、lock in share mode)将隔离级别提升至读已提交。
根据GTS实现机制的特点,其应用场景上有以下约束:加锁操作记录数量不能太大,操作冲突不能太多,加锁时间不能太长。违法以上约束时,GTS内部会占用过多资源、锁冲突和回滚增加,导致性能的下降。电商、物流、金融、零售行业中的核心交易场景有着高并发,高性能,单次操作数据集小,事务响应时间敏感的特点,GTS类方案在此类场景中有着广泛和良好的应用前景。
参考书及链接
《企业IT架构转型之道》第六章
CN201510111872-一种分布式事务处理方法及系统
CN201510111986-一种分布式事务处理方法及系统
- (转)OpenStack —— 原理架构介绍(一、二)
原文:http://blog.51cto.com/wzlinux/1961337 http://blog.51cto.com/wzlinux/category18.html-------------O ...
- 这可能是目前最透彻的Netty原理架构解析
https://juejin.im/post/5be00763e51d453d4a5cf289 本文基于 Netty 4.1 展开介绍相关理论模型,使用场景,基本组件.整体架构,知其然且知其所以然,希 ...
- Netty原理架构解析
Netty原理架构解析 转载自:http://www.sohu.com/a/272879207_463994本文转载关于Netty的原理架构解析,方便之后巩固复习 Netty是一个异步事件驱动的网络应 ...
- 【Netty】最透彻的Netty原理架构解析
这可能是目前最透彻的Netty原理架构解析 本文基于 Netty 4.1 展开介绍相关理论模型,使用场景,基本组件.整体架构,知其然且知其所以然,希望给大家在实际开发实践.学习开源项目方面提供参考. ...
- Nodejs的运行原理-架构篇
前言 本来是想只做一个Nodejs运行原理-科普篇,但是收到了不少私信,要我多分享一些更进阶,更详细的内容,所以我会在接下来的两个月里继续更新Nodejs运行原理. PS:此系列只做Nodejs的运行 ...
- MySQL运行机制原理&架构
1.MySQL知识普及: MySQL是一个开放源代码的关系数据库管理系统. MySQL架构可以在多种不同场景中应用并发挥良好作用.主要体现在存储引擎的架构上,插件式的存储引擎架构将查询处理和其它的系统 ...
- Canal——原理架构及应用场景
Canal简介 Canal是阿里开源的一款基于Mysql数据库binlog的增量订阅和消费组件,通过它可以订阅数据库的binlog日志,然后进行一些数据消费,如数据镜像.数据异构.数据索引.缓存更新等 ...
- Ceph介绍及原理架构分享
https://www.jianshu.com/p/cc3ece850433 1. Ceph架构简介及使用场景介绍 1.1 Ceph简介 Ceph是一个统一的分布式存储系统,设计初衷是提供较好的性能. ...
- MyBatis原理-架构流程
一 .MyBatis原理架构图 Mybatis的功能架构分为三层: API接口层:提供给外部使用的接口API,开发人员通过这些本地API来操纵数据库.接口层一接收到调用请求就会调用数据处理层来完成具体 ...
随机推荐
- C/C++数据类型判断与转换
最近总想着写一些通用的代码,然集中收纳到自己的私人库中去,这些代码期望能与公司基础数据结构无关.然而这比较难,因为无论如何,必需要用到一些结构 化的东西,这些与基础引擎等有关,必需极度抽象才可以做到层 ...
- vuex中mapState、mapMutations、mapAction的理解
当一个组件需要获取多个状态时候,将这些状态都声明为计算属性会有些重复和冗余.为了解决这个问题,我们可以使用 mapState 辅助函数帮助我们生成计算属性. // 在单独构建的版本中辅助函数为 Vue ...
- jmeter测试报告添加报告生成日期时间
<!-- Defined parameters (overrideable) --><xsl:param name="showData" select=" ...
- PHP基础回顾
变量:声明变量以$开始,变量名可以用中文但是不要用:不能使用特殊字符,比如@,下划线除外:变量名区分大小写.可变变量 $a = b; $b = c; => $$a = c; 理论上$可以无限使 ...
- C语言 - strcmp和strncmp的编程实现及总结
一.strcmp和strncmp的编程实现及总结 1.strcmp函数的实现 要求: 原型: int strcmp(char *dest,char * src,int n); 头文件:# ...
- RocketMQ消息发送流程和高可用设计
(源码阅读先看主线 再看支线 先点到为止 后面再详细分解) 高可用的设计就是:当producer发送消息到broker上,broker却宕机,那下一次发送如何避免发送到这个broker上,就是采用La ...
- Maven中的dependency的scope作用域
1.test范围指的是测试范围有效,在编译和打包时都不会使用这个依赖 2.compile范围指的是编译范围有效,在编译和打包时都会将依赖存储进去 3.provided依赖:在编译和测试的过程有效,最后 ...
- 第七周课程总结 & 实验报告(五)
第七周课程总结 一.抽象类与接口的应用 1.实例化 2.实际应用 ---模板设计(抽象类) ---制定标准(接口) 3.设计模式 ---工厂设计 ---代理设计 ---适配器设计 二.抽象类与接口之间 ...
- LeetCode 22. 括号生成(Generate Parentheses)
题目描述 给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合. 例如,给出 n =3,生成结果为: [ "((()))", "(() ...
- [Java]分解算术表达式一
源码: package com.hy; import java.io.BufferedReader; import java.io.IOException; import java.io.InputS ...