gpexpand 表数据重分布机制问题
 
通知
清除全部

gpexpand 表数据重分布机制问题  


xiangbin1997
帖子: 3
初出茅庐
已加入: 2周 前

GP6.5gpexpand对于增加新segment后,需要对表格做数据重分布。由于6.5中分布算法使用了jump consistency hash,使得重分布时跨节点的数据移动只发生在old segment到new segment,没有old segment之间的数据移动。这大大提高了重分布的效率,帮助降低了扩容窗口时间。

具体原理可以参考下面链接,其中第一个视频是来自Pivotal开发者杜佳伦的演讲:

https://www.bilibili.com/video/BV1PE411j7cp/?p=2

https://www.cnblogs.com/zhangrui153169/p/11435581.html

 

我翻看了一下GP6.5的代码,重分布是通过扩展一个EXPAND keyword来实现,

命令:ALTER TABLE mytbl EXPAND TABLE;

对应的内核函数:

tablecmds.c
ATExecCmd
  AT_ExpandTable
   ATExecExpandTableCTAS

 

参考AT_ExpandTable(), ATExecExpandTableCTAS()的注释及代码,重分布主要依赖下面的query来实现

CREATE TABLE tmp_tab_nam AS SELECT * FROM cur_table DISTRIBUTED BY (policy)

 

对于old segment上的数据,比较容易理解,通过此query可以创建一个临时表,把对应该segment的数据抽取出来。

我的问题是:

所谓的old segment到new segment的数据移动,通过这个query如何实现的呢?问题背景有点长,不好意思,谢谢。

 

注释:

/*
 * ALTER TABLE EXPAND TABLE
 *
 * Update a table's "numsegments" value to current cluster size, and move
 * data as needed to the new segments.
 *
 * There are currently only one way we can perform EXPAND TABLE:
 *
 * 1. Create a whole new relation file, with the new 'numsegments', copy all
 *    the data to the new reltion file, and swap it in place of the old one.
 *    This is called the "CTAS method", because it uses a CREATE TABLE AS
 *    command internally to create the new physical relation.
 */
 
    /*--
     * a) Ensure that the proposed policy is sensible
     * b) Create a temporary table and reorganise data according to our desired
     *    distribution policy. To do this, we build a Query node which express
     *    the query:
     *    CREATE TABLE tmp_tab_nam AS SELECT * FROM cur_table DISTRIBUTED BY (policy)
     * c) Execute the query across all nodes
     * d) Update our parse tree to include the details of the newly created
     *    table
     * e) Update the ownership of the temporary table
     * f) Swap the relfilenodes of the existing table and the temporary table
     * g) Update the policy on the QD to reflect the underlying data
     * h) Drop the temporary table -- and with it, the old copy of the data
     *--
     */

 

代码:

ATExecExpandTableCTAS()
// build CTAS
// CREATE TABLE tmp_tab_nam AS SELECT * FROM cur_table DISTRIBUTED BY (policy)
make_distributedby_for_rel
distby->numsegments = getgpsegmentCount();
build_ctas_with_dist

// run on all nodes
ExecutorStart
ExecutorRun

// swap relfilenodes and MORE
swap_relation_files

// reindex
reindex_relation

// Drop the tmp table
performDeletion

 

问题标签
4 回复
gaoming
帖子: 2
初出茅庐
已加入: 1月 前

这个create语句就会做数据的重分布,只是依然属于原来segment的数据发给原来的本地节点,属于新节点的数据才通过网络发给新节点。

回复
2 回复
xiangbin1997
已加入: 2周 前

初出茅庐
帖子: 3

@gaoming非常感谢

是不是DISTRIBUTED BY实现的啊?对应 ATExecSetDistributedBy()。
可能只是最终结果是数据移动只发生在old segment到new segment,但是每个segment上也做了查询。
我再研究研究。
回复
kainwen
已加入: 1周 前

初出茅庐
帖子: 1

@xiangbin1997

 

理解这个问题,首先要理解gpdb的存储分布特性的表,gp_distribution_policy这个catalog

 

这个catalog会记录分布键和一个叫做numsegments的字段,这个numsegments表示数据只在0~numsegments-1这些segments上分布。

numsegments小于cluster size的表成为部分分布表。

接下来需要理解Greenplum的motion机制,也就是用来实现分布式计算的网络通信抽象。

你可以模拟上面的过程,查看查询计划来理解这个问题:

1. 建立原始数据表t1,灌入一些数据。查看t1对应的gp_distribution_policy内容

2. 扩容但不重分布

3. 查看t1对应的gp_distribution_policy内容

4. 扩容后的集群里建立一个表t2

5. 查看t2对应的gp_distribution_policy内容

6. explain insert into t2 select * from t1; 仔细看motion的n:m

 

一些有助于理解gp执行器和查询计划的视频: https://www.bilibili.com/video/BV1Si4y1474L

 

 

https://www.bilibili.com/video/BV1J5411Y7yu?from=search&seid=3324744134128060438

 

这就可以理解这个问题。

 

回复
xiangbin1997
帖子: 3
初出茅庐
已加入: 2周 前

@kainwen 非常感谢!
按照你的思路,实验了一下,基本明白了:)。推荐的视频也很有针对性。

我的环境信息:原有6个segment,扩容了3个segment

可以看到下面重分布的Redistribute Motion 6:9,表明重分布发生在old segments(6个)到old+new segments(9个),除了old segment到new segment的motion,理论上也包含old segment到old segment的motion。但是由于数据分布hash使用的是Jump consistency hash,能够保证新加segment,old segment之间不会发生数据移动,实际上最终motion只发生在old segment到new segment。

 

// 新表,旧表的numsegments 不同,

// 旧表对应old segments数量(6个)

// 新表对应old+new segments数量(9个)

mydw=# select * from gp_distribution_policy;

localoid | policytype | numsegments | distkey | distclass
----------+------------+-------------+---------+-----------
18188 | p | 6 | 1 | 10043
18208 | p | 9 | 1 | 10043

mydw=# explain insert into t2 select * from t1;
QUERY PLAN
------------------------------------------------------------------------------------------
Insert on t2 (cost=0.00..7.00 rows=12 width=32)
-> Redistribute Motion 6:9 (slice1; segments: 6) (cost=0.00..7.00 rows=17 width=32)
Hash Key: t1.uid
-> Seq Scan on t1 (cost=0.00..7.00 rows=17 width=32)
Optimizer: Postgres query optimizer

mydw=# explain create table tmp_t1 as select * from t1 DISTRIBUTED BY (uid);
QUERY PLAN
------------------------------------------------------------------------------------
Redistribute Motion 6:9 (slice1; segments: 6) (cost=0.00..7.00 rows=17 width=32)
Hash Key: uid
-> Seq Scan on t1 (cost=0.00..7.00 rows=17 width=32)
Optimizer: Postgres query optimizer

回复

关注微信公众号

Greenplum中文社区

Greenplum官方微信群

扫码加入我们的技术讨论,请备注“网站”