GreenPlum数据加载

Posted lottu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了GreenPlum数据加载相关的知识,希望对你有一定的参考价值。

1. copy命令

  对于数据加载,GreenPlum数据库提供copy工具,copy工具源于PostgreSQL数据库,copy命令支持文件与表之间的数据加载和表对文件的数据卸载。使用copy命令进行数据加载,数据需要经过Master节点分发到Segment节点,同样使用copy命令进行数据卸载,数据也需要由Segment发送到Master节点,由Master节点汇总后再写入外部文件,这样就限制了数据加载与卸载的效率,但是数据量较小的情况下,copy命令就非常方便。下面测试通过copy命令实现操作系统文件到数据库中表的数据加载。

1.1 创建测试表

lottu=# create table tbl_pay_log_copy (id int primary key,order_num varchar(100),accountid varchar(30),qn varchar(20),appid int,amount numeric(10,2),pay_time timestamp);
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "tbl_pay_log_copy_pkey" for table "tbl_pay_log_copy"
CREATE TABLE
lottu=# \\d tbl_pay_log_copy
           Table "public.tbl_pay_log_copy"
  Column   |            Type             | Modifiers 
-----------+-----------------------------+-----------
 id        | integer                     | not null
 order_num | character varying(100)      | 
 accountid | character varying(30)       | 
 qn        | character varying(20)       | 
 appid     | integer                     | 
 amount    | numeric(10,2)               | 
 pay_time  | timestamp without time zone | 
Indexes:
    "tbl_pay_log_copy_pkey" PRIMARY KEY, btree (id), tablespace "tbs_lottu"
Distributed by: (id)
Tablespace: "tbs_lottu"

1.2 准备测试数据

[gpadmin@mdw ~]$ head ios_pay.txt 
73,ysios_receipt_3793615cb10dc393bba87c82d3c6544f,27388062,yriu1244_16043_001,2616,98.00,2017-11-06 17:36:43
72,ysios_receipt_32946f3d37e774781babe103352bd230,27424976,yriu1244_16043_001,2616,30.00,2017-11-06 15:18:56
75,ysios_receipt_3e2e432550253450412692392c7675d0,27388294,yriu1244_16043_001,2616,98.00,2017-10-19 07:33:03
74,ysios_receipt_3793615cb10dc393bba87c82d3c6544f,27388062,yriu1244_16043_001,2616,98.00,2017-11-06 20:40:46
77,ysios_receipt_ee6bed338a32f836a999133cd2e6d547,27388294,yriu1244_16043_001,2616,98.00,2017-10-19 22:27:46
76,ysios_receipt_ae53b142924c0604820537d61a9dd73e,27424976,yriu1244_16043_001,2616,648.00,2017-10-19 12:10:17
79,ysios_receipt_30ec130bcdf0e864629d12f8392d4b43,27385229,yriu1244_16043_001,2616,98.00,2017-10-21 07:46:01
78,ysios_receipt_e2b62024f1b0c3a2c3aae1e80f126eb6,27387306,yriu1244_16043_001,2616,25.00,2017-10-20 01:54:24
81,ysios_receipt_3e72a8e32c9fee546ab08d103606e6cb,27424976,yriu1244_16043_001,2616,30.00,2017-10-21 13:55:54
80,ysios_receipt_6ca291884fcfe3d1583b49a3611b4ccc,27424976,yriu1244_16043_001,2616,25.00,2017-10-21 13:55:51
[gpadmin@mdw ~]$ wc -l ios_pay.txt 
20027 ios_pay.txt
[gpadmin@mdw ~]$ du -sh ios_pay.txt 
1.9M    ios_pay.txt

  现在文本“ios_pay.txt”有20027行记录;大小约2M。

1.3 copy命令语法

