Greenplum介绍

本文介绍了Greenplum的一些内部架构。

GP的官方文档请参见:http://gpdb.docs.pivotal.io/4380/admin_guide/intro/arch_overview.html

阿里云GP的产品介绍参见:https://help.aliyun.com/document_detail/35387.html

由于后面讨论的所有的分布式数据库,包括Greenplum Database是基于单机版关系型数据库PostgreSQL的,所以我们首先简单介绍一下PostgreSQL,作为后续讨论的基础。

PostgreSQL

screenshot

每个PostgreSQL数据库的实例包含一个PostMaster的damon进程和多个子进程,包括负责写出脏数据的BG Writer进程,收集统计信息的Stats Collector进程,写事务日志的WAL Writer进程,等等。

客户端应用通过libpq协议连接到PostMaster进程;PostMaster收到连接请求后,fork出一个子进程Postgres Server来处理来自这个连接的查询语句。Postgres Server进程的功能组件可以分成两大类:查询执行和存储管理。查询执行组件包括解析器、分析器、优化器以及执行器。在查询执行过程中,需要访问和更新系统状态和数据,包括缓存,锁,文件和页面等等。

Greenplum

screenshot

作为一个单机版的关系型数据库,PostgreSQL更多地是作为联机事务处理(OLTP)系统使用的。当然,由于其丰富的分析功能,很多企业也会基于PostgreSQL来构建数据仓库,特别是在数据量不大的情况下。但是,随着数据量的增大,基于单机PostgreSQL构建的数据仓库就无法满足企业用户对查询响应时间的要求:低延迟。

为了解决这个问题,MPP架构就被引入了。这是MPP架构分布式数据库的简单示意图。MPP数据库通过将数据切片分布到各个计算节点后并行处理来解决海量数据分析的难题。每个MPP数据库集群由一个主节点(为了提供高可用性,通常还会有一个从主节点)和多个计算节点组成。主节点和每个计算节点都有自己独立的CPU,内存和外部存储。主节点负责接收客户端的请求,生成查询计划,并将计划下发到每个计算节点,协调查询计划的完成,最后汇总查询结果返回给客户端。计算节点负责数据的存储以及查询计划的执行。计算节点之间是没有任何共享依赖的(shared nothing)。查询在每个计算节点上面并行执行,大大提升了查询的效率。

我们接下来要讲的开源Greenplum Database就是基于PostgreSQL的MPP数据库。对应到这个架构图,每个节点上面的数据库实例可以简单的认为是一个PostgreSQL实例。

screenshot

我们首先通过一条简单的查询,感性地认识一下Greenplum Database是如何执行一条查询的。

这是一条简单的两表等值连接语句。其中,customer表是维度表,表数据以cust_id作为hash分布的key;sales表是事实表,在这个例子中,我们可以认为它的表数据是round-robin的方式随机分布的,不影响查询的执行。

每个查询执行是一个由操作符组成的树。只看其中一个节点的话(如前面所说,每个计算节点就是一个PostgreSQL的实例),为了执行两表的等值连接,我们首先会将两表的数据分别扫描出来,然后基于维度表customer建立hash桶。对于每一条从sales表扫描出来的纪录,我们都会到hash桶去查。如果满足匹配条件,数据连接结果;否则,直接pass。

如前面提到的,在Greenplum Database中,每张表的数据按照hash分布或者随机分布打散到每个计算节点上面。在这个例子中,由于sales表是随机分布的,为了正确执行基于cust_id的等值连接,生成的执行计划会在table scan上面添加一个Redistribution motion节点。这个motion节点根据cust_id的hash值对数据作重分布,类似MapReduce中的shuffling。由于hash join操作是在每个节点上面分布式执行的,在将结果返回给客户端的时候,需要在主节点上面执行汇总操作。Gather motion的作用就在于将每个节点上面的中间结果集中到主节点上面。

对于这样一个并行的查询计划,我们会根据数据重分布的操作将整棵查询执行树切割成不同的子树。每个子树对应查询计划的一个阶段,我们称为slice。查询计划和slice是逻辑上的概念。

screenshot

在物理层面,对应的是并行执行计划和gang。gang指的是执行同一个slice操作的一组进程。MPP数据库的一个重要特征是,计算和存储是紧耦合的。每一张表的数据打散存储到每个计算节点上面。为了确保查询结果的正确性,每个计算节点都需要参与每条查询的执行中。在Greenplum Database的架构设计中,对于每个slice执行子树,在每个计算节点中会启动一个相应的Postgres Server进程(这里称为QE进程)来执行对应的操作。执行同一个slice的一组QE进程我们称为gang。对应于查询计划中的三个slice,在执行计划中,相应有三组gang。其中底下的两个gang,我们称之为N-gang,因为这种类型的gang中,包含了每个计算节点上面启动的一个QE进程。顶上的gang,我们称之为1-gang,因为它只包含了一个进程。

screenshot

一般来说,对于N张表的关联操作,执行计划中会包含2N个gang,其中1个1-gang,对应主节点上面的进程;2N-1个N-gang,对应每个计算节点上面启动的2N-1个QE进程。在这2N-1个gang中,其中N个用于扫描N张表,中间N-1个gang用于两表关联。也就是说,对于一条涉及到N表关联操作的查询语句,我们需要在每个计算节点上面启动2N-1个QE进程。

很多用户在评估Greenplum Database的并发数,也就是支持的最大同时运行的查询数量,首先会担心主节点会成为瓶颈,直观原因是所有用户连接请求都首先会到主节点。其实,从资源使用的角度看,计算节点会首先成为瓶颈。因为在执行涉及多表关联的复杂查询时,计算节点上面启动的进程数量会远多于主节点。所以,Greenplum Database系统架构决定了它不能支持非常高的并发访问。

