在 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/雅典娜)