如何使用apache-spark快速转换多节点上的大规模数据?

时间:2017-01-29 19:30:51

标签: java apache-spark

我是新来的火花。因此,我正在努力训练自己,以便通过遵循一些教程和小任务来熟悉火花和分布式系统。 在某些任务中,下面是让我在这里提出问题以找到更好的解决方案的任务。

以下是我的示例数据。

EEL    A    CAT    LOC1    EEX    13|42|45|67
EEL    A    CAT    LOC2    EEX    24|32
....

我的目标是转换以上数据。

EEL    A    CAT    LOC1    EEX    13
EEL    A    CAT    LOC1    EEX    42
EEL    A    CAT    LOC1    EEX    45
EEL    A    CAT    LOC1    EEX    67
EEL    A    CAT    LOC2    EEX    24
EEL    A    CAT    LOC2    EEX    32

我的代码如下所示,它适用于10MB以下的数据大小。但是,如果我输入一个大型数据集(超过3GB),则需要大约10~15分钟才能在多节点平台上完成。

请注意,我使用的是一个spark-master节点和四个数据节点,它们分别具有16核和16GB内存。

考虑到我的计算机规格,我认为这个简单的转换任务不应该花费太多时间,我也认为有一种更有效的方法来缩短运行时间。

我知道spark是基于内存系统的,我坚信它可以帮助我轻松处理这样一个迭代和简单的任务,但结果对我来说有点失望。

以下是我的完整代码。我跳过了详细的解释,因为它非常简单。

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaPairRDD;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.FlatMapFunction;
import org.apache.spark.api.java.function.Function;
import org.apache.spark.api.java.function.PairFunction;
import scala.Tuple2;


public class DataTransformation {
    public static JavaRDD<String> DataTransformation_V01(String input,JavaSparkContext sc){
        JavaRDD<String> lines = sc.textFile(input);

        JavaRDD<String> line = lines.flatMap(new FlatMapFunction<String, String>() {
            public Iterator<String> call(String s) throws Exception {
                return Arrays.asList(s.split("\n")).iterator();
            }
        });


        JavaRDD<String> newLine = line.flatMap(new FlatMapFunction<String, String>() {
            public Iterator<String> call(String s) throws Exception {
                List<String> ret = new ArrayList<String>();
                List<String> ls = Arrays.asList(s.split("\t"));

                String values = ls.get(ls.size()-1);
                List<String> value = Arrays.asList(values.split("\\|"));

                for(int i=0;i<value.size();++i){
                    String ns = ls.get(0)+"\t"+ls.get(1)+"\t"+ls.get(2)+"\t"+ls.get(3)+"\t"+ls.get(4)+"\t"+ls.get(5);
                    ns = ns + "\t" + value.get(i);
                    ret.add(ns);
                }
                return ret.iterator();
            }
        });
        return newLine;
    }



    public static void main(String[] args) throws Exception{
        String inputFile = args[0];
        String outputFile = args[1];
        SparkConf conf = new SparkConf().setAppName("Data Transformation")
                .set("spark.serializer","org.apache.spark.serializer.KryoSerializer")

        JavaSparkContext sc = new JavaSparkContext(conf);

        JavaPairRDD<String,String> result = DataTransformation_V01(inputFile,sc);

        System.out.println(result.count());
        result.saveAsTextFile(outputFile);

        sc.stop();
        sc.close();
    }
} 

感谢。

1 个答案:

答案 0 :(得分:1)

尝试使用Dataframes API代替RDD进行此计算。以下代码段(使用scala简洁)将满足您的要求。

scala> import sqlContext.implicits._
import sqlContext.implicits._

scala> val df  = sc.textFile("/user/test/files/spark_test.txt").map(_.split(',')).map(x => (x(0),x(1),x(2),x(3),x(4),x(5))).toDF("c1","c2","c3","c4","c5","c6")
df: org.apache.spark.sql.DataFrame = [c1: string, c2: string, c3: string, c4: string, c5: string, c6: string]

scala> df.explode("c6","c7")((x:String) => x.split('|')).drop("c6").show()
{"level": "INFO ", "timestamp": "2017-01-30 01:13:09,138", "classname": "com.hadoop.compression.lzo.GPLNativeCodeLoader", "body": "Loaded native gpl library"}
{"level": "INFO ", "timestamp": "2017-01-30 01:13:09,141", "classname": "com.hadoop.compression.lzo.LzoCodec", "body": "Successfully loaded & initialized native-lzo library [hadoop-lzo rev 2cedc48fab9e2e10a84b909b4c198053ff379ac7]"}
+---+---+---+----+---+---+
| c1| c2| c3|  c4| c5| c7|
+---+---+---+----+---+---+
|EEL|  A|CAT|LOC1|EEX| 13|
|EEL|  A|CAT|LOC1|EEX| 42|
|EEL|  A|CAT|LOC1|EEX| 45|
|EEL|  A|CAT|LOC1|EEX| 67|
|EEL|  A|CAT|LOC2|EEX| 24|
|EEL|  A|CAT|LOC2|EEX| 32|
+---+---+---+----+---+---+

我在这里假设我们有csv文件,如下所示

EEL,A,CAT,LOC1,EEX,13|42|45|67
EEL,A,CAT,LOC2,EEX,24|32

数据帧比RDD更高性能,因为它可以利用Catalyst优化器并避免JVM对象序列化/反序列化以及相关的GC开销。 有关here的更多详情。

其他明显的性能考虑因素是使用镶木地板文件而不是纯文本文件。由于镶木地板文件是柱状的,并且运动效果更好,因此可以显着减少IO。另外,您可以将镶木地板文件直接读入数据框,并完全跳过昂贵的RDD阶段。