
# 使用GDS从远端服务器上导入表数据到DWS集群
#### 教程指引
本教程旨在演示使用GDS工具将远端服务器上的数据导入DWS中的过程，帮助您学习如何通过GDS进行数据导入的方法。DWS支持通过GDS外表将TXT、CSV和FIXED格式的数据导入到集群进行查询。
**GD** **S（General Data Service）**是一款运行在Linux操作系统的命令行工具，通过和外表机制的配合，实现数据的高速导入导出。GDS工具包需要安装在数据源文件所在的服务器上，数据源文件所在的服务器称为数据服务器，也叫GDS服务器。
本教程仅用于测试环境演示，如果您在实际业务导入场景，应综合考虑GDS服务器与DWS集群之间的网络、GDS服务器配置等，参见[了解更多：GDS导入数据调优实践]。
在本教程中，您将：
- 生成本教程需要使用的CSV格式的数据源文件。
- 将数据源文件上传到数据服务器。
- 创建外表，用于对接GDS和DWS，将数据服务器上的数据导入到DWS集群中。
- 启动DWS并创建数据库表后，将数据导入到表中。
- 根据错误表中的提示诊断加载错误并更正这些错误。
 
#### 视频介绍
<video controls="controls" preload="none" id="object538917411802" class="idp-external-video" src="https://res-video.hc-cdn.com/cloudbu-site/china/zh-cn/support/dws-video/1625216203289095175.mp4" title="GDS导入数据" poster="https://support.huaweicloud.com/bestpractice-dws/figure/zh-cn_image_0000002402510193.png" height="304.0000" width="600.0000"></video>
#### 准备ECS作为GDS服务器
购买Linux弹性云服务器的操作步骤，请参见《弹性云服务器快速入门》中的[自定义购买ECS](https://support.huaweicloud.com/usermanual-ecs/ecs_03_7002.html)。购买后，请参见[登录Linux弹性云服务器](https://support.huaweicloud.com/usermanual-ecs/ecs_03_0134.html)进行登录。
![](https://support.huaweicloud.com/bestpractice-dws/public_sys-resources/caution_3.0-zh-cn.png)
- ECS按以下最低规格配置。
  - 生产环境下：网络：\>=10GE网络；CPU：16U；内存：64GB；存储：按需，建议不低于500GB。
  
  - 测试环境下：网络：\>=10GE网络；CPU：8U；内存：32GB；存储：按需，建议不低于500GB。
   
- ECS操作系统必须是GDS工具包所支持的操作系统。
- ECS与DWS处于同一区域、同一虚拟私有云和子网。
- ECS安全组规则需放通DWS集群的访问，即安全组入规则：
  - 协议：TCP
  
  - 端口范围：5000
  
  - 源地址：选择"IP地址"，输入DWS 集群地址，例如"192.168.0.10/32"。
   
- ECS内部如果启用了防火墙，需要保证防火墙打开了GDS服务的监听端口：
  ```
  iptables  -I INPUT -p tcp -m tcp --dport <gds_port> -j ACCEPT
  ```
  
 
#### 下载GDS工具包
1. 登录[DWS控制台](https://console.huaweicloud.com/dws)。
2. 在左侧导航栏中，单击"管理 \> 连接客户端"。
3. 在"命令行客户端"的下拉列表中，选择对应版本的GDS客户端。 
   请根据集群版本和安装客户端的操作系统，选择对应版本。
   ![](https://support.huaweicloud.com/bestpractice-dws/public_sys-resources/note_3.0-zh-cn.png)
   客户端CPU架构要和集群一致，如果集群是X86规格，则应该选择X86客户端。
   
   
4. 单击"下载"。
#### 准备数据源文件
- 数据文件"product_info0.csv"
  ```
  100,XHDK-A,2017-09-01,A,2017 Shirt Women,red,M,328,2017-09-04,715,good!
  205,KDKE-B,2017-09-01,A,2017 T-shirt Women,pink,L,584,2017-09-05,40,very good!
  300,JODL-X,2017-09-01,A,2017 T-shirt men,red,XL,15,2017-09-03,502,Bad.
  310,QQPX-R,2017-09-02,B,2017 jacket women,red,L,411,2017-09-05,436,It's nice.
  150,ABEF-C,2017-09-03,B,2017 Jeans Women,blue,M,123,2017-09-06,120,good.
  ```
  
- 数据文件"product_info1.csv"
  ```
  200,BCQP-E,2017-09-04,B,2017 casual pants men,black,L,997,2017-09-10,301,good quality.
  250,EABE-D,2017-09-10,A,2017 dress women,black,S,841,2017-09-15,299,This dress fits well.
  108,CDXK-F,2017-09-11,A,2017 dress women,red,M,85,2017-09-14,22,It's really amazing to buy.
  450,MMCE-H,2017-09-11,A,2017 jacket women,white,M,114,2017-09-14,22,very good.
  260,OCDA-G,2017-09-12,B,2017 woolen coat women,red,L,2004,2017-09-15,826,Very comfortable.
  ```
  

- 数据文件"product_info2.csv"
  ```
  980,"ZKDS-J",2017-09-13,"B","2017 Women's Cotton Clothing","red","M",112,,,
  98,"FKQB-I",2017-09-15,"B","2017 new shoes men","red","M",4345,2017-09-18,5473
  50,"DMQY-K",2017-09-21,"A","2017 pants men","red","37",28,2017-09-25,58,"good","good","good"
  80,"GKLW-l",2017-09-22,"A","2017 Jeans Men","red","39",58,2017-09-25,72,"Very comfortable."
  30,"HWEC-L",2017-09-23,"A","2017 shoes women","red","M",403,2017-09-26,607,"good!"
  40,"IQPD-M",2017-09-24,"B","2017 new pants Women","red","M",35,2017-09-27,52,"very good."
  50,"LPEC-N",2017-09-25,"B","2017 dress Women","red","M",29,2017-09-28,47,"not good at all."
  60,"NQAB-O",2017-09-26,"B","2017 jacket women","red","S",69,2017-09-29,70,"It's beautiful."
  70,"HWNB-P",2017-09-27,"B","2017 jacket women","red","L",30,2017-09-30,55,"I like it so much"
  80,"JKHU-Q",2017-09-29,"C","2017 T-shirt","red","M",90,2017-10-02,82,"very good."
  ```
  
1. 新建文本文档并使用本地编辑工具（例如Visual Studio Code）打开后，将示例数据拷贝进文本文档中。
2. 选择"格式 \> 以UTF-8无BOM格式编码"。
3. 选择"文件 \> 另存为"。
4. 在弹出的对话框中输入文件名后，将文件后缀设为.csv，单击"保存"。
5. 使用SSH工具以root用户登录GDS服务器。
6. 创建数据文件存放目录"/input_data"。 
   ```
   mkdir -p /input_data
   ```
   
   
7. 使用MobaXterm将数据源文件上传至上一步所创建的目录中。
 
#### 安装并启动GDS
1. 以root用户登录GDS服务器，创建存放GDS工具包的目录/opt/bin/dws*。*
   
   ```
   mkdir -p /opt/bin/dws
   ```
   
   
2. 将GDS工具包上传至上一步所创建的目录中。 
   以上传redhat版本的工具包为例 ，将GDS工具包"dws_client_8.1.x_redhat_x64.zip"上传至上一步所创建的目录中。
   
   
3. 在工具包所在目录下，解压工具包。 
   ```
   cd /opt/bin/dws
   unzip dws_client_8.1.x_redhat_x64.zip
   ```
   
   
4. 创建用户gds_user及其所属的用户组gdsgrp。此用户用于启动GDS，且需要拥有读取数据源文件目录的权限。 
   ```
   groupadd gdsgrp
   useradd -g gdsgrp gds_user
   ```
   
   
5. 修改工具包以及数据源文件目录属主为创建的用户gds_user及其所属的用户组gdsgrp。 
   ```
   chown -R gds_user:gdsgrp /opt/bin/dws/gds
   chown -R gds_user:gdsgrp /input_data
   ```
   
   
6. 切换到gds_user用户。 
   ```
   su - gds_user
   ```
   若当前集群版本为8.0.x及之前低版本，请跳过[7]，直接执行[8]。
   若当前集群版本为8.1.x及以上版本，则正常执行以下步骤。
   
   
7. 执行环境依赖脚本。（仅8.1.x版本适用）
   
   ```
   cd /opt/bin/dws/gds/bin
   source gds_env
   ```
   
   
8. 启动GDS。
   
   ```
   /opt/bin/dws/gds/bin/gds -d /input_data/ -p 192.168.0.90:5000 -H 10.10.0.1/24 -l /opt/bin/dws/gds/gds_log.txt -D
   ```
   命令中的部分信息请根据实际填写。
   - **-d** *dir*：保存有待导入数据的数据文件所在目录。本教程中为"/input_data/"。
   
   - **-p** *ip:port*：GDS监听IP和监听端口。默认值为：127.0.0.1，需要替换为能跟DWS通信的万兆网IP。监听端口的取值范围：1024\~65535。默认值为：8098。本教程配置为：192.168.0.90:5000。
   
   - **-H** *address_string*：允许哪些主机连接和使用GDS服务。参数需为CIDR格式。此参数配置的目的是允许DWS集群可以访问GDS服务进行数据导入。所以请保证所配置的网段包含DWS集群各主机。
   
   - **-l** *log_file*：存放GDS的日志文件路径及文件名。本教程为"/opt/bin/dws/gds/gds_log.txt"。
   
   - **-D**：后台运行GDS。仅支持Linux操作系统下使用。
   
   
   
   
#### 创建外表
1. 使用SQL客户端工具连接DWS数据库。
2. 创建如下外表： 
   ![](https://support.huaweicloud.com/bestpractice-dws/public_sys-resources/caution_3.0-zh-cn.png)
   LOCATION：请替换成实际的GDS地址和端口。
   ```
   DROP FOREIGN TABLE IF EXISTS product_info_ext;
   CREATE FOREIGN TABLE product_info_ext
   (
       product_price                integer        not null,
       product_id                   char(30)       not null,
       product_time                 date           ,
       product_level                char(10)       ,
       product_name                 varchar(200)   ,
       product_type1                varchar(20)    ,
       product_type2                char(10)       ,
       product_monthly_sales_cnt    integer        ,
       product_comment_time         date           ,
       product_comment_num          integer        ,
       product_comment_content      varchar(200)              
   ) 
   SERVER gsmpp_server 
   OPTIONS(
   LOCATION 'gsfs://192.168.0.90:5000/*',
   FORMAT 'CSV' ,
   DELIMITER ',',
   ENCODING 'utf8',
   HEADER 'false',
   FILL_MISSING_FIELDS 'true',
   IGNORE_EXTRA_DATA 'true'
   )
   READ ONLY
   LOG INTO product_info_err 
   PER NODE REJECT LIMIT 'unlimited';
   ```
   返回如下信息表示创建成功：
   ```
   CREATE FOREIGN TABLE
   ```
   
   
 
#### 导入数据
1. 使用如下语句在DWS中创建目标表product_info，用于存储导入的数据。 
   ```
   DROP TABLE IF EXISTS product_info;
   CREATE TABLE product_info
   (
       product_price                integer        not null,
       product_id                   char(30)       not null,
       product_time                 date           ,
       product_level                char(10)       ,
       product_name                 varchar(200)   ,
       product_type1                varchar(20)    ,
       product_type2                char(10)       ,
       product_monthly_sales_cnt    integer        ,
       product_comment_time         date           ,
       product_comment_num          integer        ,
       product_comment_content      varchar(200)                   
   ) 
   WITH (
   orientation = column,
   compression=middle
   ) 
   DISTRIBUTE BY hash (product_id);
   ```
   
   
2. 将数据源文件中的数据通过外表"product_info_ext"导入到表"product_info"中。 
   ```
   INSERT INTO product_info SELECT * FROM product_info_ext ;
   ```
   返回如下信息，表示数据导入成功。
   ```
   INSERT 0 20
   ```
   
   
3. 执行SELECT命令查询目标表product_info，查看导入到DWS中的数据。 
   ```
   SELECT count(*) FROM product_info;
   ```
   查询结果如下，表示导入成功。
   ```
   count 
   -------
        20
   (1 row)
   ```
   
   

4. 对表product_info执行VACUUM FULL。 
   ```
   VACUUM FULL product_info;
   ```
   
   
5. 更新表product_info的统计信息。 
   ```
   ANALYZE product_info;
   ```
   
   
 
#### 停止GDS
1. 以gds_user用户登录安装GDS的数据服务器。
2. 使用以下方式停止GDS。 
   1. 执行如下命令，查询GDS进程号。其中，GDS进程号为128954。
      ```
      ps -ef|grep gds
      gds_user 128954      1  0 15:03 ?        00:00:00 gds -d /input_data/ -p 192.168.0.90:5000 -l /opt/bin/gds/gds_log.txt  -D
      gds_user 129003 118723  0 15:04 pts/0    00:00:00 grep gds
      ```
      
   
   2. 使用"kill"命令，停止GDS。其中，128954为上一步骤中查询出的GDS进程号。
      ```
      kill -9 128954
      ```
      
   
   
   
   
 
#### 清除资源
1. 执行以下命令，删除目标表product_info。 
   ```
   DROP TABLE product_info;
   ```
   当结果显示为如下信息，表示删除成功。
   ```
   DROP TABLE
   ```
   
   
2. 执行以下命令，删除外表product_info_ext。 
   ```
   DROP FOREIGN TABLE product_info_ext;
   ```
   当结果显示为如下信息，表示删除成功。
   ```
   DROP FOREIGN TABLE
   ```
   
   
 #### 了解更多：GDS导入数据调优实践
- 安装GDS前必须确认GDS所在服务器环境的系统参数是否和数据库集群的系统参数一致。
- GDS如果部署在弹性云服务器ECS上，ECS按以下最低规格配置。
  - 生产环境下：网络：\>=10GE网络；CPU：16U；内存：64GB；存储：按需，建议不低于500GB。
  
  - 测试环境下：网络：\>=10GE网络；CPU：8U；内存：32GB；存储：按需，建议不低于500GB。
   
- GDS与DWS通信要求物理网络畅通，尽量使用万兆网（10GE网络）。因为千兆网无法承载高速的数据传输压力，极易出现断连，使用千兆网时DWS无法提供通信保障。满足万兆网的同时，要求数据磁盘组I/O性能大于GDS单核处理能力上限（约400MB/s），才能保证单文件导入速率最大化。
- 提前做好服务部署规划，数据服务器上，建议一个Raid只布1\~2个GDS。GDS跟DN的数据比例建议在1:3至1:6之间。一台加载机的GDS进程不宜部署太多，千兆网卡部署1个GDS进程即可，万兆网卡机器建议部署不大于4个进程。
- 提前对GDS导入导出的数据目录做好层次划分，避免一个数据目录包含过多的文件，并及时清理过期文件。
- 合理规划目标数据库的字符集，强烈建议使用UTF8作为数据库的字符集，不建议使用sql_ascii编码，因为极易引起混合编码问题。GDS导出时保证外表的字符集和客户端字符集一致即可，导入时保证客户端编码，数据文件内容编码和客户端一致。
- 如果存在无法变更数据库，客户端，外表字符集时，可以尝试使用**iconv** 命令进行手动转换。
  ```
  #注意 -f 表示源文件的字符集，-t为目标字符集
  iconv -f utf8 -t gbk utf8.txt -o gbk.txt
  ```
  
- 关于GDS导入实践可参考[使用GDS导入数据](https://support.huaweicloud.com/bestpractice-dws/dws_05_0001.html#ZH-CN_TOPIC_0000001145910235__zh-cn_topic_0121978185_section19512101411318)。
  
- GDS支持CSV、TEXT、FIXED三种格式，默认为TEXT格式。不支持二进制格式，但是可以使用encode/decode函数处理二进制类型。例如：
  对二进制表导出：
  ```
  --创建表。
  CREATE TABLE blob_type_t1 
  (
      BT_COL BYTEA
  ) DISTRIBUTE BY REPLICATION;
  -- 创建外表
  CREATE FOREIGN TABLE f_blob_type_t1( BT_COL  text ) SERVER gsmpp_server OPTIONS (LOCATION 'gsfs://127.0.0.1:7789/', FORMAT 'text', DELIMITER E'\x08',  NULL '', EOL '0x0a' ) WRITE ONLY;
  INSERT INTO blob_type_t1 VALUES(E'\\xDEADBEEF');
  INSERT INTO blob_type_t1 VALUES(E'\\xDEADBEEF');
  INSERT INTO blob_type_t1 VALUES(E'\\xDEADBEEF');
  INSERT INTO blob_type_t1 VALUES(E'\\xDEADBEEF');
  INSERT INTO f_blob_type_t1 select encode(BT_COL,'base64') from blob_type_t1;
  ```
  对二进制表导入：
  ```
  --创建表。
  CREATE TABLE blob_type_t2 
  (
      BT_COL BYTEA
  ) DISTRIBUTE BY REPLICATION;
  -- 创建外表
  CREATE FOREIGN TABLE f_blob_type_t2( BT_COL  text ) SERVER gsmpp_server OPTIONS (LOCATION 'gsfs://127.0.0.1:7789/f_blob_type_t1.dat.0', FORMAT 'text', DELIMITER E'\x08',  NULL '', EOL '0x0a' );
  insert into  blob_type_t2 select decode(BT_COL,'base64') from f_blob_type_t2;
  SELECT * FROM blob_type_t2;
     bt_col
  ------------
   \xdeadbeef
   \xdeadbeef
   \xdeadbeef
   \xdeadbeef
  (4 rows)
  ```
  
- 对同一张外表重复导出会覆盖之前的文件，因此不要对同一个外表重复导出。
  
- 若不确定文件是否为标准的csv格式，推荐将quote参数设置为0x07，0x08或0x1b等不可见字符来进行GDS导入导出，避免文件格式问题导致任务失败。
  ```
  CREATE FOREIGN TABLE foreign_HR_staffS_ft1
  (
    MANAGER_ID     NUMBER(6),
    section_ID     NUMBER(4)
  ) SERVER gsmpp_server OPTIONS (location 'file:///input_data/*', format 'csv', mode 'private', quote '0x07', delimiter ',') WITH err_HR_staffS_ft1;
  ```
  
- GDS支持并发导入导出，**gds -t** 参数用于设置gds的工作线程池大小，控制并发场景下同时工作的工作线程数且不会加速单个sql任务。gds -t缺省值为8，上限值为200。在使用管道功能进行导入导出时，**-t**参数应不低于业务并发数。
  
- GDS外表参数delimiter是多字符时，建议TEXT格式下字符不要完全相同。例如，不建议使用delimiter '---'。
- GDS多表并行导入同一个文件提升导入性能（仅支持text和csv文件）。
  ```
  -- 创建目标表。
  CREATE TABLE pipegds_widetb_1 (city integer, tel_num varchar(16), card_code varchar(15), phone_code varchar(16), region_code varchar(6), station_id varchar(10), tmsi varchar(20), rec_date integer(6), rec_time integer(6), rec_type numeric(2), switch_id  varchar(15), attach_city varchar(6), opc varchar(20), dpc varchar(20));
  -- 创建带有file_sequence字段的外表。
  CREATE FOREIGN TABLE gds_pip_csv_r_1( like pipegds_widetb_1) SERVER gsmpp_server  OPTIONS (LOCATION 'gsfs://127.0.0.1:8781/wide_tb.txt', FORMAT 'text', DELIMITER E'|+|', NULL '', file_sequence '5-1');  
  CREATE FOREIGN TABLE gds_pip_csv_r_2( like pipegds_widetb_1) SERVER gsmpp_server  OPTIONS (LOCATION 'gsfs://127.0.0.1:8781/wide_tb.txt', FORMAT 'text', DELIMITER E'|+|', NULL '', file_sequence '5-2');
  CREATE FOREIGN TABLE gds_pip_csv_r_3( like pipegds_widetb_1) SERVER gsmpp_server  OPTIONS (LOCATION 'gsfs://127.0.0.1:8781/wide_tb.txt', FORMAT 'text', DELIMITER E'|+|', NULL '', file_sequence '5-3'); 
  CREATE FOREIGN TABLE gds_pip_csv_r_4( like pipegds_widetb_1) SERVER gsmpp_server  OPTIONS (LOCATION 'gsfs://127.0.0.1:8781/wide_tb.txt', FORMAT 'text', DELIMITER E'|+|', NULL '', file_sequence '5-4');  
   
  CREATE FOREIGN TABLE gds_pip_csv_r_5( like pipegds_widetb_1) SERVER gsmpp_server  OPTIONS (LOCATION 'gsfs://127.0.0.1:8781/wide_tb.txt', FORMAT 'text', DELIMITER E'|+|', NULL '', file_sequence '5-5');
  --将wide_tb.txt并发导入到pipegds_widetb_1。
  \parallel on
  INSERT INTO pipegds_widetb_1 SELECT * FROM gds_pip_csv_r_1;
  INSERT INTO pipegds_widetb_1 SELECT * FROM gds_pip_csv_r_2;
  INSERT INTO pipegds_widetb_1 SELECT * FROM gds_pip_csv_r_3;
  INSERT INTO pipegds_widetb_1 SELECT * FROM gds_pip_csv_r_4;
  INSERT INTO pipegds_widetb_1 SELECT * FROM gds_pip_csv_r_5;
  \parallel off
  ```
  file_sequence参数详细内容，可参考[CREATE FOREIGN TABLE (GDS导入导出)](https://support.huaweicloud.com/sqlreference-dws/dws_06_0159.html)章节。
  
