我的情况是必须在一些小的(每个~300MB)文件上应用包括group by的一定数量的操作。操作看起来像这样..
df.groupBy(....).agg(....)
现在要在多个文件上处理它,我可以使用通配符" / ** / * .csv"但是,它会创建一个RDD并将其分区以进行操作。但是,看一下这些操作,如果文件是互斥的,那么它就是一个小组,并且涉及大量的shuffle,这是不必要的。
我正在研究的是,我可以在文件上创建独立的RDD并对其进行独立操作。
答案 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();
}
}