在请求之间“缓存” Spark数据集的最佳方法是什么?

时间:2019-10-22 05:17:31

标签: apache-spark

我有一个Spark程序,该程序开始在给定年份的数据集中创建法国的网络(城市,地方当局...)。然后将其用于其他操作:本地会计,企业间搜索等。

就业务规则而言,数据集很难创建:许多过滤器,多种检查,而且我事先不知道要求它的呼叫者将如何使用它。但在大多数情况下,他都要求提供2019年的数据集,因为他只需要“ 今天法国所有存在的城市。”。

以下我的程序成功返回了2019年的结果。 下一呼叫者还呼叫2019年的城市:Spark从他之前所做的全部工作中重新开始...

这里的优化原理是什么?

我应该在程序中存储与用于请求和构建的Spark会话相同的级别,例如Map<Integer, Dataset>,其中键是年份,而数据集至少应是一个来电者要求今年吗?

3 个答案:

答案 0 :(得分:1)

您必须将数据集保存到hdfs或正在使用的任何其他存储中,并在需要时加载它,而不是再次重新计算整个数据集。这是有关如何设计应用程序的更多信息。这些数据集可能应作为数据准备工作的一部分在几年前进行预先计算,并随时准备使用。假设下次运行时,它将作为新作业被触发,例如:每天运行一次

答案 1 :(得分:0)

  

假设在同一程序中运行的spark-shell或spark-compiled程序   会话提取请求:

     
      
  1. 使用IGNITE,或
  2.   
  3. 依靠“跳过的阶段”效果(也将.cache用于DF)。
  4.   
     例如,

针对RDD,但DF具有以下基础:

val d = sc.parallelize(0 until 100000).map(i => (i%10000, i)).cache // or not cached, does not matter for RDD, for DF, DS it does

val c=d.rightOuterJoin(d.reduceByKey(_+_))
val f=d.leftOuterJoin(d.reduceByKey(_+_))

c.count
c.collect // skipped, shuffled 
f.count
f.collect // skipped, shuffled

val g = f.filter(e => e._1%2==0) 
val h = f.filter(e => e._1==657)
val j = f.filter(e => e._1==1657)

g.collect 
h.collect 
j.collect  // these skipped as well

一个示例,但您会看到Spark改组,这意味着某些方面无需再次进行,但这取决于您的用例以及您最初如何读取数据的方式。

请注意,Spark UI跳过了阶段,因此并不总是像人们想象的那样糟糕。在某些情况下,您的“缓存”是通过这种方式实现的。

对于需要不同处理的操作,那么至少底层(中间)源需要.cache或.persist。

enter image description here

  

如果使用了新的火花提交:

     
      
  1. 使用IGNITE,或
  2.   
  3. 重用检查点目录,尽管非常麻烦,请参阅Spark Checkpointing Non-Streaming - Checkpoint files can be used in subsequent job run or driver program,   尽管令人费解,但仅在预先读取的改组RDD上可能有多个操作时才真正适用,否则效果就不那么好了。或者
  4.   
  5. 使用良好的初始查询并进行存储桶保存并重新读取。参见https://databricks-prod-cloudfront.cloud.databricks.com/public/4027ec902e239c93eaaa8714f173bcfc/4861715144695760/2994977456373837/5701837197372837/latest.html。   进行分类时特别方便。
  6.   

答案 2 :(得分:0)

Redis是与spark配合使用的最佳选择。将结果存储到Redis中,对于下一个请求,只需从Redis中获取。