在 Redshift 中使用连接的最佳方式
Posted
技术标签:
【中文标题】在 Redshift 中使用连接的最佳方式【英文标题】:Optimal way of using joins in Redshift 【发布时间】:2017-07-27 13:27:22 【问题描述】:我在 AWS redshift 中有 2 个表。详情如下
a) 展示次数(计算特定广告的展示次数)
-
行数(1.7 亿)
分发密钥(ad_campaign)
排序键(created_on)
b) 点击次数(计算特定广告的点击次数)。
-
行数(8000 万)
分发密钥(ad_campaign)
排序键(created_on)
我有一个带有 2 个切片的 DC1 大型集群。
我正在尝试运行以下查询
select impressions.offer_id, count(imp_cnt) from
bidsflyer.tblImpressionLog_Opt impressions
full join bidsflyer.tblTrackingLinkLog_Opt clicks
on impressions.offer_id=clicks.offer_id and date_trunc('week',
impressions.created_on)=date_trunc('week', clicks.created_on)
where impressions.created_on >= '2017-07-27 00:00:00'
group by 1
此查询需要 8 分钟以上才能运行。考虑到数据量,我认为这是相当大的,我觉得数据量并不大。
查询计划如下所示
XN HashAggregate (cost=2778257688268.43..2778257688268.60 rows=67 width=12)
-> XN Hash Left Join DS_DIST_NONE (cost=179619.84..2778170875920.65 rows=17362469555 width=12)
Hash Cond: (("outer".offer_id = "inner".offer_id) AND (date_trunc('week'::text, "outer".created_on) = date_trunc('week'::text, "inner".created_on)))
-> XN Seq Scan on tblimpressionlog_opt impressions (cost=0.00..724967.36 rows=57997389 width=20)
Filter: (created_on >= '2017-07-27 00:00:00'::timestamp without time zone)
-> XN Hash (cost=119746.56..119746.56 rows=11974656 width=12)
-> XN Seq Scan on tbltrackinglinklog_opt clicks (cost=0.00..119746.56 rows=11974656 width=12)
谁能指导我正确使用分配键和排序键。
我应该如何设计我的查询?
【问题讨论】:
如果速度是重中之重,我强烈建议您使用至少 2 个节点。 【参考方案1】:表设置:
1) 按照计划,最昂贵的操作是按offer_id分组。这是有道理的,因为您没有按 offer_id 对数据进行排序或分发。您的表非常大,因此您可以通过(offer_id,created_on)
使用交错排序键重新创建表(交错键应该为包含的列赋予相等且与顺序无关的权重,并且已知对较大的表有积极影响)。
2) 如果您按周加入,您可以具体化您的周列(创建一个物理列并用date_trunc
输出填充它)。这可能会为您节省一些计算工作,以便在连接期间动态获取这些值。但是,此操作很便宜,如果您的表已经按时间戳列排序,Redshift 可能已经只扫描了适当的块。此外,如果每个报价运行时间很短(意味着报价列具有高基数并且与时间列具有高相关性),您可以通过 (offer_id
,week_created
) 使用复合排序键,这将允许合并连接,即更快,而且聚合也很快。
3) 如果您在其他查询中不使用ad_campaign
,您可以通过offer_id
分配这两个表。在分布键中加入连接列是一种很好的做法,您的查询不太可能从中受益,因为您只有一个节点并且分布方式主要影响多节点设置。
所有建议都只是假设,不知道数据的确切性质,它们需要运行基准测试(使用推荐的配置创建表、复制数据、清理、分析、运行相同的查询至少 3 次,并将时间与原始设置)。如果您这样做并在此处发布结果,我将不胜感激。
RE 查询本身,您可以将FULL JOIN
替换为JOIN
,因为您不需要它。 FULL JOIN
应该用于不仅要获得两个表格的交集,还要获得没有相关点击的展示,反之亦然。情况似乎并非如此,因为您按impressions.created_on
过滤并按impressions.offer_id
分组。所以,你所需要的只是路口。用简单的JOIN
替换FULL JOIN
也可能会影响查询性能。如果您想查看点击次数为零的优惠,可以使用LEFT JOIN
。
【讨论】:
【参考方案2】:Merge join 比 hash join 快,你应该尝试实现 merge join。您的排序键看起来不错,但您的数据实际上是排序的吗? Redshift 不会自动保持表的行按排序键排序,redshift 无法在您的表上执行合并连接。在表上运行完全真空,redshift 将开始执行合并连接。
select * from svv_table_info where table = 'impressions'
select * from svv_table_info where table = 'clicks'
使用上述查询检查表中未排序的数据量。 在你的两张桌子上运行完全真空。根据未排序数据的数量,这可能需要一段时间并占用大量集群资源。
VACUUM impressions to 100 percent
VACUUM clicks to 100 percent
如果我做了一个错误的假设,请发表评论,我会重新调整我的答案。
【讨论】:
感谢拉胡尔的回答。将尝试这些建议以上是关于在 Redshift 中使用连接的最佳方式的主要内容,如果未能解决你的问题,请参考以下文章
将一张表从 RDS / postgres 加载到 Redshift
在没有 AWS Pipeline 的情况下,将数据(csv 文件)从 s3 自动传输到 Redshift 的最佳方式是啥?