lottu=# \\h copy
Command:     COPY
Description: copy data between a file and a table
Syntax:
COPY table [(column [, ...])] FROM {\'file\' | STDIN}
     [ [WITH] 
       [OIDS]
       [HEADER]
       [DELIMITER [ AS ] \'delimiter\']
       [NULL [ AS ] \'null string\']
       [ESCAPE [ AS ] \'escape\' | \'OFF\']
       [NEWLINE [ AS ] \'LF\' | \'CR\' | \'CRLF\']
       [CSV [QUOTE [ AS ] \'quote\'] 
            [FORCE NOT NULL column [, ...]]
       [FILL MISSING FIELDS]
     [ [LOG ERRORS INTO error_table] [KEEP] 
       SEGMENT REJECT LIMIT count [ROWS | PERCENT] ]


COPY {table [(column [, ...])] | (query)} TO {\'file\' | STDOUT}
      [ [WITH] 
        [OIDS]
        [HEADER]
        [DELIMITER [ AS ] \'delimiter\']
        [NULL [ AS ] \'null string\']
        [ESCAPE [ AS ] \'escape\' | \'OFF\']
        [CSV [QUOTE [ AS ] \'quote\'] 
             [FORCE QUOTE column [, ...]] ]

常用参数
分隔符:[DELIMITER [ AS ] \'delimiter\']
处理空列(含有空格符的是不行的):[NULL [ AS ] \'null string\']
记录错误数据,错误日志表自动创建: [LOG ERRORS INTO error_table] [KEEP]
允许错误的行数或者百分比,大于指定值导入失败全部回滚:SEGMENT REJECT LIMIT count [ROWS | PERCENT] ]

1.4 数据加载

  执行必须拥有superuser权限的用户

lottu=# \\du lottu
                List of roles
 Role name |      Attributes      | Member of 
-----------+----------------------+-----------
 lottu     | Superuser, Create DB | 

  执行命令;使用默认参数如下:

命令:copy tbl_pay_log_copy from \'/home/gpadmin/ios_pay.txt\' with delimiter \',\' null \'\' ;

lottu=# copy tbl_pay_log_copy from \'/home/gpadmin/ios_pay.txt\' with delimiter \',\' null \'\' ;
ERROR:  invalid input syntax for type numeric: "w"  (seg0 sdw1:40000 pid=3183)
CONTEXT:  COPY tbl_pay_log_copy, line 32, column 3

出现错误行记录;先不讨论如何跳过错误行记录;执行copy命令失败;是否有必要对表进行VACUUM?可以验证下

lottu=# select ctid,id from tbl_pay_log_copy ;
 ctid | id 
------+----
(0 rows)

lottu=# insert into tbl_pay_log_copy (id) values (1);
INSERT 0 1
lottu=# select ctid,id from tbl_pay_log_copy ;
  ctid  | id 
--------+----
 (0,17) |  1
(1 row)

执行失败;数据是回滚了;但是插入记录并不是从(0,0)开始的;所以执行有必要对表进行VACUUM

  如何跳过错误行;加入下面参数即可

LOG ERRORS INTO参数指定错误数据记录到哪张表中
SEGMENT REJECT LIMIT参数指定最大跳过的错误数

命令:copy tbl_pay_log_copy from \'/home/gpadmin/ios_pay.txt\' with delimiter \',\' null \'\' LOG ERRORS INTO tbl_pay_log_copy_errs SEGMENT REJECT LIMIT 100;

lottu=# copy tbl_pay_log_copy from \'/home/gpadmin/ios_pay.txt\' with delimiter \',\' null \'\' LOG ERRORS INTO tbl_pay_log_copy_errs SEGMENT REJECT LIMIT 100;
NOTICE:  Error table "tbl_pay_log_copy_errs" does not exist. Auto generating an error table with the same name
WARNING:  The error table was created in the same transaction as this operation. It will get dropped if transaction rolls back even if bad rows are present
HINT:  To avoid this create the error table ahead of time using: CREATE TABLE <name> (cmdtime timestamp with time zone, relname text, filename text, linenum integer, bytenum integer, errmsg text, rawdata text, rawbytes bytea)
NOTICE:  Found 1 data formatting errors (1 or more input rows). Errors logged into error table "tbl_pay_log_copy_errs"
COPY 20026

  表tbl_pay_log_copy成功插入20026行记录;1条失败记录插入表tbl_pay_log_copy_errs;该表不需要创建;若库不存在表;则会创建

下面看下这20026条数据的数据分布情况。是否倾斜?

lottu=# select gp_segment_id,count(*) from tbl_pay_log_copy group by 1;
 gp_segment_id | count 
---------------+-------
             0 | 10013
             1 | 10013

1.5 数据卸载

  Copy工具不仅可以把数据从文件加载到数据库的表中,也可以将数据从数据库的表中卸载到操作系统的文件中。如下:

命令:copy tbl_pay_log_copy to \'/home/gpadmin/tbl_pay_log_copy_output.txt\' WITH DELIMITER AS \',\';

lottu=# copy tbl_pay_log_copy to \'/home/gpadmin/tbl_pay_log_copy_output.txt\' WITH DELIMITER AS \',\';
COPY 20026

  也可以根据query进行卸载;这样可以根据需求导出自己需要的记录或者列记录。如下:

命令:copy (select * from tbl_pay_log_copy where id = 73) to \'/home/gpadmin/tbl_pay_log_copy_id_73.txt\' WITH DELIMITER AS \',\';

lottu=# copy (select * from tbl_pay_log_copy where id = 73) to \'/home/gpadmin/tbl_pay_log_copy_id_73.txt\' WITH DELIMITER AS \',\';
COPY 1

  查看导出文件

[gpadmin@mdw ~]$ ll tbl_pay_log_copy_output.txt tbl_pay_log_copy_id_73.txt
-rw-r--r--. 1 gpadmin gpadmin     109 Apr 18 22:52 tbl_pay_log_copy_id_73.txt
-rw-r--r--. 1 gpadmin gpadmin 1917463 Apr 18 22:50 tbl_pay_log_copy_output.txt

  使用SELECT的COPY效率比直接使用表名COPY效率低很多,对于数据量稍大的表,可以考虑使用中间表结合COPY的方式来卸载数据

1.6 其他参数解释

  format:指定导入的文件格式为csv格式
  escape:指定了在引号中的转义字符为反斜杠,这样即使在引号字串中存在引号本身,也可以用该字符进行转义,变为一般的引号字符,而不是字段终结
  header true:指定文件中存在表头。如果没有的话,则设置为false
  quote:指定了以双引号作为字符串字段的引号,这样它会将双引号内的内容作为一个字段值来进行处理

2. 使用gpfdist的外部表

  外部表提供了对Greenplum数据库之外的来源中数据的访问。可以用SELECT语句访问它们,外部表通常被用于抽取、装载、转换(ELT)模式,这是一种抽取、转换、装载(ETL)模式的变种,这种模式可以利用Greenplum数据库的快速并行数据装载能力。这是COPY命令不持有的。


外部表加载方式有很多;详细介绍《请见Greenplum数据库参考指南》

gpfdist原理:

  gpfdist是一个使用HTTP协议的文件服务器程序,它以并行的方式向Greenplum数据库的Segment供应外部数据文件一个gpfdist实例,每秒能供应200MB并且很多gpfdist进程可以同时运行,每一个供应要被装载的数据的一部分。当使用者用INSERT INTO <table> SELECT * FROM <external_table>这样的语句开始装载时,INSERT语句会被Master解析并且分布给主Segment。Segment连接到gpfdist服务器并且并行检索数据,解析并验证数据,从分布键数据计算一个哈希值并且基于哈希键把行发送给它的目标Segment。每个gpfdist实例默认将接受最多64个来自Segment的连接。通过让许多Segment和gpfdist服务器参与到装载处理中,可以以非常高的速率被装载。

2.1 创建实验环境

lottu=# create table tbl_pay_log_gpfdist(id int primary key,order_num varchar(100),accountid varchar(30),qn varchar(20),appid int,amount numeric(10,2),pay_time timestamp);
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "tbl_pay_log_gpfdist_pkey" for table "tbl_pay_log_gpfdist"
CREATE TABLE
lottu=# \\d tbl_pay_log_gpfdist
         Table "public.tbl_pay_log_gpfdist"
  Column   |            Type             | Modifiers 
-----------+-----------------------------+-----------
 id        | integer                     | not null
 order_num | character varying(100)      | 
 accountid | character varying(30)       | 
 qn        | character varying(20)       | 
 appid     | integer                     | 
 amount    | numeric(10,2)               | 
 pay_time  | timestamp without time zone | 
Indexes:
    "tbl_pay_log_gpfdist_pkey" PRIMARY KEY, btree (id), tablespace "tbs_lottu"
Distributed by: (id)
Tablespace: "tbs_lottu"

[gpadmin@mdw ~]$ head ios_pay.txt 
73,ysios_receipt_3793615cb10dc393bba87c82d3c6544f,27388062,yriu1244_16043_001,2616,98.00,2017-11-06 17:36:43
72,ysios_receipt_32946f3d37e774781babe103352bd230,27424976,yriu1244_16043_001,2616,30.00,2017-11-06 15:18:56
75,ysios_receipt_3e2e432550253450412692392c7675d0,27388294,yriu1244_16043_001,2616,98.00,2017-10-19 07:33:03
74,ysios_receipt_3793615cb10dc393bba87c82d3c6544f,27388062,yriu1244_16043_001,2616,98.00,2017-11-06 20:40:46
77,ysios_receipt_ee6bed338a32f836a999133cd2e6d547,27388294,yriu1244_16043_001,2616,98.00,2017-10-19 22:27:46
76,ysios_receipt_ae53b142924c0604820537d61a9dd73e,27424976,yriu1244_16043_001,2616,648.00,2017-10-19 12:10:17
79,ysios_receipt_30ec130bcdf0e864629d12f8392d4b43,27385229,yriu1244_16043_001,2616,98.00,2017-10-21 07:46:01
78,ysios_receipt_e2b62024f1b0c3a2c3aae1e80f126eb6,27387306,yriu1244_16043_001,2616,25.00,2017-10-20 01:54:24
81,ysios_receipt_3e72a8e32c9fee546ab08d103606e6cb,27424976,yriu1244_16043_001,2616,30.00,2017-10-21 13:55:54
80,ysios_receipt_6ca291884fcfe3d1583b49a3611b4ccc,27424976,yriu1244_16043_001,2616,25.00,2017-10-21 13:55:51
[gpadmin@mdw ~]$ wc -l ios_pay.txt 
20027 ios_pay.txt
[gpadmin@mdw ~]$ du -sh ios_pay.txt 
1.9M    ios_pay.txt

现在文本“ios_pay.txt”有20027行记录;大小约2M。

2.2 gpfdist加载数据

  在看下gpfdist工具,gpfdist工具可以实验并行加载,需要先启动gpfdist进程及监听端口,这个命令在Master和Segment节点的GPHOME/bin目录下,如果配置了GP的环境变量,可以直接使用,如果在没有安装GP的服务器上使用gpfdist工具,只需要将gpfdist命令的文件拷贝到相应的服务器上即可使用。

本实验文件在Master节点上;只需在Master启动。

命令: nohup gpfdist -d /home/gpadmin -p 1234 -l /home/gpadmin/gpfdist.log &

[gpadmin@mdw ~]$ nohup gpfdist -d /home/gpadmin -p 1234 -l /home/gpadmin/gpfdist.log &
[1] 28777

  如上命令,启动gpfdist进程,扫描路径为/home/gpadmin,监听端口为1234,下面创建一张基于gpfdist工具的外部表。

create external table med_pay_log_gpfdist
(id int,order_num varchar(100),accountid varchar(30),qn varchar(20),appid int,amount numeric(10,2),pay_time timestamp)
location(\'gpfdist://mdw:1234/ios_pay.txt\')
format \'TEXT\' (DELIMITER as \',\' null as \'\'ESCAPE as \'OFF\')
encoding \'utf8\' LOG ERRORS INTO med_pay_log_gpfdist_err SEGMENT REJECT LIMIT 100;

  查看gpfdist进程

ps -ef|grep gpfdist|grep -v "grep"

  停掉gpfdist进程

kill xxx
pg_cancel_backend(xxx)

  加载数据

lottu=# insert into tbl_pay_log_gpfdist select * from med_pay_log_gpfdist;
NOTICE:  Found 1 data formatting errors (1 or more input rows). Rejected related input data.
INSERT 0 20026
Time: 780.017 ms

  ANALYZE

lottu=# analyze tbl_pay_log_gpfdist;
ANALYZE

  加载完成之后需要停到gpfdist进程

[gpadmin@mdw ~]$ ps -ef|grep gpfdist|grep -v "grep"
gpadmin  28777 20448  0 00:32 pts/1    00:00:00 gpfdist -d /home/gpadmin -p 1234 -l /home/gpadmin/gpfdist.log
[gpadmin@mdw ~]$ kill 28777

  删除外部表

lottu=# drop external table med_pay_log_gpfdist;
DROP EXTERNAL TABLE

  GP数据库查询数据,先扫描到的数据会直接返回,也就是多次查询的结果可能是不一样的,使用gpfdist工具加载,而gpfdist工具加载数据是并行加载的,最先插入到数据库的数据并不一定是从第一条数据开始的。

  查看数据是否倾斜

lottu=# select gp_segment_id,count(*) from tbl_pay_log_gpfdist group by 1;
 gp_segment_id | count 
---------------+-------
             1 | 10013
             0 | 10013

2.3 卸载数据

  使用可写外部表卸载数据时,如果使用gpfdist工具,就可以实现并行卸载,而且数据不需要经过Master节点,直接由Segment节点写入到外部文件中,效率比较高,卸载大量数据时,使用这种方式会节省大量的时间。

  下面测试通过可写外部表并且使用gpfdist工具来卸载数据到sdw1主机上

  在sdw1主机启动gpfdist进程

nohup gpfdist -d /home/gpadmin -p 1234 -l /home/gpadmin/gpfdist.log &

  创建可写外部表

lottu=# create writable external table med_pay_log_gpfdist_unload (like tbl_pay_log_gpfdist) location (\'gpfdist://192.168.1.202:1234/unload.txt\') Format \'text\';
NOTICE:  Table doesn\'t have \'distributed by\' clause, defaulting to distribution columns from LIKE table
CREATE EXTERNAL TABLE
Time: 46.912 ms

  卸载数据

lottu=# insert into med_pay_log_gpfdist_unload select * from tbl_pay_log_gpfdist;
INSERT 0 20026

  卸载完成之后需要停到gpfdist进程

[gpadmin@sdw1 ~]$ ps -ef|grep gpfdist|grep -v "grep"
gpadmin   4650  1163  0 00:57 pts/0    00:00:00 gpfdist -d /home/gpadmin -p 1234 -l /home/gpadmin/gpfdist.log
[gpadmin@sdw1 ~]$ kill 4650

  删除外部表

lottu=# drop external table  med_pay_log_gpfdist_unload;
DROP EXTERNAL TABLE

3. GreenPlum数据加载工具gpload

  gpload是一种数据装载工具,它扮演着Greenplum外部表并行装载特性的接口的角色。gpload使用定义在一个YAML格式的控制文件中的规范来执行一次装载。

  它会执行下列操作:

  • 调用gpfdist进程
  • 基于定义的源数据创建一个临时的外部表定义
  • 执行INSERT、UPDATE或者MERGE操作将源数据载入数据库中的目标表
  • 删除临时外部表
  • 清除gpfdist进程

3.1 创建实验环境

create table tbl_pay_log_gpload (like tbl_pay_log_gpfdist);

3.2 创建YAML格式控制文件

  使用gpload工具,需要编写gpload工具的控制文件,这个控制文件是使用yuml语言编写,如下是gpload工具的演示

---
VERSION: 1.0.0.1
DATABASE: lottu
USER: lottu
HOST: mdw
PORT: 5432
GPLOAD:
  INPUT:
    - SOURCE:
        LOCAL_HOSTNAME:
          - sdw1  
        PORT: 1234
        FILE:
          - /home/gpadmin/unload.txt
    - COLUMNS:
          - id: int
          - order_num: varchar
          - accountid: varchar
          - qn: varchar
          - appid: int
          - amount: numeric
          - pay_time: timestamp
    - FORMAT: text
    - ERROR_LIMIT: 25
    - error_table: public.tbl_pay_log_gpload_err
  OUTPUT:
    - TABLE: public.tbl_pay_log_gpload
    - MODE: INSERT
  SQL:
    - BEFORE: "truncate table public.tbl_pay_log_gpload"
    - AFTER: "ANALYZE tbl_pay_log_gpload"

  特别提醒:“-”后一定要有空格;“:”后也一定要有空格。

参数说明:

VERSION     自定义版本号(可选项)
DATABASE    需要连接的数据库,如果没有指定,根据$PGDATABASE变量确定
USER        执行操作的用户。如果没指定,根据$PGUSER变量确定
HOST        可选项。指定master节点的主机名(IP)。如果没指定,根据变量$PGHOST确定。
PORT        可选项。指定master的端口,默认是5432或者$GPORT。
GPLOAD      必须项。load部分的开始。一个GPLOAD部分必须包含一个INPUT和一个O

以上是关于GreenPlum数据加载的主要内容,如果未能解决你的问题,请参考以下文章

使用 MapReduce 或 Sqoop 将数据加载到 Greenplum DB

Datastage 作业使用 ODBC Greenplum Wire Protocol 驱动程序使 netezza 无法加载到 greenplum 数据

GreenPlum 常用命令

GreenPlum 集群常用命令

GreenPlum 使用gpload通过gpfdist文件实现数据高速加载

GreenPlum数据的装载与卸载之外部表以及gpfdist工具的使用