数据源
数据源节点有很多种,比如数据表,系统表,函数,外表(foreign data wrapper)。这些数据源节点产生的数据有各自的分布特性。而对于中间节点,比如连接,聚合,集合操作,为了能够实现特定的处理逻辑,对数据的分布特性同样有特定的要求。数据的分布特性在Greenplum中称为Locus。下表列举了Locus的类型和其分布特性的说明
CdbLocusType_Entry
|
数据分布在 Master 上
|
CdbLocusType_SingleQE
|
数据集中分布在单个执行节点上
|
CdbLocusType_General
|
自包含的数据类型,与任何数据类型兼容
|
CdbLocusType_SegmentGeneral
|
与 CdbLocusType_General 相同,但是不能应用在 QD 上
|
CdbLocusType_Replicated
|
数据在每个 QE 节点上有一份复制
|
CdbLocusType_Hashed
|
数据哈希分片,NULL 值被分布到一个 QE
|
CdbLocusType_HashedOJ
|
数据哈希分片,但是 NULL 随机分布在多个 QE 上,一般是外连接的结果
|
CdbLocusType_Strewn
|
数据以未知的方式分片,通常是随机分布
|
数据表是数据库中最常见的数据源。Greenplum目前支持两种类型的数据表,普通表和复制表。普通表的数据分布在所有的Segment上,如果用户在创建表的时候,指定了分布键,数据会按照分布键的哈希值分布在各个Segment上,那么这个表的Locus为CdbLocusType_Hashed。如果用户在建表时没有指定分布键,数据会随机分布到各个Segment,那么这个表的分布类型则CdbLocusType_Strewn。复制表中,每个Segment都有一份完整的数据,所以它的数据分布类型为CdbPathLocus_IsSegmentGenera。
系统表也能够作为查询语句的数据源。在Greenplum中,为了能够在所有的Master和Segment节点上方便的访问系统表信息,几乎所有的系统表在Master和Segment上都存在一份完整的备份,但是当查询语句显式的用到系统表时,优化器认为系统表存在于Master上,所以系统表的Locus为Entry。
FDW是一种数据库对外部数据源的抽象。由于数据库通常并不知道外部数据源的分布情况,或者外部数据源并没有数据分布的概念,又或者外部数据源对于数据分布的抽象与Greenplum不相容。在GreenPlum中默认外部数据是未知分布,所以它的Locus为Strewn。
General是一种跟任何Locus都能相容的数据分布类型。General被称为自包含的类型,这意味着General类型的数据集在任何执行节点上(包括QD和QE),都有完整的数据。所以General可以与任何一种Locus相容。最典型的General数据集是generate_series(1,100)函数,这个函数在任何节点上执行都会按顺序产生1到100的整数。
函数是Greenplum上的一种常见的数据源。在Greenplum中,创建函数时可以指定函数的运行方式,分别为:
-
EXECUTE ON ANY,函数可以运行在任何一个或几个Master或者Segment上,每个函数都能够返回全量的数据,所以函数的Locus为General
-
EXECUTE ON MASTER,函数只能运行在Master上,所以函数的Locus为Entry
-
EXECUTE ON ALL SEGMENTS,函数运行在所有Segment上,所以函数的locus为Strewn
因此大家可能会发现,以上4种数据源并没有涵盖所有的8种Locus。这是因为Join和Motion节点会产生新的Locus。下面的部分将要讨论Locus在Join中的相容性和不同Locus之间的转换。
数据分布的相容性
上文提到,其中一个可能需要添加Motion节点的位置是表连接。表连接的定义是将两个关系中符合连接条件的元组连接在一起,生成一个新的关系。那么在分布式环境中正确的进行Join的前提就是,两个表中符合条件的元组分布在相同的Segment上。那么怎样的数据分布才能保证参加Join的两个输入数据源符合连接条件的元组分布在相同的Segment节点上呢
上图反映了不同的
Locus
在Join
中能否保证符合条件的元组分布在同一个Segment
上,通常满足条件的Locus
被称为Join
相容。上图中黄色的椭圆代表连接运算输入源数据的Locus
,浅蓝色的椭圆代表Join
生成结果的Locus
。两个不同的黄色椭圆通过箭头指向浅蓝色椭圆,代表两个黄色椭圆中的Locus
的Join
的结果是浅蓝色椭圆中的Locus
。黄色椭圆指向自己的箭头表示输入的两个数据源都是黄色椭圆中的Locus
,并且连接生成的结果跟输入数据源的Locus
相同。以一个选课系统的例子来验证上图中的
Join
相容性。在这个选课系统中一共有4张数据表。student
是参与选课的学生信息。teacher
是课程教师的信息。course
是课程信息。enrolled
是选课信息CREATE TABLE student(sid int, sname text, sage int) DISTRIBUTED BY (sid);
CREATE TABLE teacher (tid int, tname text, tage int) DISTRIBUTED BY (tid);
CREATE TABLE enrolled (sid int, cid int, score int) DISTRIBUTED RANDOMLY;
CREATE TABLE course (cid int, cname text, tid int) DISTRIBUTED REPLICATED;
student和teacher的创建语句指定了分布键,所以他们的Locus为CdbLocusType_Hashed。course的创建语句指定为复制表,所以它的Locus为CdbLocusType_SegmentGeneral。enrolled的创建语句指定分布方式为randomly,所以它的Locus为Strewn
以下面的sql为例:
SELECT sid, cid, tid, cname FROM course c, enrolled e WHERE c.cid = e.cid;
表course的Locus为SegmentGeneral,表enrolled的Locus为Strewn。两表在cid字段进行内连接。可以看出Join结果的Locus仍然为Strewn。同理可以推断出Strewn与General/Replicated的连接结果的Locus同样是Strewn。
SELECT tid, tname, cname FROM course c, teacher t WHERE t.tid = c.tid;
表course的Locus为SegmentGeneral,表teacher的Locus为Hashed。两表在tid字段进行内连接。可以看出连接结果的Locus仍然为Hashed。同理可以推断出Hashed与General/Replicated的连接结果的Locus同样是Hashed。
将General/SegmentGeneral/Replicated作为一类进行考虑,统一认为数据是在所有的Segment节点上复制的。那么这类数据Locus就可以与任何其他种类的Locus相兼容,Join的输出结果则为对方的Locus。
Strewn是一种分片的数据分布,但是分片的方法未知。所以数据库只能认为数据是随机分片分布在所有的Segment上。如果对表为General/SegmentGeneral/Replicated,由于对表在任何一个Segment上都含有全量的元组,任何一个符合条件的元组都能在本地找到,所以系统认为两者是Join兼容的。Join输出的Locus为Strewn。
select sid, sname, cid from student s, enrolled e where s.sid = e.sid;
反之,Hashed和Strewn则是不相容的两种Locus。由于Strewn的分布是不确定的分布,所以如果与Strewn进行Join的数据分布类型在每个Segment上没有全量的元组,那么Join的结果就会缺失很多数据。
Hashed/HashedOJ也是一种分片的数据分布,分片方式为按照分布键上的哈希值进行分布。Hashed与HashedOJ的区别在于对空值的处理。HashedOJ是Hashed Outer Join的简写,顾名思义,外连接的结果的分布是HashedOJ。Hashed空值会被分布到同一个Segment,而HashOJ空值会分布在所有的Segment上。同Strewn一样,与General/SegmentGeneral/Replicated是Join兼容的。Join的输出的Locus为Hash。与SingleQE不兼容,因为在除了SingleQE所在的Segment上,任何一个Segment对表都没有数据。与Strewn不兼容,原因是“不能保证在JoinKey上相等的元组分布在相同的Segment上”。与Hash/HashOJ在双方HashKey相同并且是JoinKey的子集时,双方是Join相容的。因为“HashKey相等的元组会被分布到相同的Segment上”。相反,如果不能满足条件,则不相容。
数据重分布
如果参与Join操作的两个表Locus不相容,系统如何才能顺利的完成Join呢?答案对数据进行重新分布。下图描述了Locus之间通过Motion的转化关系
在上图中,Locus按照数据分布的大体特征分成三组:
-
SINGLETON:数据分布在单一的节点上,包含SingleQE,Entry两种Locus
-
REPLICATED:数据是复制的,每个节点上都有一份完整的数据复制,包含Replicated,General,SegmentGeneral三种Locus
-
PARTITIONED:数据是分片的,每个节点包含一部分数据,包含Hashed,Strewn,HashedOJ三种Locus
图中的箭头表示Locus之间互相转换使用的Motion:
-
BroadCastMotion:将输入的元组复制发送到所有的Segment节点
-
GatherMotion:将元组发送到一个节点
-
HashedMotion:将元组按照分布键发送到一组Segment节点
以上三组Locus通过Motion互相转化,每组的任意一个Locus通过对应箭头所代表的的Motion转化为另一组的第一个Locus。比如PARTITIONED组Hashed,HashedOJ,Strewn三个Locus中的任意一个,在通过BroadCastMotion都转化为Replicated