Spark SQL窗口函数导致数据分布出现偏差

时间:2017-10-28 16:58:12

标签: apache-spark apache-spark-sql

由于数据分布偏差,此Spark SQL查询的性能很差:

select c.*, coalesce(
      sum(revenue)
        OVER (PARTITION BY cid, pid, code
        ORDER BY (cTime div (1000*3600))
        RANGE BETWEEN 336 PRECEDING and 1 PRECEDING), 0L) as totalRevenue
  from records c

我在SparkUI中看到,如果我增加扫描范围,单个任务堆栈和群集就会失败。

我在AWS EMR上使用Yarn,使用Spark 2.2.0

我如何克服这个问题? 谢谢

1 个答案:

答案 0 :(得分:1)

我只能推荐几种方法来缓解你的调查条件。我实际上会尝试两种不首先处理偏斜的方法:

  • 尝试根据消息增加执行程序内存。在YARN上,您可能还需要增加最大容器内存。 Spark IIRC的默认值为2gb,并且需要增加它的情况并不罕见。
  • 尝试切换到memory_and_disk或disk_only持久性级别。我相信这应该适用于您的查询,尽管很难看到完整的查询计划

原因是至少在我看来你的数据基本上是偏斜的。如果您开始重新整形数据以解决当前数据形状的特定方式的偏斜,那么您将面临维护困难,因为数据的形状可能会随着时间的推移而变化。在我看来,至少你希望尽可能保留查询的最直接实现,并且只有在遇到SLA违规问题时才能以编程方式优化偏差问题等。

如果这些不起作用,那么您可以尝试直接解决偏差问题。 一种简单的方法是创建第三列,该列由随机数填充,用于已知有问题的列值。使用它作为关键点,对其进行一次求和操作,然后移除额外随机列的第二次传递。或者,您可以执行两个查询并将它们连接起来:一个用于偏斜数据的随机数(必须仍然在两次传递中处理),另一个用于无问题数据的未更改查询。

编辑 - 计算两帧的部分和

这里基本上有用的观察是加法是可交换的和联想的。我基于随机数的原始提案不会起作用,但这样做。基本上,您想要在几个部分中计算所需帧的部分和。最简单的方法可能是作为一组范围(为简单起见,这里使用了两个):

create temporary table partial_revenue_1 as select c.*, coalesce(
      sum(revenue)
        OVER (PARTITION BY cid, pid, code
        ORDER BY (cTime div (1000*3600))
        RANGE BETWEEN 336 PRECEDING and 118 PRECEDING), 0L) as partialTotalRevenue
  from records c

create temporary table partial_revenue_2 as select c.*, coalesce(
      sum(revenue)
        OVER (PARTITION BY cid, pid, code
        ORDER BY (cTime div (1000*3600))
        RANGE BETWEEN 117 PRECEDING and 1 PRECEDING), 0L) as partialTotalRevenue
  from records c

create temporary table combined_partials as select * from
    partial_reveneue_1 union all select * from partial_revenue_2

select sum(partialTotalRevenue), first(c.some_col) ... from 
    combined_partials c group by cid, pid, code

请注意,您需要使用first聚合函数来剔除您将从select *表上的早期records操作中获得的重复字段。不要担心,这样会很好,因为两个值来自同一张桌子。