从 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 联合查询功能来实现此目的。
在 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';
使用 CTAS 语句
create table redshift_schema.table_name as select * from
redshift_external_schema.table_name
注意:如果您的配置单元 table 具有复杂的数据类型,如数组、映射、结构等,这将不起作用。在这种情况下,您需要 运行 创建 table Redshift 中的 DDL 并使用 COPY 命令。
我的 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
但没有成功。
"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 联合查询功能来实现此目的。
在 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';
使用 CTAS 语句
create table redshift_schema.table_name as select * from redshift_external_schema.table_name
注意:如果您的配置单元 table 具有复杂的数据类型,如数组、映射、结构等,这将不起作用。在这种情况下,您需要 运行 创建 table Redshift 中的 DDL 并使用 COPY 命令。