Greenplum 編譯、安裝、和調試

<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 特性,否則⽆法初始化集群。

  1. 重啟操作系統
  2. 重啟過程中按下 command+R進⼊恢復模式
  3. 從 Utilities菜單選擇 Terminal
  4. 執 ⾏ csrutildisable
  5. 重啟操作系統

其次,安裝Greenplum管理腳本依賴的 Python 包

<code>$ wget 

https:

/

/bootstrap.pypa.io/get

-pip.py $ sudo python get-pip.py $ sudo pip install psutil lockfile paramiko setuptools epydoc/<code>

然後,需要安裝 openssl,否則⽆法編譯

<code>$ brew 

install

zstd openssl && brew

link

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] $ make

install

/<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.sh

psql postgres

SELECT version()

/<code>

有關更詳細的信息請參考 README.macOS.md。

1.2 在 Redhat/Centos系統上編譯

本⼩節以 RHEL7 為例介紹如何編譯Greenplum。

⾸先,下載 Greenplum 源代碼

<code>$ git 

clone

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 $ wget

https:

/

/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 |

g2

0

| g20 |

2

| 0 |

p

| p |

s

| u |

25432

| g20 |

g2

0

| 25438 3 |

1

| p |

p

| s |

u

| 25433 |

g2

0

| g20 |

25439

4

| 2 |

p

| p |

s

| u |

25434

| g20 |

g2

0

| 25440 5 |

0

| m |

m

| s |

u

| 25435 |

g2

0

| g20 |

25441

6

| 1 |

m

| m |

s

| u |

25436

| g20 |

g2

0

| 25442 7 |

2

| m |

m

| s |

u

| 25437 |

g2

0

| 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.sh
 

source

$HOME

/gpdb.master/greenplum_path.sh

export

PGPORT=5432

export

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_hostname

MASTER_DIRECTORY

=/path/to/your/data/master

MASTER_PORT

=

5432

TRUSTED_SHELL

=ssh

CHECK_POINT_SEGMENTS

=

8

ENCODING

=UNICODE

MACHINE_LIST_FILE

=hostfile/<code>
  • step 4. 初始化Greenplum 集群
<code>

source

env.sh

gpinitsystem -c gpinitsystem_config -a

/<code>
  • step 5. 初始化成功後,運⾏下⾯命令驗證系統狀態
<code>$ psql -l
$ gpstate/<code>
  • step 6. 簡單測試
<code>$ createdb 

test

$ psql

test

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 的⽇志,⼀種是數據庫的⽇志。它們分別保存在不同的⽬錄下:

  • gpinitsystem 的⽇志⽂件。默認路徑為 ~/gpAdmin/gpinitsystem_***
  • 數據庫的⽇志⽂件:進⼊ master ( segment 的⽇志類似) 的⽇志⽬錄 ( 例如/data/master/gpseg-1/pg_log/)查看⽇志。 這⾥⾯有2種類型的⽇志:
  • startup.log
  • gpdb-.csv
  • 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: could

    not

    connect

    to server: No such file

    or

    directory Is the server running locally

    and

    accepting connections on Unix domain

    socket

    "/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 to 

    import

    module

    : No

    module

    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

    , classname

    text

    , student_id

    int

    )

    DISTRIBUTED

    BY

    (

    id

    );

    INSERT

    INTO

    students

    VALUES

    (

    1

    ,

    'steven'

    ), (

    2

    ,

    'changchang'

    ), (

    3

    ,

    'guoguo'

    );

    INSERT

    INTO

    classes

    VALUES

    (

    1

    ,

    'math'

    ,

    1

    ), (

    2

    ,

    'math'

    ,

    2

    ), (

    3

    ,

    'physics'

    ,

    3

    );/<code>

    以下⾯的SQL為例⼦,瞭解 SQL 在 Greenplum 中的執⾏過程:

    <code>

    SELECT

    s.name student_name, c.classname

    FROM

    students s, classes c

    WHERE

    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>
    Greenplum 編譯、安裝、和調試

    從上圖可以很明顯看出該計劃包含兩個 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),返回給客戶端。

    Greenplum 編譯、安裝、和調試

    4 調試Greenplum MPP 數據庫

    4.1 調試 Master節點Backend進程

    調試 Master 的Backend進程(也稱為 QD)和調試單節點的PostgreSQL ⾮常類似。通常遇到解析、優化、調度相關問題時,需要調試QD。

    下⾯以⼀個例⼦介紹如何調試 GreenplumQD進程。啟動兩個窗⼝,⼀個運⾏psql,⼀個運⾏ lldb

    Greenplum 編譯、安裝、和調試

    Greenplum 編譯、安裝、和調試

    使⽤ 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(VERSION 

    3.8

    ) project(gpdb) set(CMAKE_CXX_STANDARD

    11

    ) 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請見前面小節)

    Greenplum 編譯、安裝、和調試

    如果 clion調試器console顯示類似 “Debugger attached to process 38965” 的消息,則表示進程attach成功,可以使⽤ clion進⾏調試了。

    通過圖像化窗⼝定位到 “ExecProcNode” 函數,通過單擊下圖的⼩紅圈處,即可設置斷點在 ExecAgg() 調⽤處。

    Greenplum 編譯、安裝、和調試

    執⾏ SELECT count(*) FROM students 語句,可以使⽤各種調試命令(例如單步執⾏、斷點、跳出函數等)⽅便的調試代碼。

    Greenplum 編譯、安裝、和調試

    如上圖所示,可以通過 IDE很直觀的看到正在執⾏的代碼⽚段,以及函數中變量的值。對於學習和調試Greenplum⾮常有幫助。

    來源:Greenplum中文社區


    分享到:


    相關文章: