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

Posted

技术标签:

【中文标题】在 Redshift 中执行表交换的最佳实践【英文标题】:Best practices for performing a table swap in Redshift 【发布时间】:2017-09-06 13:54:05 【问题描述】:

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

BEGIN;

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

COMMIT;

此操作的问题在于,长时间运行的分析查询会在 public.data_facts 上放置一个 AccessShareLock,从而防止它被丢弃并破坏我们的 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;

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

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

【问题讨论】:

AccessShareLock 也不会阻止重命名表吗? 呃,是的,似乎是这样。 你的表是完全重写还是历史部分稳定? 就目前而言,当前过程涉及重写表。 这种方法的另一个缺点是视图在重命名时似乎会粘在表上,然后您就不能删除它。 (查看没有模式绑定的视图,你在这个空间中)。就个人而言,我没有发现比 OP 的方法 + 没有模式绑定更好的方法。还是希望... 【参考方案1】:

我没有尝试过,但是查看the documentation of CREATE VIEW 似乎可以使用后期绑定视图来完成。

主要思想是用户与之交互的视图public.data_facts。在幕后,您可以加载新数据,然后交换视图以“指向”新表。

引导

-- load data into public.data_facts_v0
CREATE VIEW public.data_facts AS
SELECT * from public.data_facts_v0 WITH NO SCHEMA BINDING;

更新

-- load data into public.data_facts_v1
CREATE OR REPLACE VIEW public.data_facts AS
SELECT * from public.data_facts_v1 WITH NO SCHEMA BINDING;
DROP TABLE public.data_facts_v0;

WITH NO SCHEMA BINDING 表示视图将是后期绑定的。 “在查询视图之前,后期绑定视图不会检查底层数据库对象,例如表和其他视图。”这意味着更新甚至可以引入具有重命名列或全新结构的表。

注意事项:

    最好将交换操作包装到 transaction 中,以确保在 VIEW 交换失败时我们不会删除前一个表。

【讨论】:

这看起来是一个聪明的解决方案,我希望有人提供反馈。 我们在实践中发现的两个警告: 1. 一些用户可能会使用依赖表元数据的工具,例如模式。使用后期绑定,这些工具可能无法获得他们想要的信息。他们可以从基础表中获取它,但重点是用户不与该表进行交互。 2. 如果用户创建了依赖于该视图的其他表或视图,您将无法在更新期间删除/重新创建它除非您使用CASCADE,这也会删除所有依赖的对象风景。您的客户可能会也可能不会接受。【参考方案2】:

您可以向目标表添加一个新的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;

注意:public.data_facts_staging 应该与public.data_facts 具有完全相同的结构,除了public.data_facts 的最后一列是load_time,因此在插入时将填充当前时间戳

唯一的含义是在插入新行和删除旧行之间需要额外的磁盘空间,load_time 必须始终是最后一列。此外,每次执行此操作时,您都必须 vaccum 表。

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

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

【讨论】:

我建议不要在 Redshift 中这样做。在行存储中这很有意义,但在列存储(如 Redshfit)中,这种方法可以为数据库产生大量额外的工作。见,例如docs.aws.amazon.com/redshift/latest/dg/….

以上是关于在 Redshift 中执行表交换的最佳实践的主要内容,如果未能解决你的问题,请参考以下文章

在 AWS 上设置数据管道的最佳实践? (Lambda/EMR/Redshift/雅典娜)

AWS Redshift ETL的几个性能最佳实践

通过任何人的 COPY 操作使 Redshift 可以访问 S3 文件的最佳实践

在 Redshift 中存储事件数据的最佳方式是啥?

在tomcat上保存临时文件的最佳实践? [复制]

基于WPS的Word最佳实践系列(利用表格控制排版)