目录
一、Greenplum的架构
1.1 Greenplum Master
1.2 Greenplum Segment
1.3 Greenplum Interconnect
1.4 Greenplum Standby Master
1.5 Greenplum Mirror Segment
二、Greenplum查询计划
2.1 单机查询计划
2.1 并行查询计划
2.1.1 Motion
2.2.2 Slice和Gang
三、Greenplum数据库查询处理的过程
3.1 解析器
3.2 优化器
3.3 调度器
3.4 执行器
3.5 Interconnect
3.6 系统表
3.7 分布式事务
3.8 Greenplum数据库查询的执行流程
一、Greenplum的架构
Greenplum是业界先进的开源分布式数据库,采用无共享(Share-Nothing)的MPP(Massively Parallel Processing)架构,主要用于大规模数据处理、数据仓库、商务智能和数据挖掘等应用。
近年来,随着流式数据处理、混合负载应用场景的出现,Greenplum在这些方面展现出强大的能力,这也使得Greenplum从单纯的数据库向大数据平台方向发展。无共享架构的特点是整个系统使用多个处理单元协作完成任务。每个处理单元拥有自己的操作系统、内存和磁盘等资源,不同节点之间通过网络进行通信。在Greenplum数据库中,每个处理单元叫作Segment节点。负责生成查询计划并协调每个处理单元进行查询执行的节点叫作Master节点。除了Master和Segment节点外,有一个重要的功能组件叫作Interconnect,主要负责在查询执行过程中,实现节点间的数据传输。
同时,为了保障Greenplum架构的高可用,Master节点和每个Segment节点都有自己的备份节点,分别称为Standby Master节点和Mirror Segment节点,为Master节点和Segment节点提供高可用支持。Greenplum的高可用架构之所以采用主备方式而不是类似Hadoop的多副本机制,主要是因为Hadoop的磁盘存储是基于磁盘簇(Just Bundle Of Disks, JBOD),每一块磁盘没有冗余机制,而是通过应用层的多副本来保证数据可靠性。Greenplum建议的磁盘存储方式是RAID(Redundant Array Of InexpensiveDisks),每一块磁盘在物理上都有冗余机制,主备方式则是在磁盘冗余基础上提供另一级的数据可靠性。
1.1 Greenplum Master
Greenplum Master是Greenplum数据库的主节点,作为数据库的入口,主要负责接收客户端连接请求,对SQL语句生成查询计划,并将查询计划分发给所有的Greenplum Segment节点完成查询执行。对用户而言,可以把整个Greenplum数据库当作一个PostgreSQL数据库,可以使用PostgreSQL的客户端工具PSQL,以及应用编程接口JDBC和ODBC来访问。Greenplum数据库的系统表存储在Master和Segment节点上,Master节点不存储任何用户数据,用户数据全部存储在Segment节点上。
1.2 Greenplum Segment
每个Greenplum Segment实例都可以视为一个独立的PostgreSQL数据库,其上存储着一部分用户数据,同时参与查询执行工作。Greenplum Segment节点指的是Segment实例所在的主机,根据Segment节点所在主机的CPU核数、内存空间、网络带宽等因素,通常一个GreenplumSegment节点会部署2~8个Segment实例。Segment的实例数越多,表示在此Segment节点上执行查询时资源使用越多,查询执行速度越快。通常情况下,为了获得更好的查询性能,用户数据应该均匀分布在所有Segment实例上。
1.3 Greenplum Interconnect
Greenplum Interconnect是Greenplum数据库的网络层,主要负责查询执行过程中所有Segment实例之间以及Segment和Master之间的数据通信。默认情况下,Interconnect使用UDP协议在网络中传输数据,然而由于UDP协议无法保证服务质量,因此Interconnect在UDP协议基础上增加了应用层的数据包验证功能,从而达到与TCP一样的可靠性,但性能及扩展性方面要优于TCP。之所以选择UDP,主要是因为在大规模集群中,TCP由于端口号数量有限会限制整个集群的规模。
1.4 Greenplum Standby Master
Standby Master是Master节点的备份节点,通常部署在与Master节点不同的物理节点上。Master节点并不存储用户数据,当数据库元信息有更新时,这些变化会自动同步到Standby Master节点以保证元信息的一致性。当Master节点出现故障而无法恢复时,Standby Master节点可以被激活而成为新的Master节点。
1.5 Greenplum Mirror Segment
每一个Segment节点可以部署一个Mirror Segment节点,此时原有的Segment节点称为Primary Segment节点。当Primary Segment节点接收Master节点的查询请求并需要修改用户数据时,对应的数据变化会复制到相应的Mirror Segment节点上。在查询执行过程中,一旦Primary Segment不可用,查询执行会重新分发到Mirror Segment节点以保证返回正确的查询结果。
二、Greenplum查询计划
查询计划是数据库最重要的部分之一,一个查询计划的优劣直接决定了查询性能的好坏。Greenplum查询计划与PostgreSQL类似,但由于Greenplum数据库分布式架构的特点,其查询计划也引入了与分布式计算相关的概念。
首先定义如下四张哈希分布的表:sale、customer、product和vendor。
表之间的关系如图
postgres=# \d
List of relations
Schema | Name | Type | Owner | Storage
--------+-------------------+-------+---------+----------
public | customer | table | gpadmin | heap
public | product | table | gpadmin | heap
public | sale | table | gpadmin | heap
public | vendor | table | gpadmin | heap
select c.cname,sum(s.prc * s.qty)as mount
from sale s,customer c
where s.cn = c.cn
group by c.cname;
2.1 单机查询计划
在PostgreSQL数据库中通过explain显示的单机查询计划如下:
查询执行数据流如图
此查询计划在单机数据库上可以很好地执行。但是,在Greenplum分布式数据库架构下,由于各个表的数据是分散存储在所有的Segment实例上的,因此当一个Segment实例执行查询时,如果仅考虑自身的数据,无法得到正确的查询结果。例如,当哈希连接操作在分布式环境下执行时,对于一个Segment实例上的sale表,表中的元组仅能关联到存储在同一Segment实例上的customer表元组,不能关联到其他Segment实例上的customer表元组,这将使得查询结果变少。所以,在分布式环境中,需要对查询计划进行改进使得Segment实例中的数据可以按照某种规则进行重分布,让类似哈希连接这样的操作可以看到数据的全局视图,从而保证用户得到正确的查询结果。
2.1 并行查询计划
分布式数据库的架构实现了数据的分布,并行查询计划就是在数据分布的基础上确定如何高效地实现计算的并行化以及如何让计算更好地贴近数据,从而保证提供最好的查询性能。
Greenplum数据库可以在用户创建表时指定数据的分布策略:哈希分布或随机分布。如果采用哈希分布,那么还需要指定分布键。
在Greenplum数据库中,针对上述查询语句,通过EXPLAIN显示的并行查询计划如下:
postgres=# explain select c.cname,sum(s.prc * s.qty)as mount
from sale s,customer c
where s.cn = c.cn
group by c.cname;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------
Gather Motion 4:1 (slice3; segments: 4) (cost=0.00..862.00 rows=1 width=16)
-> GroupAggregate (cost=0.00..862.00 rows=1 width=16)
Group Key: customer.cname
-> Sort (cost=0.00..862.00 rows=1 width=16)
Sort Key: customer.cname
-> Redistribute Motion 4:4 (slice2; segments: 4) (cost=0.00..862.00 rows=1 width=16)
Hash Key: customer.cname
-> Result (cost=0.00..862.00 rows=1 width=16)
-> GroupAggregate (cost=0.00..862.00 rows=1 width=16)
Group Key: customer.cname
-> Sort (cost=0.00..862.00 rows=1 width=20)
Sort Key: customer.cname
-> Hash Join (cost=0.00..862.00 rows=1 width=20)
Hash Cond: (sale.cn = customer.cn)
-> Redistribute Motion 4:4 (slice1; segments: 4) (cost=0.00..431.00 rows=1 width=16)
Hash Key: sale.cn
-> Seq Scan on sale (cost=0.00..431.00 rows=1 width=16)
-> Hash (cost=431.00..431.00 rows=1 width=12)
-> Seq Scan on customer (cost=0.00..431.00 rows=1 width=12)
Optimizer: Pivotal Optimizer (GPORCA) version 3.85.0
(20 rows)
此查询对应的并行查询计划与单机查询计划有如下差别:
- 单机查询计划根节点哈希聚集上新增Gather Motion节点作为新的根节点。
- sale表的顺序扫描节点上加入Redistribute Motion节点,重分布键为s.cn。
- Gather Motion和Redistribute Motion节点均存在slice及segments属性。
- 聚集操作由单机版的一阶段聚集变成分布式查询计划的二阶段聚集,即查询计划中包括两个哈希聚集节点。
- 两个哈希聚集节点间存在Redistribute Motion节点,重分布键为c.cname。
2.1.1 Motion
在上述差异中,一个很重要的因素是Motion节点的引入。Gather Motion节点的主要作用是运行在Master节点上,将所有Segment实例上查询执行的结果收集起来。如果需要,可以进行聚集操作,最终将查询结果返回给用户。除了通过Gather Motion节点进行查询结果汇集外,Motion节点另一个重要作用是对数据进行重分布。因为在分布式架构下,一个Segment实例仅存储一部分用户数据,不同Segment实例间的数据没有交集。
在上述查询执行过程中,当一个Segment实例根据关联键cn执行哈希连接操作时,由于sale表的分布键是(cn, vn,pn)而不是cn,因此需要对sale表的数据进行重分布,将所有与当前Segment实例上customer表的c.cn值相关联的所有sale表数据都收集到此Segment实例上,这类Motion的操作方式是针对某一个键值将某一个表的数据重新分发到所有Segment上,因此这类操作叫作Redistribute Motion,也就是按照新的数据分布规则将表中的数据通过网络重新分发到各个Segment实例上。
在上述差异中,一个很重要的因素是Motion节点的引入。Gather Motion节点的主要作用是运行在Master节点上,将所有Segment实例上查询执行的结果收集起来。如果需要,可以进行聚集操作,最终将查询结果返回给用户。除了通过Gather Motion节点进行查询结果汇集外,Motion节点另一个重要作用是对数据进行重分布。因为在分布式架构下,一个Segment实例仅存储一部分用户数据,不同Segment实例间的数据没有交集。在上述查询执行过程中,当一个Segment实例根据关联键cn执行哈希连接操作时,由于sale表的分布键是(cn, vn,pn)而不是cn,因此需要对sale表的数据进行重分布,将所有与当前Segment实例上customer表的c.cn值相关联的所有sale表数据都收集到此Segment实例上,这类Motion的操作方式是针对某一个键值将某一个表的数据重新分发到所有Segment上,因此这类操作叫作Redistribute Motion,也就是按照新的数据分布规则将表中的数据通过网络重新分发到各个Segment实例上。
除了Gather Motion、Redistribute Motion外,还有一种Motion节点叫作Broadcast Motion。Broadcast Motion的作用是将当前Segment实例操作的数据发送给其他Segment实例。这样,每一个Segment实例都能得到这个表所对应的全局数据。在上述实例中,为了完成哈希聚集,查询计划中针对sale表进行了Redistribute Motion操作,将所有可以进行关联的数据收集到一起执行哈希聚集操作。但如果sale表的数据量很多,而customer表数据很少,我们也可以对customer表数据执行Broadcast Motion操作,然后和每一个Segment上的sale表进行关联,同样可以得到正确的查询结果。
除了哈希聚集算子外,聚集也会涉及数据的重分布。当分组字段并不是表的分布键时,需要根据分组字段对待聚集数据重分布。同时,为了减少重分布的数据量,可以先在每个Segment实例上对本地数据执行分组操作,然后将结果按照分组字段重分布到上层节点进行全局汇总,此过程即为分布式数据库中的二阶段聚集。在上述实例中,
- 第一阶段聚集是根据一个Segment实例上哈希连接的结果,用分组的字段先进行局部数据的聚集,此时数据的分布键是仍是c.cn。
- 第二阶段聚集则是根据分组的字段c.cname对数据进行重分布,对每一个分组的值进行全局数据的聚集。此时,任一分组的结果仅存储在一个Segment实例上,不同Segment实例上存储的是不同分组的结果。
- 最后则由Master节点执行Gather Motion,收集所有分组的结果并返回给用户。
并行查询计划对应的查询执行数据流如图
2.2.2 Slice和Gang
Greenplum数据库在查询执行过程中,为了增加查询执行的并行度,会将查询计划切分成多个Slice。每一个Slice可以视为单机可执行的查询计划片段,其所对应的数据或者是本地数据,或者是通过计划树下层Motion节点传递过来的数据。所以,在Greenplum并行查询计划中,每一个Motion节点都会生成一个Slice,每一个Slice由一个进程来执行对应此部分的查询计划。通过Slice将查询计划并行化,既可加快查询执行速度,又可以充分提高系统CPU等资源的利用率。
在Greenplum并行查询计划中,每个Motion节点均有两个属性:
- sliceId sliceId是这个Slice在并行查询计划里的唯一标识
- segments segments是参与执行该Slice的Segment实例数量,要么是1,对应Master节点;要么是N,对应整个Greenplum集群中Segment实例总数。
所有Segment实例上执行同一SliceId操作的一组进程称为一个Gang。在一个规模为N的Greenplum集群中,Gang的大小或者是N(N-Gang),或是1(1-Gang)。N-Gang通常对应运行在Segment实例上的普通操作。例如,在N个Segment实例上,每个Segment启动一个进程执行全表扫描操作。1-Gang通常对应运行在Master节点上的Gather Motion,启动一个进程收集所有Segment上的数据进行汇总。Gang的大小即为查询的并行度。
将Greenplum并行查询计划划分为Slice后的结果如图
此并行查询计划分为4个Slice, Root Slice(Slice0)运行在Master节点上,其余Slice均运行在Segment上,执行每个非Root Slice的进程数为Segment实例数。
在有3个Segment实例的Greenplum集群中,根据Slice分配Gang的结果如图
三、Greenplum数据库查询处理的过程
3.1 解析器
Master节点接收到客户端请求后,进行认证操作。认证成功并建立连接后,客户端发送查询语句给数据库。解析器负责对收到的查询语句,即SQL字符串进行词法分析、语法分析,并生成查询树。
3.2 优化器
查询计划描述了如何执行一条查询,通常以树形结构表示。查询优化器用于对解析器的结果(即查询树)进行处理,从所有可能的查询计划中选择一个最优的或者接近最优的查询计划,作为最终的查询计划。Greenplum数据库支持两种优化器,一种是基于PostgreSQL的planner查询优化器,另一种是全新的ORCA查询优化器。ORCA是Pivotal开源的针对OLAP业务的查询优化器,它在模块化、扩展性、性能等很多方面相比planner查询优化器有很大的改进,性能提升几十倍到上千倍不等。
3.3 调度器
调度器(Query Dispatcher, QD)和下面将要介绍的执行器(Query Executor,QE)是Greenplum数据库中两个非常重要的组件。根据查询计划中Slice的定义,调度器将查询计划发送给所有Segment节点上的执行器,待执行器执行完查询任务后,收集所有执行器的查询结果,汇总后返回给客户端。调度器在查询执行过程中,主要负责执行器的创建、销毁、错误处理、任务取消、状态更新等。
3.4 执行器
当执行器收到调度器发送的查询计划后,就执行查询计划中自己所负责的那个部分。典型的操作包括表扫描、哈希关联、排序、聚集等。
3.5 Interconnect
Interconnect主要负责集群中各个节点间的数据传输。
3.6 系统表
系统表用于存储和管理数据库、表、字段的元数据。每个节点上都有相应的数据拷贝。
3.7 分布式事务
Master节点上的分布式事务管理器负责协调Segment数据节点上的事务提交和回滚操作,由两阶段提交(2PC)协议实现。每个Segment数据节点都有自己的事务日志,负责自己节点上的事务处理操作。
3.8 Greenplum数据库查询的执行流程
Greenplum数据库处理查询请求的过程
Greenplum数据库查询的执行主要有以下步骤:
1)客户端(如PSQL)通过libpq协议首先连接到Master节点的Postmaster进程。
2)Master节点的Postmaster进程产生一个后端Postgres进程QD, QD与客户端进行连接,执行客户端请求认证。认证通过后,QD等待客户端发送查询请求。
3)客户端将查询请求提交给QD进程。
4)QD生成查询计划,并根据查询计划中的Slice数量,通过libpq与每个Segment实例上的Postmaster进程连接以启动QE进行查询执行。每个Segment实例上启动的QE数量等于需要执行的Slice数量。
5)QD将查询计划分发给所有Segment实例上所有的QE进程。
6)每个QE进程根据查询计划执行属于自己的Slice,不同Slice间QE的数据通信通过Interconnect完成。
7)所有负责执行最后一个Slice的QE将查询执行的结果返回给QD。
8)QD汇总查询结果,返回给客户端,并等待下一个查询语句的到来。
Greenplum数据库通过以上流程即能保证查询请求以并行执行的方式在数据库中进行处理,将查询结果迅速返回给用户。