将数据从 Amazon S3 复制到 Redshift 并避免重复行

Posted

技术标签:

【中文标题】将数据从 Amazon S3 复制到 Redshift 并避免重复行【英文标题】:Copy data from Amazon S3 to Redshift and avoid duplicate rows 【发布时间】:2013-03-20 01:04:31 【问题描述】:

我正在将数据从 Amazon S3 复制到 Redshift。在此过程中,我需要避免再次加载相同的文件。我的 Redshift 表没有任何独特的限制。有没有办法使用复制命令来实现这一点?

http://docs.aws.amazon.com/redshift/latest/dg/r_COPY_command_examples.html

我尝试添加唯一约束并将列设置为主键,但没有成功。 Redshift 似乎不支持唯一/主键约束。

【问题讨论】:

【参考方案1】:

正如 user1045047 提到的,Amazon Redshift 不支持唯一约束,因此我一直在寻找使用删除语句从表中删除重复记录的方法。 最后,我找到了一个合理的方法。

Amazon Redshift 支持创建存储自动生成的唯一编号的 IDENTITY 列。 http://docs.aws.amazon.com/redshift/latest/dg/r_CREATE_TABLE_NEW.html

以下sql用于PostgreSQL删除OID为唯一列的重复记录,您可以通过将OID替换为标识列来使用此sql。

DELETE FROM duplicated_table WHERE OID > (
 SELECT MIN(OID) FROM duplicated_table d2
  WHERE column1 = d2.dupl_column1
  AND column2 = d2.column2
);

这是我在 Amazon Redshift 集群上测试的示例。

create table auto_id_table (auto_id int IDENTITY, name varchar, age int);

insert into auto_id_table (name, age) values('John', 18);
insert into auto_id_table (name, age) values('John', 18);
insert into auto_id_table (name, age) values('John', 18);
insert into auto_id_table (name, age) values('John', 18);
insert into auto_id_table (name, age) values('John', 18);
insert into auto_id_table (name, age) values('Bob', 20);
insert into auto_id_table (name, age) values('Bob', 20);  
insert into auto_id_table (name, age) values('Matt', 24); 

select * from auto_id_table order by auto_id; 
 auto_id | name | age 
---------+------+-----
       1 | John |  18
       2 | John |  18
       3 | John |  18
       4 | John |  18
       5 | John |  18
       6 | Bob  |  20
       7 | Bob  |  20
       8 | Matt |  24    
(8 rows) 

delete from auto_id_table where auto_id > (
  select min(auto_id) from auto_id_table d
    where auto_id_table.name = d.name
    and auto_id_table.age = d.age
);

select * from auto_id_table order by auto_id;
 auto_id | name | age 
---------+------+-----
       1 | John |  18
       6 | Bob  |  20
       8 | Matt |  24
(3 rows)

它也适用于这样的 COPY 命令。

auto_id_table.csv

John,18
Bob,20
Matt,24

复制sql

copy auto_id_table (name, age) from '[s3-path]/auto_id_table.csv' CREDENTIALS 'aws_access_key_id=[your-aws-key-id] ;aws_secret_access_key=[your-aws-secret-key]' delimiter ','; 

这种方式的优点是您不需要运行 DDL 语句。但是,它不适用于没有标识列的现有表,因为无法将标识列添加到现有表中。删除现有表中重复记录的唯一方法是像这样迁移所有记录。 (与user1045047的回答相同)

insert into temp_table (select distinct from original_table);
drop table original_table;
alter table temp_table rename to original_table;

【讨论】:

我不清楚你的copy sql 是如何正确处理重复的。对于您正在加载的数据集audo_id_table.csv,它只有 3 个唯一行,不是吗? 重点是可以轻松删除重复记录。即使你运行copy auto_id_table 3 次并得到三个重复记录,这些重复记录也会被上面的delete from auto_id_table.... 查询删除。 使用这种方法,是否需要运行VACUUM 命令?我读了here:If you use multiple concurrent COPY commands to load one table from multiple files, Amazon Redshift is forced to perform a serialized load, which is much slower and requires a VACUUM at the end if the table has a sort column defined 在此之后运行 VACUUM 和 ANALYZE 会更好,但不是必需的,因为 VACUUM 需要一些成本。就我而言,由于重复记录的数量很少,我计划每天运行一次 VACUUM,所以我不使用此查询运行它。【参考方案2】:

嗯..

不直接将数据加载到主表中会怎样。

避免重复的步骤:

    开始交易 批量加载到临时临时表中 从其中行 = 临时表行的主表中删除 从临时表插入主表(合并) 删除临时表 结束交易。

这也是 super 有点快,由 redshift docs 推荐。

【讨论】:

