您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Model Center   Code  
会员   
   
 
     
   
 订阅
  捐助
Greenplum初探-数据哈希分布与集群扩容
 
作者: 程序员致知录
   次浏览      
2020-9-29
 
编辑推荐:
本文介绍了 Greenplum(GP)分布式数据库的哈希分布策略,希望对您的学习有所帮助。
本文来自程序员致知录,由火龙果软件Alice编辑、推荐。

Greenplum(GP)是分布式数据库,因此,数据的分布是基础。GP提供了多种分布策略:哈希分布、随机分布和复制表。其中,最常用的就是哈希分布。本篇文章我将向大家介绍GP的哈希分布。

首先,我们先回顾一下上篇文章用于调试的那张表:

CREATE table t1 AS SELECT g c1, g + 1 as c2 FROM generate_series(1, 10) g DISTRIBUTED BY (c1);

大家可以看到建表语句末尾有DISTRIBUTED BY (c1),这就表示上面这张表是一张哈希分布表,且通过列c1的值散列数据。我们再来看下表里的数据:

因为psql默认连接的是Master,所以这里只能看到整张表的数据。如果我们想观察数据在Segment中的分布情况,那么有没有办法只看某个Segment中存储的数据呢?答案是有的。由于Segment也是一个PostgresQL(PG)实例,psql提供了一个utility模式,可以直接连接Segment(注意不要通过此模式,绕过Master,直接在Segment执行DDL或数据插入语句,这样做可能会引发集群异常)。执行以下命令,直连Segment:

PGOPTIONS='-c gp_session_role=utility' psql -p 6000 postgres

连接上Segment后,再次查看Segment上t1表的数据:

由于我们上次搭建的环境只有一台Segment,表t1的数据理所当然全部存储在这台Segment上。所以,下面我们需要将集群扩容,再将t1的数据重分布,然后再观察。

扩容

GP的官方文档中,有对扩容的详细介绍,具体可以查阅《GPDB62Docs》—— Chapter 4 Greenplum Database Administrator Guide —— Managing a Greenplum System —— Expanding a Greenplum System。

1,初始化新的Segments

cd /home/gpadmin/
#以交互模式创建扩容输入文件
gpexpand
# 是否初始化新的扩容
> y
# 输入需要扩容的主机名,多个主机之间以逗号分隔(若只在当前已存在主机增加segments,输入空行即可)
>
# 输入新加入的primary segments数量
> 1
# 输入新primary segments的目录
> /home/data/primary

完成后,会在当前目录生成一个input file,gpexpand_inputfile_yyyymmdd_xxxx。

2,执行扩容

# 扩容前需要确保系统安装了rsync命令,没有的话先yum -y install rsync
gpexpand -i input_file
# 如果执行失败,再次执行前需回滚上次失败的扩容信息
# gpexpand --rollback

查看扩容状态:

gpstate -x

3,执行数据重分布

gpexpand -d 60:00:00

再次查看扩容状态:

4,删除本次扩容的schema信息:

gpexpand -c

我们看一下此时后台的进程数:

可以看到新的Segment已启动,至此,扩容已完成。

数据分布

扩容后,我们分别利用psql连接两个Segment,查看数据分布情况。

第一个Segment:

第二个Segment:

从上面两张图片可以看到,原先全部存储于第一个Segment中的数据被分散到了两个Segment中,不过数据比较倾斜(由于t1表的数据量比较少,当前的现象并不能说明什么问题)。

通过阅读官方文档《GPDB62Docs》可知,从6.0版本开始,GP使用了新的哈希算法——Jump Consistent Hash。这是谷歌提出的一个一致性哈希算法,效率很高。本文暂不对这个算法展开讨论,我们直接从源码中找到这个算法的代码:

/*
* The following jump consistent hash algorithm is
* just the one from the original paper:
* https://arxiv.org/abs/1406.2294
*/
static inline int32


jump_consistent_hash(uint64 key, int32 num_segments)
{
int64 b = -1;
int64 j = 0;
while (j < num_segments)
{
b = j;
key = key * 2862933555777941757ULL + 1;
j = (b + 1) * ((double)(1LL << 31) / (double)((key >> 33) + 1));
}
return b;
}

据此,我们可以验证一下,上面的数据是否按照此算法分布:

直接通过列c1的值去哈希,得到的结果和t1表实际的数据分布是不一致的。为此,我们可以通过调试源码,一探究竟(调试方法大家可以回顾一下上一篇文章)。这次我们直接在jump_consistent_hash处打一个断点,通过psql执行一条sql:

insert into t1 values(1, 3);

程序中断后查看传入jump_consistent_hash的变量值:

可以发现,传入jump_consistent_hash的入参并不是我们插入的c1的值1,而是变成了2389907270,很明显这是类似哈希值的一个东东。继续看下当前堆栈:

向上溯源找到nodeResult.c:279处的源码,我们看下h->hash是如何产生的:

图中1处,将h->hash置为0。图2根据c1的值重新计算hashkey,并赋给h->hash。继续查看cdbhash的代码:

在cdbhash中可以看到,datum即为c1的真实值1,通过FunctionCallInvoke(&fcinfo)产生了新的hkey,通过调试可以发现,此处调用的函数即hashint4:

Datum hashint4(PG_FUNCTION_ARGS)
{
return hash_uint32(PG_GETARG_INT32(0));
}
Datum hash_uint32(uint32 k)
{
register uint32 a,
b,
c;
a = b = c = 0x9e3779b9 + (uint32) sizeof(uint32) + 3923095;
a += k;
final(a, b, c);
/* report the result */
return UInt32GetDatum(c);
}

看到这里,真相大白。原来是根据列的类型调用相应的hash函数,先对列值计算了一次hash值,然后再对此哈希值进行jump哈希找到对应的segment编号。如此一来,我们在测试代码中添加hash_uint32,继续验证:

可以看到,这次的验证结果与实际的数据分布结果是一致的。至此,GP的哈希分布策略介绍完毕。

 

   
次浏览       
相关文章

基于EA的数据库建模
数据流建模(EA指南)
“数据湖”:概念、特征、架构与案例
在线商城数据库系统设计 思路+效果
 
相关文档

Greenplum数据库基础培训
MySQL5.1性能优化方案
某电商数据中台架构实践
MySQL高扩展架构设计
相关课程

数据治理、数据架构及数据标准
MongoDB实战课程
并发、大容量、高性能数据库设计与优化
PostgreSQL数据库实战培训
最新活动计划
LLM大模型应用与项目构建 12-26[特惠]
QT应用开发 11-21[线上]
C++高级编程 11-27[北京]
业务建模&领域驱动设计 11-15[北京]
用户研究与用户建模 11-21[北京]
SysML和EA进行系统设计建模 11-28[北京]
 
最新文章
InfluxDB概念和基本操作
InfluxDB TSM存储引擎之数据写入
深度漫谈数据系统架构——Lambda architecture
Lambda架构实践
InfluxDB TSM存储引擎之数据读取
最新课程
Oracle数据库性能优化、架构设计和运行维护
并发、大容量、高性能数据库设计与优化
NoSQL数据库(原理、应用、最佳实践)
企业级Hadoop大数据处理最佳实践
Oracle数据库性能优化最佳实践
更多...   
成功案例
某金融公司 Mysql集群与性能优化
北京 并发、大容量、高性能数据库设计与优化
知名某信息通信公司 NoSQL缓存数据库技术
北京 oracle数据库SQL优化
中国移动 IaaS云平台-主流数据库及存储技术
更多...