在 Redshift 中执行表交换的最佳实践

Ros*_* W. 5 etl amazon-redshift

我们正在 Redshift 集群上运行少量每小时脚本,这些脚本为数据消费者构建汇总表。组装临时表后,脚本然后运行一个事务,删除现有表并将其替换为临时表,如下所示:

BEGIN;

DROP TABLE IF EXISTS public.data_facts;
ALTER TABLE public.data_facts_stage RENAME TO data_facts;

COMMIT;
Run Code Online (Sandbox Code Playgroud)

此操作的问题在于,长时间运行的分析查询会将 AccessShareLock 放在 上public.data_facts,以防止它被删除并破坏我们的 ETL 周期。我认为更好的解决方案是重命名现有表,如下所示:

ALTER TABLE public.data_facts RENAME TO data_facts_old;
ALTER TABLE public.data_facts_stage RENAME TO data_facts;
DROP TABLE public.data_facts_old;
Run Code Online (Sandbox Code Playgroud)

但是,这种方法的前提是 1)public.data_facts 存在,2)public.data_facts_old 不存在。

您知道是否有一种方法可以在 SQL 中安全地执行此操作,而不依赖于应用程序逻辑?(例如,像 ALTER TABLE IF EXISTS 这样的东西)。

Ale*_*Yes 1

您可以向目标表添加新load time timestamp encode runlength default getdate()列,并使 ETL 执行以下操作:

INSERT INTO public.data_facts
SELECT * FROM public.data_facts_staging;
DELETE FROM public.data_facts
WHERE load_time<(select max(load_time) from public.data_facts);
DROP TABLE public.data_facts_staging;
Run Code Online (Sandbox Code Playgroud)

注意:public.data_facts_staging应该具有与 完全相同的结构,除了ispublic.data_facts的最后一列,以便在插入时它将填充当前时间戳。public.data_factsload_time

唯一的含义是,在插入新行和删除旧行之间需要额外的磁盘空间,并且load_time必须始终是最后一列。vaccum而且每次这样做时你都必须餐桌。

这样做的另一个好处是,如果您的 ETL 失败并且临时表为空或没有临时表,您不会丢失数据。在使用 DDL 交换表的纯 SQL 场景中,当暂存表丢失时,您无法避免删除目标表。在建议的场景中,如果没有插入新行,则删除语句不会删除任何内容(没有行小于最大加载时间),因此最坏的情况是只有旧版本的数据。

ps 有一个命令不仅可以insert ... select ...将指针从暂存表更改为目标表(alter table ... append from ...),而且它需要与我猜测相同类型的锁alter table,所以我不建议这样做