Spark编程指南

一。概观


每个spark应用程序都包含一个驱动程序,通过main函数在集群上执行各种并行操作。
1.Spark提供的是主要抽象是RDD,他是跨群集结点分区的元素的集合,可以并行操作(宽依赖)。RDD是通过从Hadoop文件系统(或其他Hadoop支持的文件系统)中的文件或驱动程序中的现有Scala集合开始并对其进行转换而创建的。用户还可以要求Spark在内存中保留RDD,允许它在并行操作中有效地重用。最后,RDD会自动从节点故障中恢复。
2. spark可以在并行操作中使用共享变量,有两种:广播变量:可用于缓存所有节点的内存中的值;累加器,他们是仅用于“added”的用法,例如,计数或者加和


二。Java与spark连接


spark2.1.0与Java7及以上版本,使用Java8可以使用lambda表达式简洁的写函数,或者使用在 org.apache.spark.api.java.function包里的类

添加spark的依赖:
<!-- https://mvnrepository.com/artifact/org.apache.spark/spark-core -->
<dependency>
    <groupId>org.apache.spark</groupId>
    <artifactId>spark-core_2.11</artifactId>
    <version>2.1.0</version>
</dependency>
如果想要进入Hadoop集群,添加依赖:
<!-- https://mvnrepository.com/artifact/org.apache.hadoop/hadoop-client -->
<dependency>
    <groupId>org.apache.hadoop</groupId>
    <artifactId>hadoop-client</artifactId>
    <version>2.6.0</version>
</dependency>
最后,需要在程序里import一些spark的类:
import org.apache.spark.api.java.JavaSparkContext
import org.apache.spark.api.java.JavaRDD
import org.apache.spark.SparkConf

三。初始化spark


第一件事必须是要建立一个JavaSparkContext对象来告诉spark如何进入一个集群,建立一个Sparkcontext首先要创建一个SparkConf类来包含关于你的软件的信息。
SparkConf conf=new SparkConf().setAppName("Appname").setMaster(master);
JAvaSparkContext sc=new JAvaSparkContext(conf);
其中APPName是为了显示在spark webUI中提交任务的名字,,
master 是一个Spark,Mesos or Yarn Cluster URL or "local"使用Local模式,实际上,当运行在集群上是,可以不指定master,可以在用spark-submit运行程序的时候指定集群地址。然而,当进行本地测试和单元测试的时候,采用“local”模式。


 四。利用Shell


 执行Spark的任务:两个工具:
 1。spark-submit:用于提交Spark的任务(就是一个jar包)
 命令:
        bin/spark-submit --master spark://bigdata111:7077  提交在哪个节点上运行 
        --class org.apache.spark.examples.SparkPi 类 examples/jars/spark-examples_2.11-2.1.0.jar 100 类的包 投多少次
                                                                         2、spark-shell:相当于REPL,命令行工具;作为一个独立的Application运行 
直接执行:bin/spark-shell进入spark-shell的操作界面
日志:Spark context available as 'sc' (master = local[*], app id = local-1540472971277)
 *执行:处理本地文件 直接打印结果
sc.textFile("/root/tools/input/word.txt").flatMap(_.split(" ")).map((_,1)).reduceByKey(_+_).collect
*处理HDFS的文件:输出到HDFS
                sc.textFile("hdfs://bigdata111:9000/input/data.txt").flatMap(_.split(" ")).map((_,1))
                  .reduceByKey(_+_).saveAsTextFile("hdfs://bigdata111:9000/output/1025")                                                
(*) 单步运行WordCount  ---->  知识点:RDD
                val rdd1 = sc.textFile("/root/tools/input/word.txt")
                                                                rdd1.collect
                val rdd2 = rdd1.flatMap(_.split(" "))
                val rdd3 = rdd2.map((_,1))   完整: val rdd3 = rdd2.map((word:String)=>(word,1) )
                val rdd4 = rdd3.reduceByKey(_+_) 完整 
                                                      val rdd4 = rdd3.reduceByKey((a:Int,b:Int)=> a+b)
                rdd4.collect                  
***在spark-shell中已经存在sc变量即SparkContext,可以使用--master参数指定哪一个context,也可以加载jar包 并指定它的路径
 1.For example, to run bin/spark-shell on exactly four cores, use:
./bin/spark-shell --master local[4] 
 2.to also add code.jar to its classpath, use:
 ./bin/spark-shell --master local[4] --jars code.jar
3.to include a dependency using maven coordinates:maven工程依赖
 ./bin/spark-shell --master local[4] --packages "org.example:example:0.1"


五:RDD算子(Resilient Distributed Datasets)