screenshot

前面我们简单阐述了MPP分布式数据库的架构,并通过一条简单的查询语句解释了分布式的执行计划。接下来我们深入讨论一下Greenplum Database的重要组件。

首先是解析器。从使用者的角度看,Greenplum Database跟PostgreSQL没有明显差别。主节点作为整个分布式系统集群的大脑,负责接收客户连接,处理请求。跟PostgreSQL一样,对于每一个连接请求,Greenplum Database都会在主节点上面fork一个Postgres Server(我们称之为QD)进程出来,负责处理这个连接提交的查询语句。对于每一条进来的查询语句,QD进程中的解析器执行语法分析和词法分析,生成解析树。虽然在一些DDL语句上面,Greenplum Database跟PostgreSQL会有一些语法上的小不同,例如建表语句可以指定数据进行hash分布的key,但总体上,在解析器这个组件上面,两者的差别不大。

screenshot

优化器根据解析器生成的解析树,生成查询计划。查询计划描述了如何执行查询。查询计划的优劣直接影响查询的执行效率。对于同样一条查询语句,一个好的查询执行效率比一个次好的查询计划快上100倍,也是一个很正常的事情。从PostgreSQL到MPP架构的Greenplum Database,优化器做了重大改动。虽然两者都是基于代价来生成最优的查询计划,但是Greenplum Database除了需要常规的表扫描代价、连接和聚合的执行方式外,还需要考虑数据的分布式状态、数据重分布的代价,以及集群计算节点数量对执行效率的影响,因为它最终是要生成一个分布式的查询计划。

screenshot

调度器是Greenplum Database在PostgreSQL上新增的一个组件,负责分配处理查询需要的计算资源,将查询计划发送到每个计算节点。在Greenplum Database中,我们称计算节点为Segment节点。前面也提过,每一个Segment实例实际上就是一个PostgreSQL实例。调度器根据优化器生成的查询计划确定执行计划需要的计算资源,然后通过libpg(修改过的libpg协议)协议给每个Segment实例发送连接请求,通过Segment实例上的PostMaster进程fork出前面提到过的QE进程。调度器同时负责这些fork出来的QE进程的整个生命周期。

screenshot

每个QE进程接收到从调度器发送过来的查询计划之后,通过执行器执行分配给自己的任务。除了增加一个新的称谓Motion的操作节点(负责不同QE进程间的数据交换)之外,总体上看,Greenplum Database的执行器跟PostgreSQL的执行器差别不大。

screenshot

MPP数据库在执行查询语句的时候,跟单机数据库的一个重要差别在于,它会涉及到不同计算节点间的数据交换。在Greenplum Database系统架构中,我们引入了Interconnect组件负责数据交换,作用类似于MapReduce中的shuffling阶段。不过与MapReduce基于HTTP协议不一样,Greenplum Database出于数据传输效率和系统扩展性方面的考虑,实现了基于UDP协议的数据交换组件。前面在解析执行器的时候提到,Greenplum Database引入了一个叫Motion的操作节点。Motion操作节点就是通过Interconnect组件在不同的计算节点之间实现数据的重分布。

screenshot

前面讲到的解析器、优化器、调度器、执行器和Interconnect都是跟计算相关的组件,属于无状态组件。下面我们再看一下跟系统状态相关的组件。首先是,系统表。系统表负责存储和管理数据库、表、字段等元数据。主节点上面的系统表是全局数据库对象的元数据,称为全局系统表;每个Segment实例上也有一份本地数据库对象的元数据,称为本地系统表。解析器、优化器、调度器、执行器和Interconenct等无状态组件在运行过程中需要访问系统表信息,决定执行的逻辑。由于系统表分布式地存储在不同的节点中,如何保持系统表中信息的一致性是极具挑战的任务。一旦出现系统表不一致的情况,整个分布式数据库系统是无法正常工作的。

screenshot

跟很多分布式系统一样,Greenplum Database是通过分布式事务来确保系统信息一致的,更确切地说,通过两阶段提交来确保系统元数据的一致性。主节点上的分布式事务管理器协调Segment节点上的提交和回滚操作。每个Segment实例有自己的事务日志,确定何时提交和回滚自己的事务。本地事务状态保存在本地的事务日志中。

screenshot

介绍完Greenplum Database的查询组件和系统状态组件后,我们再看看它是如何提供高可用性的。首先是管理节点的高可用。我们采取的方式是,启动一个称为Standby的从主节点作为主节点的备份,通过同步进程同步主节点和Standby节点两者的事务日志,在Standby节点上重做系统表的更新操作,从而实现两者在全局系统表上面的信息同步。当主节点出故障的时候,我们能够切换到Standby节点,系统继续正常工作,从而实现管理节点的高可用。

计算节点高可用性的实现类似于管理节点,但是细节上有些小不同。每个Segment实例都会有另外一个Segment实例作为备份。处于正常工作状态的Segment实例我们称为Primary,它的备份称为Mirror。不同于管理节点日志重放方式,计算节点的高可用是通过文件复制。对于每一个Segment实例,它的状态以文件的形式保存在本地存储介质中。这些本地状态可以分成三大类:本地系统表、本地事务日志和本地表分区数据。通过以文件复制的方式保证Primary和Mirror之间的状态一致,我们能够实现计算节点的高可用。

(转载自https://segmentfault.com/a/1190000007419222?from=timeline&isappinstalled=0,有删改)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值