Scala 将多个文件作为独立的RDD处理';平行的
我有一个场景,其中包括GROUPBY在内的一定数量的操作必须应用于许多小文件(每个文件约300MB)。操作看起来是这样的Scala 将多个文件作为独立的RDD处理';平行的,scala,apache-spark,apache-spark-sql,Scala,Apache Spark,Apache Spark Sql,我有一个场景,其中包括GROUPBY在内的一定数量的操作必须应用于许多小文件(每个文件约300MB)。操作看起来是这样的 df.groupBy(..).agg(..) 现在要在多个文件上处理它,我可以使用通配符“/**/*.csv”,但是,它会创建一个RDD并将其分区以用于操作。但是,从操作上看,这是一个分组操作,涉及大量的洗牌操作,如果文件相互排斥,则不必进行洗牌 我正在研究的是,我可以在文件上创建独立的RDD,并对其进行独立操作。如果您有许多文件,并且每个文件都很小(您可以说300MB以上,
df.groupBy(..).agg(..)
现在要在多个文件上处理它,我可以使用通配符“/**/*.csv”,但是,它会创建一个RDD并将其分区以用于操作。但是,从操作上看,这是一个分组操作,涉及大量的洗牌操作,如果文件相互排斥,则不必进行洗牌
我正在研究的是,我可以在文件上创建独立的RDD,并对其进行独立操作。如果您有许多文件,并且每个文件都很小(您可以说300MB以上,我可以将其视为Spark的小文件),您可以尝试使用
SparkContext.wholeTextFiles
,它将创建一个RDD,其中每个记录都是一个完整的文件 这与其说是一个完整的解决方案,不如说是一个想法,我还没有测试过它
您可以从将数据处理管道提取到函数中开始
def pipeline(f: String, n: Int) = {
sqlContext
.read
.format("com.databricks.spark.csv")
.option("header", "true")
.load(f)
.repartition(n)
.groupBy(...)
.agg(...)
.cache // Cache so we can force computation later
}
如果您的文件很小,您可以调整n
参数以使用尽可能少的分区,以适应单个文件中的数据并避免混乱。这意味着您正在限制并发性,但我们稍后将继续讨论这个问题
val n: Int = ???
接下来,您必须获得输入文件的列表。此步骤取决于数据源,但大多数情况下,它或多或少是简单的:
val files: Array[String] = ???
接下来,您可以使用管道
功能映射上述列表:
val rdds = files.map(f => pipeline(f, n))
由于我们将并发限制在单个文件的级别,因此我们希望通过提交多个作业来进行补偿。让我们添加一个简单的帮助器,它强制求值并用Future
import scala.concurrent._
import ExecutionContext.Implicits.global
def pipelineToFuture(df: org.apache.spark.sql.DataFrame) = future {
df.rdd.foreach(_ => ()) // Force computation
df
}
最后,我们可以在RDD
上使用上述帮助程序:
val result = Future.sequence(
rdds.map(rdd => pipelineToFuture(rdd)).toList
)
根据您的需求,您可以添加
onComplete
回调或使用反应流来收集结果。通过这种方式,我们可以并行编写多个RDD
public class ParallelWriteSevice implements IApplicationEventListener {
private static final IprogramLogger logger = programLoggerFactory.getLogger(ParallelWriteSevice.class);
private static ExecutorService executorService=null;
private static List<Future<Boolean>> futures=new ArrayList<Future<Boolean>>();
public static void submit(Callable callable) {
if(executorService==null)
{
executorService=Executors.newFixedThreadPool(15);//Based on target tables increase this
}
futures.add(executorService.submit(callable));
}
public static boolean isWriteSucess() {
boolean writeFailureOccured = false;
try {
for (Future<Boolean> future : futures) {
try {
Boolean writeStatus = future.get();
if (writeStatus == false) {
writeFailureOccured = true;
}
} catch (Exception e) {
logger.error("Erorr - Scdeduled write failed " + e.getMessage(), e);
writeFailureOccured = true;
}
}
} finally {
resetFutures();
if (executorService != null)
executorService.shutdown();
executorService = null;
}
return !writeFailureOccured;
}
private static void resetFutures() {
logger.error("resetFutures called");
//futures.clear();
}
}
公共类ParallelWriteDevice实现IAApplicationEventListener{
私有静态最终IprogramLogger=programLoggerFactory.getLogger(parallelWriteDevice.class);
私有静态ExecutorService ExecutorService=null;
private static List futures=new ArrayList();
公共静态无效提交(可调用){
if(executorService==null)
{
executorService=Executors.newFixedThreadPool(15);//根据目标表增加此值
}
futures.add(executorService.submit(callable));
}
公共静态布尔值isWriteSucess(){
布尔writeFailureOccursed=false;
试一试{
for(未来:未来){
试一试{
布尔writeStatus=future.get();
if(writeStatus==false){
WriteFailureOccursed=true;
}
}捕获(例外e){
logger.error(“Erorr-推断写入失败”+e.getMessage(),e);
WriteFailureOccursed=true;
}
}
}最后{
重置期货();
if(executorService!=null)
executorService.shutdown();
executorService=null;
}
return!发生writefailured;
}
私有静态void resetFutures(){
logger.error(“resetFutures”);
//期货;
}
}
嗯,就我而言,没有必要采取变通办法,因为这在这里完全无关紧要sqlContext
只在驱动程序上使用,因此没有任何理由进行序列化。@Alexaspo并没有完全做到这一点,但我使用过一两次类似的方法。除非您有非常多的内存,否则执行实际操作而不是依赖缓存更有意义。如果您对一般原则感兴趣,请查看org.apache.spark.rdd.AsyncRDDActions