将多个文件并行处理为独立的RDD

时间:2015-08-10 06:27:41

标签: scala apache-spark apache-spark-sql

我的情况是必须在一些小的(每个~300MB)文件上应用包括group by的一定数量的操作。操作看起来像这样..

df.groupBy(....).agg(....)

现在要在多个文件上处理它,我可以使用通配符" / ** / * .csv"但是,它会创建一个RDD并将其分区以进行操作。但是,看一下这些操作,如果文件是互斥的,那么它就是一个小组,并且涉及大量的shuffle,这是不必要的。

我正在研究的是,我可以在文件上创建独立的RDD并对其进行独立操作。

3 个答案:

答案 0 :(得分:7)

这是一个想法,而不是一个完整的解决方案,我还没有测试过它。

您可以从将数据处理管道提取到函数中开始。

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] = ???

接下来,您可以使用pipeline函数映射上面的列表:

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
}

最后,我们可以在rdds上使用上面的帮助:

val result = Future.sequence(
   rdds.map(rdd => pipelineToFuture(rdd)).toList
)

根据您的要求,您可以添加onComplete个回调或使用反应流来收集结果。

答案 1 :(得分:0)

如果你有很多文件,并且每个文件很小(你说300 MB以上,我认为它对Spark来说很小),你可以尝试使用SparkContext.wholeTextFiles创建一个RDD,其中每条记录都是一个完整的文件。

答案 2 :(得分:0)

通过这种方式,我们可以并行编写多个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();
    }




}