一 前言
Hybrid Transaction Analytical Processing(HTAP) 是著名信息技术咨询与分析公司Gartner在2014年提出的一个新的数据库系统定义,特指一类兼具OLTP能力(事务能力)和OLAP能力(分析能力)的数据库系统。在传统场景中,承担OLTP任务和OLAP任务的数据库是两个不同的系统。典型的OLTP系统包括MySQL、PostgreSQL、PolarDB等,典型的OLAP系统包括Clickhouse、AnalyticDB等。在生产系统中,业务原始数据通常存储在OLTP系统中,然后通过离线导入、ETL、DTS等方式以一定延迟同步到OLAP系统中,再进行后续的数据分析工作。
HTAP系统的一个直观的优点是可以在一个系统中完成OLTP和OLAP任务,节约用户的系统使用成本。而且,HTAP系统具备完整的ACID能力,让开发者拥有更多的数据写入方式,不管是实时插入、离线导入、数据单条更新,都可以轻松应对。另外,一个完备的HTAP产品,同样是一个优秀的ETL工具,开发者可以利用HTAP系统处理常见的数据加工需求。HTAP系统能够大大节约用户的使用成本和开发成本,并影响上层业务系统的形态。目前,存储计算分离、云原生技术和HTAP等技术,被业界公认为是数据库系统目前的重要演进方向。
AnalyticDB PostgreSQL版是阿里云的一款实时数仓产品(以下简称ADB PG)。ADB PG采用MPP水平扩展架构,支持标准SQL 2003,兼容PostgreSQL/Greenplum,高度兼容 Oracle 语法生态,也是一款HTAP产品。ADB PG已经通过了中国信息通信研究院组织的分布式分析型数据库和分布式事务数据库功能和性能认证,是国内唯一一家同时通过这两项认证的数据库产品。ADB PG早期版本主打OLAP场景、具备OLTP能力。随着HTAP的流行,ADB PG自6.0版本开始对OLTP性能在多个方面进行了大幅度优化,其中很重要的一个项目就是Multi-Master项目,通过Scale Out打破了原有架构的仅支持单个Master节点带来的性能瓶颈问题,让OLTP事务性能具备Scale out能力,更好地满足用户的实时数仓和HTAP需求。
Multi-Master项目在2019年启动后,经历了一写多读和多写多读2个演进阶段,极大的提升了ADB PG系统高并发能力、实时写入/更新/查询的能力,在阿里内部支撑了如数据银行等多个核心业务,也经过了阿里2020年双11、双12等大促的考验。目前,产品的稳定性和性能都已经得到了广泛验证。在本文的如下部分,我们首先介绍ADB PG原有的Single-Master架构导致的性能瓶颈及其原因,并介绍Multi-Master的设计思路。然后我们会详细介绍Multi-Master架构的详细设计。之后我们会介绍我们在Multi-Master项目中所解决的几个关键技术问题和核心解决方案。最后,我们会对Multi-Master架构的性能表现进行测试。
二 Single-Master架构 vs. Multi-Master架构
在数仓系统设计中,通常把系统中的节点分为Master节点和Segment节点(计算节点),Master节点和计算节点承担不同类型的任务。以ADB PG为例,Master节点主要负责接收用户的请求、查询优化、任务分发、元信息管理和事务管理等任务。Segment节点负责计算任务和存储管理任务。对于查询请求,Master节点需要对用户提交的SQL进行解析和优化,然后将优化后的执行计划分发到计算节点。计算节点需要对本地存储的数据进行读取,然后再完成计算和数据shuffle等任务,最后计算节点把计算结果返回到Master节点进行汇总。对于建表、写入等请求,Master节点需要对元信息、事务等进行管理,并协调计算节点之间的工作。
如上图所示,ADB PG是由Greenplum演化而来,早期的ADB PG版本和Greenplum一样,是一种单Master架构。也就是说,一个数据库实例只有一个Main Master在工作,配置一个或者多个Standby Master节点作为高可用备份,只有当Main Master节点宕机,才会切换到Standby Master进行工作。随着业务的发展,尤其是实时数仓和HTAP场景需求的增加, Single Master的系统瓶颈问题也逐渐显现。对于查询链路,有些查询的最后一个阶段需要在Master节点上进行最终的数据处理,消耗一定的CPU/内存资源。对于写入场景,大量的实时插入/更新/删除的需要高性能保证。而且Single Master架构如何处理超大并发连接数也是个问题。以上问题可以通过提高Master节点的配置(Scale up)来缓解,但是无法从根本上解决。
ADB PG在2019年启动了Multi-Master项目,目标是通过节点扩展(Scale out)的方式来解决Master层的资源瓶颈问题,更好地满足实时数仓及HTAP等业务场景的需求。上图是Multi-master架构的示意图,通过增加多个Secondary Master节点来实现性能的Scale out,同时保留原有的Standby Master来保证高可用能力。为了保障ADB PG的事务能力,Multi-master项目需要克服一些其他不支持事务的实时数仓不会遇到的困难。一方面,ADB PG需要对分布式事务能力进行扩展,支持多个Master的场景。一方面,对于全局死锁处理、DDL支持以及分布式表锁支持方面,ADB PG需要进行算法的创新和修改。最后,ADB PG需要对更新之后的新架构的集群容错能力和高可用能力进行设计。在本文的余下部分,我们将对上述几个议题进行介绍。
三 Multi-Master 架构设计
相对于原Single-Master架构,Multi-Master架构在Main Master/Standby Master的基础之上新增实现了Secondary Master的角色,Secondary Master(s)支持承接和Main Master一样的DDL,DML等请求,同时用户可以按需扩展来提升系统整体能力。下面是各个Master角色及对应主要能力的简单介绍。
Main Master:承接用户业务请求,并把任务分发到各个计算节点进行分布式处理。除此之外,Main Master还承担了GTM,FTS和全局元信息服务的角色,这些组件与Multi-Master的实现密切相关。
- GTM:全局事务管理(Global Transaction Manager),维护了全局的事务id及快照信息,是实现分布式事务的核心组件。
- FTS:容错服务(Fault-Tolerance Service), 检测计算节点及辅协调节点的健康状态,并在计算节点发生故障时进行计算节点的Primary与Mirror角色的切换。
- Catalog:以系统表Catalog等信息为代表的全局元信息存储。
- Standby Master:和Main Master组成典型的主备关系,在原Main Master故障的时候可以接替成为新的Main Master。
- Secondary Master:可以视为"弱化的Main Master",和Main Master一样可以承接业务请求并将任务分发到各个计算节点进行处理。Secondary Master会通过GTM Proxy与Main Master上的GTM以及计算节点交互来实现分布式事务。
需要注意的是,Main Master与Secondary Master通过上层的SLB来做基于权重的负载均衡管理。如果是在Main Master和Secondary Master相同的规格配置下,Main Master会通过权重设置来承担相对少的业务请求负载,从而为GTM,FTS等预留足够的处理能力。
四 Multi-Master关键技术
本章将对Multi-Master的一些关键技术点进行详细的介绍,主要包括分布式事务处理、全局死锁处理、DDL支持、分布式表锁支持、集群容错和高可用能力。
1 分布式事务管理
ADB PG的分布式事务实现
ADB PG的分布式事务是使用二阶段提交(2PC)协议来实现的,同时使用了分布式快照来保证Master和不同Segment间的数据一致性,具体实现实现要点如下。
分布式事务由Main Master发起,并通过2PC协议提交到Segments。2PC是分布式系统的经典协议,将整体事务的提交过程拆分成了Prepare和Commit/Abort两个阶段,如上面的简单示意图所示,只有参与事务的所有Segments都成功提交整体事务才会成功提交。如果在第一阶段有存在Prepare失败的Segment,则整体事务会Abort掉;如果在第二阶段有Commit失败的Segment,而且Master已经成功记录了PREPARED日志,则会发起重试来Retry失败的Commits。需要说明的是,如果一个事务仅仅牵涉到1个Segment,系统会优化为按照1PC的方式来提交事务从而提升性能,具体来说就是将上图中Master参与协调的Prepare和Commit两个阶段合二为一,最终由唯一参与的Segment来保证事务执行的原子性。
Main Master上的GTM全局事务管理组件会维护全局的分布式事务状态,每一个事务都会产生一个新的分布式事务id、设置时间戳及相应的状态信息,在获取快照时,创建分布式快照并保存在当前快照中。如下是分布式快照记录的核心信息:
执行查询时,Main Master将分布式事务和快照等信息序列化,通过libpq协议发送给Segment上来执行。Segment反序列化后,获得对应分布式事务和快照信息,并以此为依据来判定查询到的元组的可见性。所有参与该查询的Segments都使用同一份分布式事务和快照信息判断元组的可见性,因而保证了整个集群数据的一致性。另外,和 PostgreSQL 的提交日志clog类似,ADB PG会保存全局事务的提交日志,以判断某个事务是否已经提交。这些信息保存在共享内存中并持久化存储在distributedlog目录下。另外,ADB PG实现了本地事务-分布式事务提交缓存来帮助快速查到本地事务id(xid)和分布式全局事务id(gxid)的映射关系。下面让我们通过一个例子来具体理解一下:
如上图所示,Txn A在插入一条数据后,Txn B对该数据进行了更新。基于PostgreSQL的MVCC机制,当前Heap表中会存在两条对应的记录,Txn B更新完数据后会将原来tuple对应的xmax改为自身的本地xid值(由0改为4)。此后,Txn C和Txn D两个查询会结合自己的分布式快照信息来做可见性判断,具体规则是:
- 如果 gxid < distribedSnapshot->xmin,则元组可见
- 如果 gxid > distribedSnapshot->xmax,则元组不可见
- 如果 distribedSnapshot->inProgressXidArray 包含 gxid,则元组不可见
- 否则元组可见。如果不能根据分布式快照判断可见性,或者不需要根据分布式快照判断可见性,则使用本地快照信息判断,这个逻辑和PostgreSQL的判断可见性逻辑一样。
基于上述规则,Txn C查到两条tuple记录后,通过xid和gxid映射关系找到两条记录对应的gxid值(分别为100, 105),规则c会限定Txn B的更新对Txn C不可见,所以Txn C查询到的结果是'foo';而Txn D基于规则则对Txn B更新后的tuple可见,所以查询到的是'bar'。
Multi-Master的分布式事务实现
Multi-Master的分布式事务本质是在原有分布式事务基础之上进行了增强。如上图所示,Postmaster是守护进程,Main Master的Backend业务处理进程和GTM Server之间通过共享内存通信,但Secondary Master是无法直接通过共享内存与Main Master上的GTM Server通信的,为此,我们在Secondary Master和Main Master之间新增了一条通道并实现了一套GTM交互协议。另外,为了减少Secondary Master和Main Master之间的连接并提升网络通信效率,我们新增实现了GTM Proxy来代理同一个Secondary Master上多个Backend进程的GTM请求。下面,本文将从GTM交互协议 、GTM Proxy和分布事务恢复三个方面来系统的阐述一下Multi-Master分布式事务实现的技术细节。
(1)GTM交互协议
GTM交互协议是Secondary Master和Main Master之间事务交互的核心协议,具体协议的消息和说明如下表所示:
协议核心消息 | 说明 |
GET_GXID | 分配gxid |
SNAPSHOT_GET | 取分布式快照 |
TXN_BEGIN | 创建新事务 |
TXN_BEGIN_GETGXID | 创建新事务并分配gxid |
TXN_PREPARE | 给定的事务完成2pc的prepare阶段 |
TXN_COMMIT | 提交给定的事务 |
TXN_ROLLBACK | 回滚给定的事务 |
TXN_GET_STATUS | 取属于指定master的所有事务状态信息 |
GET_GTM_READY | 检查GTM server是否可服务正常事务请求 |
SET_GTM_READY | 设置GTM server可服务正常事务请求 |
TXN_COMMIT_RECOVERY | master恢复阶段提交给定的事务 |
TXN_ROLLBACK_RECOVERY | master恢复阶段回滚给定的事务 |
CLEANUP_MASTER_TRANS | 恢复完时清除master的剩余事务 |
可以看到,消息的核心还是在交换GXID,SNAPSHOT等信息,同时做BEGIN/PREPARE/COMMIT/ABORT等事务操作,此处就不再做一一说明。值得特别指出的是,跨节点的消息交互成本是很高的,考虑到OLAP用户的特点和需求,我们配合协议提供了不同的一致性选项,从而让用户可以在性能和一致性上进行权衡和选择:
- 会话一致:同一个会话满足可预期的一致性要求,包括单调读,单调写,读自己所写,读后写的一致性。
- 强一致:线性一致性,一旦操作完成,所有会话可见。也基于不同的一致性模式进行了定制和精简。
如上表所示,如果用户需要更高的性能而对于一致性可以做出一定妥协,则可以选择会话一致模式,相对强一致,会话一致对协议交互进行了大幅度精简,仅仅保留了 GET_GXID和 GET_GXID_MULTI :
协议核心消息 | 说明 |
GET_GXID | 分配gxid |
GET_GXID_MULTI | 批量分配gxid |
其中, GET_GXID_MULTI本质就是 GET_GXID的批量操作。在会话一致模式下,Secondary Master只需要从Main Master获取全局的GXID信息,然后结合本地快照并配合重试及GDD全局死锁检测(后面会讲到)来独立处理事务,从而大幅度简化与Master之间的消息交互提升性能。当然,这里的代价就是在一致性上做出的让步,事实上,会话一致可以满足绝大部分OLAP/HTAP客户的诉求。
(2)GTM Proxy的实现
在Multi-Master的实现中,GTM Proxy是作为Postmaster的子进程来管理的,这样做的好处是:1) 无需新增新的角色,配套管控更简单;2) GTM Proxy和Backend之间是天然认证和互信的;3) GTM Proxy可以通过共享内存和Backend进程通信,这样相比Tcp Loopback更高效,既可以减少内存拷贝,也无Network Stack开销。
每个GTM Proxy进程会和GTM server建立一个网络连接,并会服务多个本地的backend进程,将它们的GTM请求转发给GTM server。GTM Proxy还针对性的做一些请求优化处理,如:
- Backends间共享Snapshot,从而减少Snapshot请求数
- 合并和批处理Backends的并发GTM请求
- 批量获取gxid(会话一致)
GTM Proxy是减少Secondary Master和Main Master之间连接并提升网络通信效率的关键。事实上,在实现中,如果用户开启了强一致模式,我们在Main Master上会默认开启GTM Proxy来代理Main Master上多个Backend进程与GTM Server之间的请求,从而进一步降低GTM Server的压力。
(3)分布式事务的恢复
在很多情况下系统都需要做分布式事务的恢复处理,比如系统/Master重启,Main Master/Standby Master切换等,当不考虑Multi-Master,分布式事务的恢复可以简单划分为如下3大步骤:
- Main Master回放xlog,找出所有已经Prepared但是尚未Committed的事务;
- 命令所有Segments提交所有需要Committed的事务;
- 收集所有Segments上未Committed而且不在“Main Master”需要提交的事务列表中的事务,Abort掉这些事务。
上面的流程如果进一步考虑Multi-Master,那么一些新的问题就引入了进来,核心需要解决的有:1)Secondary Master发起的事务的恢复;2) Segments和Secondary Master上残留Prepared阶段的事务在Secondary Master或者Master重启等情况下的恢复/清理等等。为此,针对Multi-Master,我们对二阶段事务的提交和分布式事务的恢复流程都做了增强,如下主要讲一下二阶段事务提交的增强和Secondary Master被删除及第一次启动时对应的清理流程:
此外,Main Master/Secondary Master重启的流程也进行了增强,这里面和原Main Master重启恢复的主要差别是需要区分出属于自己发起的分布式事务,具体的区分是通过增强GXID来实现的。我们在原本GXID的基本信息之上添加了masterid信息,这样{GXID}-MasterID结合起来,就可以基于GXID来区分出具体的Master了。
2 全局死锁检测
ADB PG 4.3版本是通过对表加写锁来避免执行UPDATE和DELETE时出现全局死锁。这个方法虽然避免了全局死锁,但是并发更新的性能很差。ADB PG从6.0开始引入了全局死锁检测。该检测进程收集并分析集群中的锁等待信息,如果发现了死锁则杀死造成死锁的进程来解除死锁,这样极大地提高了高并发情况下简单查询、插入、删除和更新操作的性能。ADB PG 6实现全局死锁检测的要点如下:
- 全局死锁检测服务进程(GDD)运行在Main Master上
- GDD会周期性获取所有segment上的分布式事务的gxid及其等待关系
- GDD构造全局的事务等待关系图,检测是否成环,如果成环,则回滚环中一个事务,破解死锁
ADB PG Multi-Master的全局死锁检测整体也是ADB PG 6.0版本的实现来增强的,如下图所示:
ADB PG Multi-Master的GDD也运行在Main Master之上,主要新增了两个Master-to-Master的RPC调用来采集由Secondary Master发起的分布式事务gxid列表以及通知Secondary Master去破解负责分布式事务的死锁。
- Get_gxids: 从每个secondary master获取gxid列表,以判断导致死锁的事务各属于哪些master
- Cancel_deadlock_txn: 如果导致死锁的事务属于某个secondary master,则请求该master回滚掉该事务
3 DDL支持
在ADB PG的原生实现中,Main Master对DDL的支持和与Segments上Catalog的修改同步是通过2PC的方式实现的,ADBPG Multi-Master扩展了这一实现来支持对Secondary Master上Catalog的同步。
此外, Secondary Master也支持处理DDL,简单说来,我们在Secondary Master内部实现了一个简单的代理,Secondary Master如果收到DDL请求,会将请求转发给Main Master来处理。具体如下图所示:
DDL的实现非常复杂,真实的落地其实要比上面复杂很多,也牵涉到很多细节,比如VACCUM/CLUSTER/ANALYZE等相对特殊的DDL处理,但整体的实现方案都基本遵从上面的原则。
4 分布式表锁
众所周知,在数据库的实现里,为支持对表数据的并发访问,一般都会通过锁来实现。ADB PG的锁模型和PostgreSQL是兼容的,具体如下表所示:
Multi-Master对ADB PG的表锁协议进行了增强和适配,总结起来,我们定义了一套新的分布式表锁协议来规范Main Master及Secondary Master上加锁的顺序和规则:
任意Master上的进程请求1-3级锁
- 本地请求该表锁
- 在所有Segments上请求该表锁
- 事务结束时所有节点释放锁
Main Mater上的进程请求4-8级锁
- 本地请求该表锁
- 在所有Secondary master上请求该表锁
- 在所有Segments上请求该表锁
- 事务结束时所有节点释放锁
Secondary Master上的进程请求4-8级锁
- 在Main Master上请求该表锁
- 本地请求该表锁
- 在所有其他Secondary Master上请求该表锁
- 在所有Segments上请求该表锁
- 事务结束时所有节点释放锁
基于上述规则,我们可以实现任何的表锁请求会最终在某个Master或者Segment得到裁决,从而保证了对ADB PG的原表锁协议的兼容。
5 集群容错与高可用
ADB PG是通过复制和监控来实现容错和高可用的,主要包括:1)Standby Master和Mirror Segment分别为Main Master和Primary Segment提供副本(通过PG流复制实现);2)FTS在后台负责监控与主备切换。如上图中的流程:
- Main Master到Standby Master的流复制;
- Primary Segment到Mirror segment的流复制;
- Main Master的FTS Probe进程发包探活Primary Segment;
- Main Master的FTS Probe进程发包探活Secondary Master;
- Main Master重启后,其FTS Probe进程向GTM Server通报所有Master;
- Secondary Master的FTS Probe发包探活Main Master,获取最新集群配置和状态信息并存在本地;
- Secondary Master的FTS Probe无法连接Main Master后尝试探活Standby master,若成功则更新其为新的Main Master;否则继续探活原Main Master。
简单说来,ADBPG Multi-Master在原ADB PG的容错和高可用基础之上进行了增强,让系统能够进一步对Secondary Master进行兼容。另外,Secondary Master如果故障,则会进一步由管控系统看护并做实时修复。
五 Multi-master 扩展性能评测
ADB PG单Master实例在高并发点查、导入等偏OLTP的场景往往会存在单Master瓶颈,而Multi-Master的引入很好的解决了问题。为了验证Multi-Master在OLTP负载下横向扩展的能力,本章节对ADB PG Multi-Master在默认的会话一致模式下的TPC-B/C两种典型负载进行了性能测试。
1 TPC-C性能测试
TPC-C是事务处理性能委员会(TPC)旗下一的一个主流性能测试Benchmark集合,主要是测试数据库系统的事务能力。TPC-C测试过程中,会实现多种事务处理并发执行、在线与离线事务混合执行等方式,能够比较全面地考察数据库系统的事务能力。我们采用的测试环境是基于阿里云ECS的ADB PG实例,具体参数如下:
- Master(Main Master/Secondary Master):8c64g
- 节点规格(segment):4C32G
- 节点数量(segment): 32
- 存储类型:ESSD云盘
- 节点存储容量(segment): 1000GB
可以看到,在只有1个Master时,当并发数到达64时,TPC-C的性能基本达到峰值,无法再随着并发数增加而增加,但是当有4个Masters时,随着并发数的增加TPC-C的性能依旧可以非常好的线性扩展。
2 TPC-B性能测试
TPC-B是TPC旗下另一个性能测试Benchmark集合,主要用于衡量一个系统每秒能够处理的并发事务数。我们采用的测试环境是基于阿里云ECS的ADB PG实例,具体参数如下:
- Master(Main Master/Secondary Master):8c64g
- 节点规格(segment):4C32G
- 节点数量(segment): 32
- 存储类型:ESSD云盘
- 节点存储容量(segment): 1000GB
可以看到,和TPC-C类似,在只有1个Master时,当并发数到达64时,TPC-B的性能基本达到峰值,无法再随着并发数增加而增加,但是当有4个Masters时,随着并发数的增加TPC-B的性能依旧可以非常好的线性扩展。
六 总结
ADB PG Multi-Master通过水平扩展Master节点很好的突破了原架构单Master的限制,配合计算节点的弹性,系统整体能力尤其是连接数及读写性能得到进一步提升,可以更好的满足实时数仓及HTAP等业务场景的需求。