rdd是元素的容错集合能够进行并行计算,两种创建方法,两种方法都可以设置分区:
  1.parallelizing 在运行程序中的已存的collection
  List<Integer> data = Arrays.asList(1, 2, 3, 4, 5);
  JavaRDD<Integer> distData = sc.parallelize(data);
  2.引用其他存储系统中的文件
  JavaRDD<String> distFile = sc.textFile("data.txt");
  
  分区:List<Integer> data = Arrays.asList(1, 2, 3, 4, 5);
  JavaRDD<Integer> distData = sc.parallelize(data,3)设置3个分区
  
  
  其他读取文件形式:
  1.JavaSparkContext.wholeTextFiles 可以直接读取整个文件夹
  2.SequenceFiles  利用SparkContext的sequenceFile[K,V],方法,他们应该继承Hadoop的writable接口
  3.JavaSparkContext.hadoopRDD  用于Hadoop文件形式
  4.JavaRDD.saveAsObjectFile 和 JavaSparkContext.objectFile

六。RDD操作


两种操作形式:transformation :lazy
action:
操作:
JavaRDD<String> lines=sc.textFile("data.txt");
JavaRDD<Integer> lineLengths=lines.map(s->s.length());//lazy操作
//如果我们想在后面再用lineLength,需要添加:
//lineLengths.persist(storageLevel。mEMORY_ONLY());
//在reduce之前,在他第一次被计算之后可以被保存在内存中
int totalLength=lineLengths.reduce((a,b)->a+b);//action 操作

七。给spark传递函数

使用lambda表达式简洁话定义一个函数 

JavaRDD<String> lines=sc.textFile("data.txt");
JavaRDD<Integer> lineLengths=lines.map(new Function<String,Integer>(){
    public Integer call(String s){
        return s.length();
    }
});
int totalLength=lineLengths.reduce(new Function2<Integer,Integer,Integer>(){
    public Integer call(Integer a,Integer b){
        return a+b;
    }
});
如果不使用匿名函数:要把每个函数都分别写出来
class GetLength implements Function<String, Integer> {
  public Integer call(String s) { return s.length(); }
}
class Sum implements Function2<Integer, Integer, Integer> {
  public Integer call(Integer a, Integer b) { return a + b; }
}

JavaRDD<String> lines = sc.textFile("data.txt");
JavaRDD<Integer> lineLengths = lines.map(new GetLength());
int totalLength = lineLengths.reduce(new Sum());


八。本地与群集模式


在进行累加操作时,不要使用foreach(),应该使用Accumulator
打印RDD的元素:
使用collect(),rdd.collect.foreach(println)
但是这会导致驱动程序内存不足,因为collect()将整个RDD提取到一台机器上;如果只需要打印RDD的一些元素,,更安全的方法是使用take();rdd.take(100).foreach(println);

九,使用键值对


虽然大多数Spark操作都适用于包含任何类型对象的RDD,但一些特殊操作仅适用于键值对的RDD,最常见的就是key-value元素进行分组或者聚合;
在Java中,键值对使用Scala标准库中的scala.Tuple2类表示。
 //每一个单词记一次数(k2,v2),Beijing---->(Beijing,1)
        JavaPairRDD<String,Integer> rdd3=rdd2.mapToPair(new PairFunction<String, String, Integer>() {
            @Override
            public Tuple2<String, Integer> call(String word) throws Exception {
                return new Tuple2<String,Integer>(word,1);
            }
        });
        JavaPairRDD<String,Integer> rdd4=rdd3.reduceByKey(new Function2<Integer, Integer, Integer>() {
            @Override
            public Integer call(Integer a, Integer b) throws Exception {
                //累加
                return a+b;
            }
        });
        
        
        
键值对的RDD由JavaPairRDD类表示,也可以使用mapToPair 或flatMapToPair
例如:
JavaRDD<String> lines=sc.textFile(""data.txt");
JavaPairRDD<String,Integer> pairs=lines.mapToPair(s->new Tuple2(s,1));
JavaPairRDD<String,Integer> counts=pairs.reduceByKey((a,b)->a+b);

十。transformation


map(func)   通过func函数对没哟个元素进行处理并返回一个新的分布式的数据集

filter(func)    通过func函数,将返回为true的数据返回
flatMap(func)   和map类似,只不过每一个元素会被分成1个或多个元素
mapPartition(func)  和map类似,但是会单独的运行RDD的每一个分区,所以func必须是一个iterator<T>
mapPartitionWithIndex(func) 指定分区进行map
sample(withReplacement, fraction, seed) 用给定的随机数生成器种子,在有或没有替换的情况下对数据的一小部分进行采样。
union(otherDataset) 返回一个新数据集,其中包含源数据集和参数中元素的并集。
intersection(otherDataset) 返回包含源数据集和参数中元素交集的新RDD。
distinct([numTasks])) 返回包含源数据集的不同元素的新数据集。
groupByKey([numTasks]) 在(K,V)对的数据集上调用时,返回(K,Iterable <V>)对的数据集。
注意:如果要进行分组以便对每个密钥执行聚合(例如总和或平均值),则使用reduceByKey或aggregateByKey将产生更好的性能。 
注意:默认情况下,输出中的并行级别取决于父RDD的分区数。您可以传递可选numTasks参数来设置不同数量的任务。
reduceByKey(func, [numTasks]) 调用(K,V)对的数据集时,返回(K,V)对的数据集,其中使用给定的reduce函数func聚合每个键的值,该函数必须是类型(V,V)=> V.同样groupByKey,reduce任务的数量可通过可选的第二个参数进行配置。
aggregateByKey(zeroValue)(seqOp, combOp, [numTasks])当调用(K,V)对的数据集时,返回(K,U)对的数据集,其中使用给定的组合函数和中性“零”值聚合每个键的值。允许与输入值类型不同的聚合值类型,同时避免不必要的分配。同样groupByKey,reduce任务的数量可通过可选的第二个参数进行配置。