您能否详细说明此答案的“这也超级快”部分?我们有一个有数十亿行的表,我们使用亚马逊文档推荐的这种方法,但除非我们做错了什么,否则它肯定不会超快。这是迄今为止我们 ETL 流程的瓶颈。 我猜超快是相当主观的。我只处理了数百万行,而不是数十亿行……但是没有比这更快的过程来确保没有我听说过的重复记录。 (如果你有什么想听的) 问题是临时表有重复记录。 是的,如果临时表有重复行,您将插入重复行。我们所做的是在考虑到这一点的情况下编写我们的合并 sql...或多或少 group by 复合键并执行任何必要的聚合或 first_value 以避免插入时重复。【参考方案3】:

我的解决方案是在桌子上的“复制”之前运行“删除”命令。在我的用例中,每次我需要将每日快照的记录复制到redshift表中,因此我可以使用以下'delete'命令确保删除重复记录,然后运行'copy'命令。

DELETE from t_data where snapshot_day = 'xxxx-xx-xx';

【讨论】:

我使用过类似的解决方案。【参考方案4】:

目前没有办法从 redshift 中删除重复项。 Redshift 不支持主键/唯一键约束,并且使用行号删除重复项也不是一种选择(删除行号大于 1 的行),因为 redshift 上的删除操作不允许复杂的语句(也是redshift 中不存在行号)。

删除重复项的最佳方法是编写一个 cron/quartz 作业,该作业将选择所有不同的行,将它们放在单独的表中,然后将表重命名为原始表。

Insert into temp_originalTable (Select Distinct from originalTable)

Drop table originalTable

Alter table temp_originalTable rename to originalTable

【讨论】:

我们在向表中添加新记录时无法实现这一点。我们在插入之前先检查了表。如果存在记录,我们将在插入之前删除该行。 你考虑过使用事务吗?【参考方案5】:

我们每周删除重复项,但您也可以在 @Kyle 提到的加载事务期间执行此操作。此外,这确实需要存在一个自动生成的 ID 列作为删除的最终目标:

DELETE FROM <your table> WHERE ID NOT IN ( 
   SELECT ID FROM (
      SELECT *, ROW_NUMBER() OVER 
         ( PARTITION BY <your constraint columns> ORDER BY ID ASC ) DUPLICATES 
      FROM REQUESTS 
   ) WHERE DUPLICATES=1
); COMMIT; 

例如:

CREATE TABLE IF NOT EXISTS public.requests
(
    id BIGINT NOT NULL DEFAULT "identity"(1, 0, '1,1'::text) ENCODE delta
    kaid VARCHAR(50)   NOT NULL 
    ,eid VARCHAR(50)   NOT NULL ENCODE text32k
    ,aid VARCHAR(100)  NOT NULL ENCODE text32k
    ,sid VARCHAR(100)  NOT NULL ENCODE zstd
    ,rid VARCHAR(100)  NOT NULL ENCODE zstd
    ,"ts" TIMESTAMP WITHOUT TIME ZONE NOT NULL  ENCODE delta32k
    ,rtype VARCHAR(50) NOT NULL ENCODE bytedict
    ,stype VARCHAR(25)          ENCODE bytedict
    ,sver VARCHAR(50)  NOT NULL ENCODE text255
    ,dmacd INTEGER              ENCODE delta32k
    ,reqnum INTEGER    NOT NULL ENCODE delta32k
    ,did VARCHAR(255)           ENCODE zstd
    ,"region" VARCHAR(10)       ENCODE lzo
)
DISTSTYLE EVEN
SORTKEY (kaid, eid, aid, "ts")
;

. . . 

DELETE FROM REQUESTS WHERE ID NOT IN ( 
   SELECT ID FROM (
      SELECT *, ROW_NUMBER() OVER 
         ( PARTITION BY DID,RID,RTYPE,TS ORDER BY ID ASC ) DUPLICATES 
      FROM REQUESTS 
   ) WHERE DUPLICATES=1
); COMMIT; 

【讨论】:

【参考方案6】:

还有另一种解决方案可以真正避免数据重复,尽管它不像插入后删除重复数据那么简单。 复制命令有 manifest 选项来指定要复制的文件

copy customer
from 's3://mybucket/cust.manifest' 
iam_role 'arn:aws:iam::0123456789012:role/MyRedshiftRole'
manifest; 

您可以在每次运行复制命令之前构建一个生成新清单文件的 lambda。该 lambda 会将已复制的文件与到达的新文件进行比较,并将创建一个仅包含新文件的新清单,这样您就不会两次摄取同一个文件

【讨论】:

以上是关于将数据从 Amazon S3 复制到 Redshift 并避免重复行的主要内容,如果未能解决你的问题,请参考以下文章

Laravel 5:如何将文件从Amazon S3复制(流)到FTP?

将所有文件从 Amazon S3 存储桶复制到 Microsoft Azure 容器的最佳方法

如何使用适用于 Node.js 的 AWS 开发工具包将 Amazon S3 中的所有对象从一个前缀复制/移动到另一个前缀

Laravel 5:如何将本地文件复制到 Amazon S3?

从多个进程访问 Amazon S3 文件

将数据从 Amazon redshift 卸载到 Amazon s3