Scala Spark(流式)RDD foreachPartitionAsync功能/工作模式
我将谈到实际问题,但请先容忍我的用例。我有以下用例,假设我从某处获得了Scala Spark(流式)RDD foreachPartitionAsync功能/工作模式,scala,apache-spark,spark-streaming,rdd,Scala,Apache Spark,Spark Streaming,Rdd,我将谈到实际问题,但请先容忍我的用例。我有以下用例,假设我从某处获得了rddStud: val-rddStud:RDD[(字符串,学生)]=??? 其中“String”-一些随机字符串和“Student”-案例类学生(名称:String,id:String,到达时间:Long,classId:String) 我只是以学生为例——实际的业务逻辑有很多不同的复杂类,有很多领域 我想要实现的是-必须按照到达时间的升序处理id相同的学生 为此,我正在做以下工作: //Get RDD from Stude
rddStud
:
val-rddStud:RDD[(字符串,学生)]=???
其中“String”-一些随机字符串和“Student”-案例类学生(名称:String,id:String,到达时间:Long,classId:String)
我只是以学生为例——实际的业务逻辑有很多不同的复杂类,有很多领域
我想要实现的是-必须按照到达时间的升序处理id相同的学生
为此,我正在做以下工作:
//Get RDD from Student.id -> Student
val studMapRdd: RDD[(String,Student)] = rddStud.map(tuple => {
val student = tuple._2
(student.id,student)
})
//Make sure all students with same student.id are in same partition.
//I can potentially use groupByKey/combineByKey.... etc, but I don't see much performance difference
val studPartitionRdd: RDD[(String,Student)] = studMapRdd.partitionBy(new HashPartitioner(studMapRdd.getNumPartitions))
val studSortedRdd: RDD[(String,Student)] = studPartitionRdd.sortBy({ case(studentId,student} =>
student.arrivalTime
}, ascending = true)
studSortedRdd.foreachPartition(itr =>{
itr.foreach{ case (studentId, student) => {
val studentName = student.name
val time = student.arrivalTime
//send for additional processing studentName and time combination
}
})
我的问题是:
如果我使用foreachPartitionAsync,它会并行处理所有分区,但会按顺序处理每个分区中的元素吗?如果不是,那么foreachPartitionAsync和foreachAsync之间有什么区别
重新划分后的排序方法是否合理?或者,您是否可以建议对上述逻辑进行任何优化
非常感谢。同步(foreach(Partition)
)和异步(foreach(Partition)Async
)提交之间的选择以及元素访问和分区访问之间的选择都不会影响执行顺序。在第一种情况下,阻塞执行与非阻塞执行的重要区别在于,在第二种情况下,数据的公开方式与实际执行机制大致相同
重新分区后排序不是有效的方法sortBy
将触发完全洗牌,不会保留现有的数据分发。如果要保留现有数据布局,可以在后续的mapPartitions
阶段中进行排序,或者更好地使用repartitionAndSortWithinPartitions
class StudentIdPartitioner[V](n: Int) extends org.apache.spark.Partitioner {
def numPartitions: Int = n
def getPartition(key: Any): Int = {
val x = key.asInstanceOf[Student].id.hashCode % n
x + (if (x < 0) n else 0)
}
}
val rddStud: RDD[Student] = ???
val partitioner = new StudentIdPartitioner(rddStud.getNumPartitions)
val arrTimeOrdering = scala.math.Ordering.by[Student, Long](_.arrivalTime)
{
implicit val ord = arrTimeOrdering
rddStud.map((_, null)).repartitionAndSortWithinPartitions(partitioner)
}
class studentdpartitioner[V](n:Int)扩展org.apache.spark.Partitioner{
def numPartitions:Int=n
def getPartition(键:Any):Int={
val x=key.asInstanceOf[Student].id.hashCode%n
x+(如果(x<0)n否则0)
}
}
val rddStud:RDD[学生]=???
val partitioner=newstudentdpartitioner(rddStud.getNumPartitions)
val arrtimeording=scala.math.Ordering.by[Student,Long](u.arrivalTime)
{
隐式val-ord=arrtimeording
rddStud.map((,null)).repartitionAndSortWithinPartitions(分区器)
}
为什么要在下一步对分区进行散列排序。这毫无意义foreachPartition
使用与具有分区并行性的foreach
完全相同的机制。假设RDD有3个分区,其中id=1的学生事件分布在3个分区中。现在,hashpartitioning将确保id=1中的所有事件都在同一个分区中,比如p1,但它不会确保它们按到达时间排序——由于某些业务需求,这就是我想要处理它们的方式。我的理解有什么错误吗?所以建议在这里转移更多的讨论,谢谢你的澄清编辑。。。另外,可能值得一提的是,我们正在研究一个foreach操作,它只在一个进程上运行,它用一个定制的“add”更新一个accumulable,我们需要它在所有处理器上运行,这就是foreachAsync所做的吗?有什么理由使用foreachAsyncPartition吗?如果需要单独的问题,请告诉我,谢谢@zero323@JimLohse当然,谢谢你指出这一点。但我不确定我是否理解您的用例。你所说的一个过程是什么意思?单执行器?当此操作运行时(foreach将RDD的每个元素与累加器进行比较),它在htop中只使用一个处理器,我们希望它“fork join”来使用所有处理器。@JimLohse LOL。是的,单独的问题看起来是个好主意。我甚至不确定我是否理解这个问题:)