sortByKey([ascending], [numTasks]) 当在K实现Ordered的(K,V)对的数据集上调用时,返回按键按升序或降序排序的(K,V)对的数据集,如布尔ascending参数中所指定的。
join(otherDataset, [numTasks]) 当调用类型(K,V)和(K,W)的数据集时,返回(K,(V,W))对的数据集以及每个键的所有元素对。外连接通过支持leftOuterJoin,rightOuterJoin和fullOuterJoin。
cogroup(otherDataset, [numTasks]) 当调用类型(K,V)和(K,W)的数据集时,返回(K,(Iterable <V>,Iterable <W>))元组的数据集。此操作也称为groupWith。
cartesian(otherDataset) 当调用类型为T和U的数据集时,返回(T,U)对的数据集(所有元素对)。
pipe(command, [envVars]) 通过shell命令管道RDD的每个分区,例如Perl或bash脚本。RDD元素被写入进程的stdin,并且输出到其stdout的行将作为字符串的RDD返回。
coalesce(numPartitions) 将RDD中的分区数减少为numPartitions。过滤大型数据集后,可以更有效地运行操作。
repartition(numPartitions) 随机重新调整RDD中的数据以创建更多或更少的分区并在它们之间进行平衡。这总是随机播放网络上的所有数据。
repartitionAndSortWithinPartitions(partitioner)     根据给定的分区重新分区RDD,并在每个生成的分区中按键对记录进行排序。这比repartition在每个分区中调用然后排序更有效,因为它可以将排序推送到shuffle机器中。

十一 Action 


reduce(func) 使用函数func(它接受两个参数并返回一个)来聚合数据集的元素。该函数应该是可交换的和关联的,以便可以并行正确计算
collect()****程序中将数据集的所有元素作为返回。在过滤器或其他返回足够小的数据子集的操作之后,这通常很有用。
count() 返回数据集中的元素数。
first() 返回数据集的第一个元素(类似于take(1))。
take(n) 返回包含数据集的前n个元素的数组。
takeSample(withReplacement, num, [seed]) 返回一个数组,其中包含数据集的num个元素的随机样本,有或没有替换,可选地预先指定随机数生成器种子。
takeOrdered(n, [ordering]) 使用自然顺序或自定义比较器返回RDD 的前n个元素。
saveAsTextFile(path) 将数据集的元素作为文本文件(或文本文件集)写入本地文件系统,HDFS或任何其他Hadoop支持的文件系统的给定目录中。Spark将在每个元素上调用toString,将其转换为文件中的一行文本。
saveAsSequenceFile(path)  将数据集的元素作为Hadoop SequenceFile写入本地文件系统,HDFS或任何其他Hadoop支持的文件系统中的给定路径中。这可以在实现Hadoop的Writable接口的键值对的RDD上使用。在Scala中,它也可以在可隐式转换为Writable的类型上使用(Spark包括基本类型的转换,如Int,Double,String等)。
(Java and Scala)

saveAsObjectFile(path)  使用Java序列化以简单格式编写数据集的元素,然后可以使用它进行加载 SparkContext.objectFile()。
(Java and Scala)
countByKey() 仅适用于类型(K,V)的RDD。返回(K,Int)对的散列映射,其中包含每个键的计数。
foreach(func) 在数据集的每个元素上运行函数func。这通常用于副作用,例如更新累加器或与外部存储系统交互。 
注意:修改除累加器之外的变量foreach()可能会导致未定义的行为。有关详细信息,请参阅了解闭包

十二。广播变量


广播变量允许程序员在每台机器上保留一个只读变量,而不是随副本一起发送它的副本。例如,它们可用于以有效的方式为每个节点提供大输入数据集的副本。Spark还尝试使用有效的广播算法来分发广播变量,以降低通信成本。
Spark动作通过一组阶段执行,由分布式“shuffle”操作分隔。Spark自动广播每个阶段中任务所需的公共数据。以这种方式广播的数据以序列化形式缓存并在运行每个任务之前反序列化。这意味着显式创建广播变量仅在跨多个阶段的任务需要相同数据或以反序列化形式缓存数据很重要时才有用。
广播变量是v通过调用从变量创建的SparkContext.broadcast(v)。广播变量是一个包装器v,可以通过调用该value 方法来访问它的值。下面的代码显示了这个:

Java

Broadcast<int[]> broadcastVar = sc.broadcast(new int[] {1, 2, 3});

broadcastVar.value();
// returns [1, 2, 3]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值