从 S3 加载 Redshift(带分区)

loading Redshift from S3 (with partitions)

我的 S3 文件 (s3://MY_s3/stage/my_s3_file) 在 load_dt 上有一个分区:

S3 结构:

-MY_S3
 --stage
  ---my_s3_file
    ----load_dt=2016-02-01
         ----000024
    ----load_dt=2016-02-02
         ----000036

实际文件在 load_dt 分区下,例如 000024 和 000036。

如果我不在 Redshift table 中保留 load_dt,COPY 命令工作正常,但是当我在 Redshift table 中添加 load_dt 时,COPY 命令失败,因为数据错误,因为输入布局和目标布局不匹配(目标上有额外的 load_dt)。

创建 S3 文件的配置单元 table 在末尾显示分区 load_dt。

如何使此 COPY 命令与 load_dt 在目标 Redshift 上一起工作?

我什至尝试将 S3 位置更改为 s3://MY_s3/stage/my_s3_file/load_dt 但没有成功。

如果您可以将文件名更改为 "load_dt_20160201" 而不是 "load_dt=2016-02-01",

"copy table from 's3://MY_s3/stage/my_s3_file/load_dt' " 应该可以工作。 COPY 命令将 S3 路径的最后一部分作为前缀。

我想我找到了我的案例的答案。

我无法加载 Hive 分区,因为 Hive 将该分区值存储在 Hive 元数据中,这就是分区列不在 S3 文件中的原因。

现在我通过 Hive 向 S3 添加一个新列 Load_Dt_New 这样 S3 文件就会有我的 Redshift COPY 命令工作所需的列。

当 Hive(Apache Hadoop 下的 运行)创建分区 EXTERNAL TABLE 时,它按目录分隔文件。例如,所有 load_dt=2016-02-01 的行都存储在名为 load_dt=2016-02-01.

的目录中

目录中的文件不存储分区列的值 (load_dt)。相反,分区列的值存储为目录名称的一部分。因此,重命名目录实际上会更改目录中所有行的该列中的值。

是的,这有点奇怪,但这就是 Hive 存储数据的方式!

Amazon Redshift 可以从 Amazon S3 导入 CSV 文件(包括压缩的 CSV 文件)。它甚至可以从多个子目录导入文件,因为它只查看要加载的文件的路径前缀。但是,它不理解 Hive 存储分区数据的方式,因此 它不会从目录名 .

加载分区列

部分选项:

  • 在包含相同值的输出数据中添加另一列(就像您现在所做的那样)
  • 运行 COPY 之后的命令,它根据某些计算(例如从日期字段)设置列值
  • 一次加载一个目录(实现起来很复杂而且效率不高)

Redshift 'Copy' 命令将在 table 模式和镶木地板列之间的不匹配列下显示错误。 所以当你使用范围(每日)分区时,你可以使用下面的脚本。

export PGUSER=sample
export PGPASSWORD=sample
export PGHOST=xxxx.xxxx.redshift.amazonaws.com
export PGPORT=5439
export PGDATABASE=xxxx

start_date=`date -d '2018-01-01' +%s`
base_s3='s3://bucket_and_table_root_path/range_column_name='


for day in {0..364}
do
  base_ymd=`date -d @$((${start_date} + 3600 * 24 * ${day})) +'%Y-%m-%d'`
  base_ymd_lower=`echo ${base_ymd} | sed '1,$s/-/_/g'`
  echo ${base_s3}${base_ymd}
  echo 'start-time' `date`
  psql <<EOF
DROP TABLE loading_table_${base_ymd_lower}_temp;
CREATE TABLE loading_table_${base_ymd_lower}_temp (
    col_a VARCHAR(xxxx) ENCODE LZO,
    ...
)
DISTSTYLE even
;
COPY loading_table_${base_ymd_lower}_temp
FROM '${base_s3}${base_ymd}'
iam_role 'arn:aws:iam::xxxxxxxxxxx:role/xxxxxxxxxx'
format as parquet
;
EOF

done

接下来,您可以在 Redshift 中使用带 UNION 关键字的 CTAS。

您可以使用 Redshift 联合查询功能来实现此目的。

  1. 在 Redshift 中创建一个指向 Hive 的外部模式。

    示例:

    create external schema "redshift_external_schema"
    from hive metastore
    database 'hive_database'
    uri 'hive.host.name.com' port 9083
    iam_role 'arn:aws:iam::2188*********:role/redshift-role-name';
    
  2. 使用 CTAS 语句

    create table redshift_schema.table_name as select * from 
    redshift_external_schema.table_name
    

注意:如果您的配置单元 table 具有复杂的数据类型,如数组、映射、结构等,这将不起作用。在这种情况下,您需要 运行 创建 table Redshift 中的 DDL 并使用 COPY 命令。