<code>本⽂先介绍如何从源代码编译安装Greenplum、初始化Greenplum集群。然后介绍SQL在Greenplum中的典型执⾏路径,最后介绍⼀些调试技巧。/<code>
源 代 码 使 ⽤ Greenplum 开 源 社 区 最 新 源 代 码 6X_STABLE 分 ⽀ :ghttps://github.com/greenplum-db/gpdb%EF%BC%8C 内核代码基于 PostgreSQL9.4。⽬前(2019/04/23) 主⼲分⽀的代码基于 PostgreSQL 9.4 。合并到 PostgreSQL 9.5 的⼯作也已经开始, 有关最新⼯作进展请参⻅:https://github.com/greenplum-db/gpdb-postgres-merge%E3%80%82
1 从源代码编译 Greenplum
Greenplum ⽬前官⽅⽀持 Redhat/Centos/SuSE/Ubuntu 等Linux系统。⼤量开发⼈员包括我⾃⼰使⽤Mac系统,但是不在官⽅⽀持列表中。
1.1 在 Mac系统上编译
⾸先需要关闭苹果操作系统的 SIP 特性,否则⽆法初始化集群。
- 重启操作系统
- 重启过程中按下 command+R进⼊恢复模式
- 从 Utilities菜单选择 Terminal
- 执 ⾏ csrutildisable
- 重启操作系统
其次,安装Greenplum管理脚本依赖的 Python 包
<code>$ wgethttps:
//bootstrap.pypa.io/get
-pip.py $ sudo python get-pip.py $ sudo pip install psutil lockfile paramiko setuptools epydoc/<code>
然后,需要安装 openssl,否则⽆法编译
<code>$ brewinstall
zstd openssl && brewlink
openssl $ CPPFLAGS="-I/usr/local/include/ -I/usr/local/opt/openssl/include"
\ LDFLAGS="-L/usr/local/lib -L/usr/local/opt/openssl/lib"
\ CFLAGS="-O0 -g3 -ggdb3"
\ ./configure $ make [-j4] $ makeinstall
/<code>
最后,在苹果系统上初始化Greenplum单节点集群时,需要做些准备⼯作:
- 添加export PGHOST=localhost⾄~/.bash_profile
- 将本机的hostname与127.0.0.1的map写到/etc/hosts中。例如
<code> 127.0
.0
.1
yydzero
yydzero
.local
/<code>
- 修改/etc/sysctl.conf⽂件,并重启:
<code>kern.sysv.shmmax=2147483648 kern.sysv.shmmin=1 kern.sysv.shmmni=64 kern.sysv.shmseg=16 kern.sysv.shmall=524288 kern.maxfiles=65535 kern.maxfilesperproc=65535 net.inet.tcp.msl=60
cd
gpAux/gpdemo
source
$HOME
/gpdb.master/greenplum_path.sh
export
PGHOST=`hostname`make
source
gpdemo-env.shpsql postgres
SELECT version()
/<code>
有关更详细的信息请参考 README.macOS.md。
1.2 在 Redhat/Centos系统上编译
本⼩节以 RHEL7 为例介绍如何编译Greenplum。
⾸先,下载 Greenplum 源代码
<code>$ gitclone
https:/<code>
其次,Greenplum Database 编译和运⾏依赖于各种系统库和Python库。需要先安装这些依赖:
<code>$ sudo yum groupinstall'Development Tools'
$ sudo yum install curl-devel bzip2-devel python-devel openssl-devel readline-devel libzstd-devel $ sudo yum install perl-ExtUtils-Embed $ sudo yum install libxml2-devel $ sudo yum install openldap-devel $ sudo yum install pam pam-devel $ sudo yum install perl-devel $ wgethttps:
//bootstrap.pypa.io/get
-pip.py $ sudo python get-pip.py $ sudo pip install psutil lockfile paramiko setuptools epydoc/<code>
再次,编译 Greenplum Database 源代码,假定安装到 $HOME/gpdb.master ⽬录下
<code>$ CFLAGS="-O0 -g3 -ggdb3"
\ ./configure --with-perl --with-python --with-libxml --enable
-debug --enable
-cassert \ --disable
-orca --disable
-gpcloud --disable
-gpfdist \ --prefix=/home/gpadmin/gpdb.master $ make $ make install/<code>
<code>
cd
gpAux/gpdemo
source
/home/gpadmin/gpdb.master/greenplum_path.sh
export
PGHOST=`hostname`make
source
gpdemo-env.sh/<code>
<code>$
psql
postgres
postgres#
SELECT
version()
version
-----------------------------------------------------------------
PostgreSQL
8.3
.23
(Greenplum
Database
5.4
.1
+dev.56.gcdfadd9
build
dev)
...
/<code>
<code>postgres= dbid| content |
role| preferred_role |
mode| status |
port| hostname |
address| replication_port ------+---------+------+----------------+------+--------+-------+----------+---------+------------------ 1 |
-1
| p |
p| s |
u| 15432 |
g20
| g20 |
2
| 0 |
p| p |
s| u |
25432
| g20 |
g20
| 25438 3 |
1
| p |
p| s |
u| 25433 |
g20
| g20 |
25439
4
| 2 |
p| p |
s| u |
25434
| g20 |
g20
| 25440 5 |
0
| m |
m| s |
u| 25435 |
g20
| g20 |
25441
6
| 1 |
m| m |
s| u |
25436
| g20 |
g20
| 25442 7 |
2
| m |
m| s |
u| 25437 |
g20
| g20 |
25443
/<code>
2 初始化 Greenplum 集群
前⾯编译部分介绍了如何使⽤ Greenplum 源代码中的 demo 集群脚本创建集群。这种⽅法简单快捷,让⽽屏蔽了很多细节。
2.1 ⼿⼯集群初始化
下⾯介绍如何⼿⼯部署⼀个单机集群:在⼀台笔记本上安装⼀个Greenplum的集群,包括⼀个master,两个segments。
- step 0. 系统环境配置
<code>$
/etc/sysctl.conf
kernel.shmmax
=
500000000
kernel.shmmni
=
4096
kernel.shmall
=
4000000000
kernel.sem
=
250
512000
100
2048
kernel.sysrq
=
1
kernel.core_uses_pid
=
1
kernel.msgmnb
=
65536
kernel.msgmax
=
65536
kernel.msgmni
=
2048
net.ipv4.tcp_syncookies
=
1
net.ipv4.conf.default.accept_source_route
=
0
net.ipv4.tcp_tw_recycle
=
1
net.ipv4.tcp_max_syn_backlog
=
4096
net.ipv4.conf.all.arp_filter
=
1
net.ipv4.ip_local_port_range
=
10000
65535
net.core.netdev_max_backlog
=
10000
net.core.rmem_max
=
2097152
net.core.wmem_max
=
2097152
vm.overcommit_memory
=
2
$
cat
/etc/security/limits.conf
*
soft
nofile
65536
*
hard
nofile
65536
*
soft
nproc
131072
*
hard
nproc
131072
$
sudo
reboot
/<code>
- step 1. source⼀些环境变量, 例如PATH
<code>
source
$HOME
/gpdb.master/greenplum_path.sh/<code>
- step 2. 交换集群中所有机器的ssh密钥, 我们这⾥只有⼀台机器
<code>$ gpssh-exkeys -h`hostname`
/<code>
- step 3. ⽣成三个配置⽂件:env.sh, hostfile, gpinitsystem_config
<code>$ cat env.shsource
$HOME
/gpdb.master/greenplum_path.shexport
PGPORT=5432export
MASTER_DATA_DIRECTORY=$HOME
/data/master/gpseg-1/<code>
# hostfile 包括集群中所有机器的hostname, 我们这⾥只有⼀台
<code>cat hostfile
cat gpinitsystem_config
ARRAY_NAME
="Open Source Greenplum"
SEG_PREFIX
=gpseg
PORT_BASE
=40000
/<code>
# 根据需要,修改下⾯的路径和主机名
# 有⼏个DATA_DIRECTORY, 每个节点上便会启动⼏个segments
<code>declare
-a DATA_DIRECTORY=(/path
/to
/your/data
/path
/to
/your/data
)/<code>
# master的主机名, 路径和端⼝
<code>MASTER_HOSTNAME
=your_hostnameMASTER_DIRECTORY
=/path/to/your/data/masterMASTER_PORT
=5432
TRUSTED_SHELL
=sshCHECK_POINT_SEGMENTS
=8
ENCODING
=UNICODEMACHINE_LIST_FILE
=hostfile/<code>
- step 4. 初始化Greenplum 集群
<code>
source
env.shgpinitsystem -c gpinitsystem_config -a
/<code>
- step 5. 初始化成功后,运⾏下⾯命令验证系统状态
<code>$ psql -l $ gpstate/<code>
- step 6. 简单测试
<code>$ createdbtest
$ psqltest
test
test
gp_segment_id | count ---------------+--------- 0 | 501 1 | 499/<code>
有关如何安装多节点集群,请参考Greenplum官方安装文档。
2.2 集群初始化问题调试
有时候 gpinitsystem 会失败,但是不清楚失败原因是什么。下⾯提供⼀些思路来 RCA:
2.2.1 使⽤ gpinitsystem调试模式
gpinitsystem有⼀个 -D选项,使⽤这个选项可以看到更多的输出信息,根据这些额外的输出信息可以发现并解决⼤部分问题。
2.2.2 查看⽇志
常⽤的⽇志⽂件有两类,⼀种是 gpinitsystem 的⽇志,⼀种是数据库的⽇志。它们分别保存在不同的⽬录下:
2.2.3 初始化 master数据库失败
⼿动执⾏initdb查看详细错误信息,然后分析具体错误信息采取相应错误。不同的版本可能参数不同,可以通过在 gpinitsystem 脚本中找到完整的命令。
<code>$ initdb -E UNICODE -D /data/master/gpseg-1
/<code>
2.2.4 master起不来
使⽤下⾯命令,⼿动启动master观看⽇志是否有问题。下⾯使⽤ Utility 模式启动master, 仅仅允许utility 模式连接。
<code>$
postgres
-D
/data/master/gpseg-1
-i
-p
5432
-c
gp_role=utility
-M
master
-b
1
-C
-1
-z
0
-m
/<code>
2.2.5 启动Segment出错
如果启动 segment 时出错,并且看不到具体错误信息(通常由于错误信息被重定向到/dev/null 了),则可以尝试⼿动启动 segment。
⼿动启动segment的命令参考下⾯,需要根据⾃⼰的环境修改某些路径或者参数:
<code>export LD_LIBRARY_PATH=/home/gpadmin
/build/gpdb
.master/lib:
/lib:
;export PGPORT=40006
;/home/gpadmin
/build/gpdb
.master/bin/pg_ctl -w -l /data2/primary/gpseg18/pg_log/startup.log -D /data2/primary/gpseg18 -o"-i -p 40006 -M mirrorless -b 20 -C 18 -z 0"
start/<code>
有时候单独执⾏各种命令没有问题,但是使⽤ SSH 执⾏时报错。
这通常是由于 ssh 改变了环境变量造成的,查看 .bash_profile, .bashrc, 发现 .bashrc 设置了不同的默认 PGHOST,删除这个配置后就可以了。
2.2.6 不能连接到server:找不到domain socket
<code>○ → PGOPTIONS='-c gp_session_role=utility'
/Users/yydzero/work/build/master/bin/psql postgres psql: couldnot
connect
to server: No such fileor
directory Is the server running locallyand
accepting connections on Unix domainsocket
"/var/pgsql_socket/.s.PGSQL.5432"
?/<code>
这个通常是由于不同的 psqlbinary造成的,也就是说⾃⼰编译的 psql调⽤了系统的 libpq 库。可以通过 ldd或者 otool-L查看。
解决⽅法:
<code>export
LD_LIBRARY_PATH=/path/
to/your/psql/lib/<code>
2.2.7 gpstart失败,并且原因不明
$gpstart-v//使⽤ verbose模式,显示每个执⾏的命令以及其结果。遇到的⼀个问题报错如下:
<code>unable toimport
module
: Nomodule
named psutil/<code>
原因是 psutil 这个python包没有安装,但是使⽤ python 验证,发现已经安装了。⽽使⽤ssh 验证发现使⽤了不同路径的 python。
2.2.8 关闭 IPv6
如果遇到下⾯错误,则关闭 IPv6:
<code>ping
cmdStr='/bin/ping6 -c 1 gp1′ had result: cmd had rc=2 completed=True halted=False
stdout
=”
stderr
='connect: Invalid argument'
/<code>
如何关闭 IPv6:
加⼊以下两⾏到⽂件:/etc/sysctl.conf
<code>net.ipv6.conf.all.disable_ipv6
=1
net.ipv6.conf.default.disable_ipv6
=1
/<code>
然后 $ sysctl -p
2.2.9 小技巧
Greenplum使⽤ Bash和 Python脚本初始化集群和管理集群。可以通过在合适的地⽅设置⽇志或者调试信息可以帮助分析某些难以解决的问题。
- 集群初始化⼯具 gpinitsystem 是Bash脚本⼯具,有些时候它的报错信息很不清楚。这个时候可以
- 使⽤ -D 选项
- gp_bash_functions.sh 是内部⼀个被频繁调⽤执⾏系统命令的函数,可以通过set -x 可以打印出所有执⾏的命令的详细信息。对调试 hang 问题很有效。
- 在合适的代码处启⽤ Python 调试器,如果不知道什么地⽅合适,则在⼊⼝处。
3 Greenplum SQL执⾏流程概要
下⾯介绍下 Greenplum 中 SQL 执⾏的简单过程。例⼦中集群⼀个 Master 两个Segments。
准备简单的数据:
<code>CREATE
TABLE
students (id
int
,name
text
)DISTRIBUTED
BY
(id
);CREATE
TABLE
classes(id
int
, classnametext
, student_idint
)DISTRIBUTED
BY
(id
);INSERT
INTO
studentsVALUES
(1
,'steven'
), (2
,'changchang'
), (3
,'guoguo'
);INSERT
INTO
classesVALUES
(1
,'math'
,1
), (2
,'math'
,2
), (3
,'physics'
,3
);/<code>
以下⾯的SQL为例⼦,了解 SQL 在 Greenplum 中的执⾏过程:
<code>SELECT
s.name student_name, c.classnameFROM
students s, classes cWHERE
s.id=c.student_id;/<code>
3.1 查询计划
其对应的查询计划如下所示:
<code>test=#
explain
SELECT
s.name
student_name,
c.classname
test-#
FROM
students
s,
classes
c
test-#
WHERE
s.id=c.student_id;
QUERY
PLAN
-----------------------------------------------------------------------------------------------
Gather
Motion
2
:1
(slice2;
segments:
2
)
(cost=2.07..4.21
rows=4
width=14)
->
Hash
Join
(cost=2.07..4.21
rows=2
width=14)
Hash Cond:
c.student_id
=
s.id
->
Redistribute
Motion
2
:2
(slice1;
segments:
2
)
(cost=0.00..2.09
rows=2
width=10)
Hash Key:
c.student_id
->
Seq
Scan
on
classes
c
(cost=0.00..2.03
rows=2
width=10)
->
Hash
(cost=2.03..2.03
rows=2
width=12)
->
Seq
Scan
on
students
s
(cost=0.00..2.03
rows=2
width=12)
Optimizer status:
legacy
query
optimizer
/<code>
使用 explain.pl 可以生成如下的查询计划图:(把上面的explain结果保存到一个名为 a.plainplan 的文件中)
<code> $ explain.pl -opt jpg/tmp/a
.plainplan >/tmp/a
.jpg/<code>
从上图可以很明显看出该计划包含两个 slice,slice 使⽤的motion为重分布。
hashjoin的两个表为students和classes, 它们的分布键都是其 id, ⽽ join的键值是student.id=classes.student_id其中 student的join键是其主键, 因⽽不需要数据移动(motion);⽽classes的关联键是 student_id,和其分布键不同,因⽽需要数据移动(motion),以保证相同关联键的数据都在同⼀个 segment 上。
感兴趣的读者可以尝试把 stendent 的分布键改成其他字段,看看计划有什么变化。
3.2 查询执行
QD(Query Dispatcher) 将上⾯的并⾏计划分发到每个 segment 上执⾏。这个例⼦中⼀共有2个segments。
查询计划包含2个slices,所以每个 segment 会启动 2 个 QE(Query Executor),⼀个QE 负责执⾏⼀个 slice 对应的任务。
同⼀个 slice 在每个 segment 的 QE形成⼀个 Gang,它们在不同的segment上执⾏相同的任务。
HashJoin需要相同关联键的所有数据都在⼀个 segment上,因⽽如果关联键不是分布键, 则需要数据移动。在这个例⼦中classes的分布键(id)和关联键(student_id) 不同,所以需要数据重分布。
数据重分布由 Motion 操作符节点处理,它分成2个部分,⼀部分负责发送数据,⼀部分负责接收数据。发送数据者可以根据不同的策略将数据发送给接收⽅,现在⽀持的策略有1)重分布(redistribution);2)⼴播(broadcast)。
最后每个segment执⾏结束后,将结果发送给 Master。Master对最终的数据整合(Gather Motion),返回给客户端。
4 调试Greenplum MPP 数据库
4.1 调试 Master节点Backend进程
调试 Master 的Backend进程(也称为 QD)和调试单节点的PostgreSQL ⾮常类似。通常遇到解析、优化、调度相关问题时,需要调试QD。
下⾯以⼀个例⼦介绍如何调试 GreenplumQD进程。启动两个窗⼝,⼀个运⾏psql,⼀个运⾏ lldb
使⽤ lldb 的 gui 命令可以使⽤⼀个简单的源代码浏览器查看当前正在执⾏的代码区域,以及执⾏函数的相关变量。
通过简单的断点和单步执⾏,可以快速了解SQL的执⾏过程。譬如上⾯例⼦中可以看到cdbdisp_dispatchToGang 在 ExecutorStart 之后、ExecutorRun 之前运⾏,⽤途是将 QD 优化好的计划分发给 segments 执⾏。
4.2 调试 Segment节点Backend进程 (QE)
调试 segment 进程(通常是 QE)和调试master上的进程⼀样,唯⼀的区别是如何获得进程的id?
此时不能通过 pg_backend_pid() 获得,因为该pid是 QD 的进程号。常⽤的⽅法是通过执⾏2次 SQL,获得 QE 的进程号。
Greenplum为了提⾼效率, 降低创建 Gang/QEs的代价, 通常会重⽤已经创建的Gang/QEs。利⽤这⼀特性,可以⽅便的找到每个 segment上 QE 的pid。
先执⾏⼀次想要调试的 SQL。然后使⽤下⾯的命令找出感兴趣的 QE 的pid。
这个例⼦中进程38965 是 QD进程, 41210是 segment0上的 QE进程, 41211是segment 1 上的 QE 进程。
<code>○ →ps
-ef
|grep
postgres
|grep
idle
503
38965
38387
0
9
:35PM
0
:00.46
postgres
:5432
,yydzero
test
::1(51161)
con9
cmd65
idle
503
41210
38354
0
10
:39PM
0
:00.10
postgres
:40000
,yydzero
test
**(51490
)con9
seg0
idle
503
41211
38355
0
10
:39PM
0
:00.11
postgres
:40001
,yydzero
test
**(51491
)con9
seg1
idle
/<code>
知道了 QE的进程号,使⽤ lldbattach到该进程,重新执⾏ SQL就可以进⾏调试了。Gang/QEs的重⽤时间由 GUCgp_vmem_idle_resource_timeout控制。
4.3 使⽤ IDE调试
常⽤的调试器gdb/lldb虽然简单易⽤、 功能也很强⼤,但是不直观。很多集成开发环境(IDE)提供了⾮常直观、强⼤、易⽤的调试环境,包括 clion、eclipse、xcode 等。IDE 对于学习 Greenplum 代码也⾮常有帮助,可以⼤⼤提⾼效率。
下⾯简单介绍如何使⽤ clion 图形化⽤户界⾯调试 Greenplum 代码。( Eclipse、VisualCode具有类似功能)
Greenplum进程都是 daemon进程,很难通过启动⽅式进⼊调试器。因⽽通常使⽤的⽅法是 attach到已经运⾏的进程。
⾸先启动 clion,导⼊ Greenplum源代码项⽬。clion需要 CMakeLists.txt⽂件构建⼯程项⽬。将下⾯的 CMakeLists.txt放到 Greenplum源代码⽬录的顶层⽬录中,再启动 clion既可建⽴合适的⼯程项⽬。
<code>$ cat CMakeLists.txt cmake_minimum_required(VERSION3.8
) project(gpdb) set(CMAKE_CXX_STANDARD11
) include_directories(src/include
src/backend/gp_libpq_fe) file(GLOB_RECURSE SOURCE_FILES"src"
"*.c"
"*.h"
) add_executable(gpdb ${SOURCE_FILES})/<code>
然后选择 Run → Attach to Local Process… 出现下面 “Attach with LLDB to” 窗口。选择需要调试的进程id即可。(如果确定进程id请见前面小节)
如果 clion调试器console显示类似 “Debugger attached to process 38965” 的消息,则表示进程attach成功,可以使⽤ clion进⾏调试了。
通过图像化窗⼝定位到 “ExecProcNode” 函数,通过单击下图的⼩红圈处,即可设置断点在 ExecAgg() 调⽤处。
执⾏ SELECT count(*) FROM students 语句,可以使⽤各种调试命令(例如单步执⾏、断点、跳出函数等)⽅便的调试代码。
如上图所示,可以通过 IDE很直观的看到正在执⾏的代码⽚段,以及函数中变量的值。对于学习和调试Greenplum⾮常有帮助。
来源:Greenplum中文社区
關鍵字: 编译 gpinitsystem 集群