一、Spark集群运行
-------------------------------------------------------
1.local //本地模式
2.standalone //独立模式
3.yarn //yarn模式
4.mesos //mesos
二、解决maven打包,不包含scala类
-----------------------------------------------------
1.setting --> compiler --> auto compiler 打钩
2.在maven中添加打包scala插件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.it18zhang</groupId>
<artifactId>SparkDemo1</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<sourceDirectory>src/main/java</sourceDirectory>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<version>3.2.2</version>
<configuration>
<recompileMode>incremental</recompileMode>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.11</artifactId>
<version>2.1.0</version>
</dependency>
</dependencies>
</project>
三、RDD 弹性分布式数据集
---------------------------------------------------------
是spark的基本数据结构,是不可变数据集。RDD中的数据集进行逻辑分区,每个分区可以单独在集群节点
进行计算。可以包含任何java,scala,python和自定义类型。
RDD是只读的记录分区集合。RDD具有容错机制。
创建RDD方式,一、并行化一个现有集合。
hadoop 花费90%时间用户rw。、
内存处理计算。在job间进行数据共享。内存的IO速率高于网络和disk的10 ~ 100之间。
内部包含5个主要属性
-----------------------
1.分区列表
2.针对每个split的计算函数。
3.对其他rdd的依赖列表
4.可选,如果是KeyValueRDD的话,可以带分区类。
5.可选,首选块位置列表(hdfs block location);
//默认并发度
local.backend.defaultParallelism() = scheduler.conf.getInt("spark.default.parallelism", totalCores)
taskScheduler.defaultParallelism = backend.defaultParallelism()
sc.defaultParallelism =...; taskScheduler.defaultParallelism
defaultMinPartitions = math.min(defaultParallelism, 2)
sc.textFile(path,defaultMinPartitions) //1,2
四、RDD变换
-------------------------------------------------------------
1.对一个数据为{1,2, 3, 3} 的RDD进行基本的RDD转化操作
a.map()
//函数应用于RDD中的每个元素
rdd.map(x=>x+1) => new RDD: {2,3,4,4}
b.flatMap()
//将压扁函数应用于RDD中的每个元素,通常用来切分单词
rdd.flatMap(x=>x.to(3)) => new RDD: {1,2,3,2,3,3,3}
c.filter()
//返回一个通过传给filter()的函数的元素组成的RDD,如果不等于1就过滤掉
rdd.filter(x=>x!=1) => new RDD: {2,3,3}
d.distinct()
//去除重复元素
rdd.distinct() => new RDD: {1,2,3}
e.sample(withReplacement,fraction,[seed])
//对RDD进行采样,以及是否替换
rdd.sample(false,0.5) => new RDD: { 非确定 }
f.mapPartitions()
//对每个分区进行应用变换,输入的Iterator,返回新的迭代器,可以对分区进行函数处理。
//Iterator<T> => Iterator<U>
g.mapPartitionsWithIndex(func)
//同上,(Int, Iterator<T>) => Iterator<U>
l.代码演示
import org.apache.spark.{SparkConf, SparkContext}
import scala.collection.mutable.ArrayBuffer
object Test1 {
def main(args: Array[String]): Unit = {
val sparkConf: SparkConf = new SparkConf().setAppName("Test-1").setMaster("local")
val sc: SparkContext = new SparkContext(sparkConf)
val data = List(1,2,3,3);
val rdd1 = sc.parallelize(data, 2)
//结果:{(1,1) (2,1) (3,1) (4,1)}
val rdd2 = rdd1.map( e => (e , 1))
rdd2.foreach(e => println(e))
println("-----------------------------")
//结果:{1,2,3,2,3,3,3}
val rdd3 = rdd1.flatMap( x => x.to(3) )
rdd3.foreach(println)
println("-----------------------------")
//结果:new RDD: {2,3,3}
val rdd4 = rdd1.filter( x => x != 1)
rdd4.foreach(println)
println("-----------------------------")
//结果:new RDD: {1,2,3}
val rdd5 = rdd1.distinct();
rdd5.foreach(println)
println("-----------------------------")
//结果:不确定
val rdd6 = rdd1.sample(false,0.5);
rdd6.foreach(println)
println("-----------------------------")
//结果:
// _1 _2 _3 _3
//
val rdd7 = rdd1.mapPartitions(
(it) => {
val buf = ArrayBuffer[String]();
for(e <- it)
{
buf.+=("_" + e)
}
buf.iterator;
}
)
rdd7.foreach(println)
println("-----------------------------")
//结果:
// 开始第0号分区
//_1
//_2
//开始第1号分区
//_3
//_3
val rdd8 = rdd1.mapPartitionsWithIndex(
(i,it) => {
val buf = ArrayBuffer[String]();
println("开始第" + i + "号分区")
for(e <- it)
{
buf.+=("_" + e)
}
buf.iterator;
}
)
rdd8.foreach(println)
println("-----------------------------")
}
}
2.对数据分别为{1,2,3}和{3,4,5}的RDD进行针对两个RDD的转化操作
a.union()
//生成一个包含两个RDD中所有元素的RDD
rdd.union(other) => new RDD:{1,2,3,3,4,5}
b.intersection()
//求两个RDD共同的元素RDD
rdd.intersection(other) => new RDD: {3}
c.subtract()
//移除一个元素的内容
rdd.subtract(other) => new RDD: {1,2}
d.cartesian()
//与另一个RDD的笛卡儿积
rdd.cartesian(other) => new RDD: {(1,3),(1,4)...(3,4),(3,5)}
e.代码演示
import org.apache.spark.{SparkConf, SparkContext}
object Test2 {
def main(args: Array[String]): Unit = {
val sparkConf: SparkConf = new SparkConf().setAppName("Test-1").setMaster("local")
val sc: SparkContext = new SparkContext(sparkConf)
val data1 = List(1,2,3);
val data2 = List(3,4,5);
val rdd1 = sc.parallelize(data1, 2)
val rdd2 = sc.parallelize(data2, 2)
//结果:{1,2,3,3,4,5}
val rdd3 = rdd1.union(rdd2);
rdd3.foreach(println)
println("=========================================")
//结果:{3}
val rdd4 = rdd1.intersection(rdd2);
rdd4.foreach(println)
println("=========================================")
//结果:{1,2}
val rdd5 = rdd1.subtract(rdd2);
rdd5.foreach(println)
println("=========================================")
//结果:{(1,3) : (1,4) : (1,5): (2,3) : (3,3) : (2,4) : (2,5) : (3,4) : (3,5)}
val rdd6 = rdd1.cartesian(rdd2);
rdd6.foreach(e => print(e + " : "))
println("=========================================")
}
}
3.单个pair RDD的转化操作(以键值对集合{(1,2),(3,4),(3,6)}为例)
a.reduceByKey(func)
//合并具有相同键的值,对相同键的对偶应用函数法则
rdd.reduceByKey((x,y)=>x+y) => {(1,2),(3,10)}
b.groupByKey()
//对具有相同键的值进行分组
(K,V) => (K,Iterable<V>)
rdd.groupByKey() => {(1,[2]),(3,[4,6])}
c.combineByKey(createCombiner,mergeCombiners,partitioner)
//使用不同的返回类型合并具有相同键的值
def combineByKey[C](
createCombiner: V => C,
mergeValue: (C, V) => C,
mergeCombiners: (C, C) => C): RDD[(K, C)] = self.withScope {
combineByKeyWithClassTag(createCombiner, mergeValue, mergeCombiners)(null)
}
如下解释下3个重要的函数参数:
createCombiner: V => C ,这个函数把当前的值作为参数,此时我们可以对其做些附加操作(类型转换)并把它返回 (这一步类似于初始化操作)
mergeValue: (C, V) => C,该函数把元素V合并到之前的元素C(createCombiner)上 (这个操作在每个分区内进行)
mergeCombiners: (C, C) => C,该函数把2个元素C合并 (这个操作在不同分区间进行)
//结果:
// (Wilma,(3,286.0))
//(Fred,(3,274.0))
val initialScores = Array(("Fred", 88.0), ("Fred", 95.0), ("Fred", 91.0), ("Wilma", 93.0), ("Wilma", 95.0), ("Wilma", 98.0))
val d1 = sc.parallelize(initialScores)
type MVType = (Int, Double) //定义一个元组类型(科目计数器,分数)
d1.combineByKey(
score => (1, score), //createCombiner,将88变成(1,88)
(c1: MVType, newScore) => (c1._1 + 1, c1._2 + newScore), //mergeValue,针对每个分区:将相同key的分数聚合成新的tuple (2,88+91)
(c1: MVType, c2: MVType) => (c1._1 + c2._1, c1._2 + c2._2) //mergeCombiners,针对所有分区,将不同分区相同key的结果进行聚合
).foreach(println)
println("=========================================")
d.mapValues(func)
//对pairRDD中的每个值value应用一个函数而不改变键
rdd.mapValues(x=>x+1) => {(1,3),(3,5),(3,7)}
e.flatMapValues()
//对pairRDD中的每个值value应用一个返回迭代器的函数,然后返回的每个元素都生成一个对应原键的键值对记录,通常用于符号化和value的范围限定等
//键有1和3,那么,就(1,2),(1,3),(1,4),(1,5) | (3,4),(3,5),因为(3,6),6不属于3 to 5范围,弃掉
rdd.flatMapValues(x=>(x to 5)) => {(1,2),(1,3),(1,4),(1,5),(3,4),(3,5)}
f.keys
//返回一个仅包含键的RDD
rdd.keys => {1,3,3}
g.values()
//返回一个仅包含值的RDD
rdd.values() => {2,4,6}
h.sortByKey()
//返回一个根据键排序的RDD
rdd.sortByKey() => {(1,2),(3,4),(3,6)}
i.aggregateByKey(zeroValue)(seqOp, combOp, [numTasks])
//def aggregateByKey[U: ClassTag](zeroValue: U)(seqOp: (U, V) => U,combOp: (U, U) => U): RDD[(K, U)]
//按照key进行聚合,把相同的Key按照seqOp函数进行捏合
//对PairRDD中相同的Key值进行聚合操作,在聚合过程中同样使用了一个中立的初始值。和aggregate函数类似,
//aggregateByKey返回值的类型不需要和RDD中value的类型一致。
//因为aggregateByKey是对相同Key中的值进行聚合操作,所以aggregateByKey'函数最终返回的类型还是PairRDD,
//对应的结果是Key和聚合后的值,而aggregate函数直接返回的是非RDD的结果。
//步骤 List((1,3),(1,2),(1,4),(2,3))
//1.输入<key,value>,以及初始值zeroValue("100").
//2.每个分区:将分配到该分区部分List的第一个<k,v>的Value值,执行seqOp函数,得到与zeroValue类型相同的<k,new value>
//3.继续该分区的下一个<k,v>,如果k和2步骤的结果k相同,就继续将2的结果作为zeroVlue进行聚合,得到新的<k,new value1>,
//4.一直聚合下去,直到该分区所有相同key的value都已经按照seqOp函数处理完毕,那么分区聚合完毕。
//5.然后对各个分区的结果执行combOp函数,将相同key的value继续聚合.
//6.分区也聚合完毕,最终输出 <K,U>
import org.apache.spark.{SparkConf, SparkContext}
/**
* 将数据拆分成两个分区
* //分区一数据
* (1,3)
* (1,2)
* //分区二数据
* (1,4)
* (2,3)
*
* //分区一相同key的数据进行合并
* seq: 100 3 //(1,3)开始和中立值进行合并 合并结果为 1003
* seq: 1003 2 //(1,2)再次合并 结果为 10032
*
* //分区二相同key的数据进行合并
* seq: 100 4 //(1,4) 开始和中立值进行合并 1004
* seq: 100 3 //(2,3) 开始和中立值进行合并 1003
*
* 将两个分区的结果进行合并
* //key为2的,只在一个分区存在,不需要合并 (2,1003)
* (2,1003)
*
* //key为1的, 在两个分区存在,并且数据类型一致,合并
* comb: 10032 1004
* (1,100321004)
*/
object TestApi_1 {
def main(args: Array[String]): Unit = {
val sparkConf: SparkConf = new SparkConf().setAppName("AggregateByKey").setMaster("local")
val sc: SparkContext = new SparkContext(sparkConf)
val data=List((1,3),(1,2),(1,4),(2,3))
val rdd=sc.parallelize(data, 2)
//合并不同partition中的值,a,b得数据类型为zeroValue的数据类型
def combOp(a:String,b:String):String={
println("combOp: "+a+"\t"+b)
a+b
}
//合并在同一个partition中的值,a的数据类型为zeroValue的数据类型,b的数据类型为原value的数据类型
def seqOp(a:String,b:Int):String={
println("SeqOp:"+a+"\t"+b)
a+b
}
rdd.foreach(println)
//zeroValue:中立值,定义返回value的类型,并参与运算
//seqOp:用来在同一个partition中合并值
//combOp:用来在不同partiton中合并值
val aggregateByKeyRDD=rdd.aggregateByKey("100")(seqOp, combOp)
aggregateByKeyRDD.foreach(e => println(e))
//输出结果
//(2,1003) (1,100321004)
sc.stop()
}
}
j.代码演示
import org.apache.spark.{SparkConf, SparkContext}
object Test3 {
def main(args: Array[String]): Unit = {
val sparkConf: SparkConf = new SparkConf().setAppName("Test-1").setMaster("local")
val sc: SparkContext = new SparkContext(sparkConf)
val data1 = List((1,2),(3,4),(3,6));
val rdd1 = sc.parallelize(data1, 2)
//1.结果:{(1,2),(3,10)}
val rdd2 = rdd1.reduceByKey((x,y) => x + y);
rdd2.foreach(e => print(e + ","))
println("=========================================")
//2.结果:{(1,CompactBuffer(2)),(3,CompactBuffer(4, 6))}
val rdd3 = rdd1.groupByKey();
rdd3.foreach(e => print(e + ","))
println("=========================================")
//3.结果
// (Wilma,(3,286.0))
//(Fred,(3,274.0))
val initialScores = Array(("Fred", 88.0), ("Fred", 95.0), ("Fred", 91.0), ("Wilma", 93.0), ("Wilma", 95.0), ("Wilma", 98.0))
val d1 = sc.parallelize(initialScores)
type MVType = (Int, Double) //定义一个元组类型(科目计数器,分数)
d1.combineByKey(
score => (1, score), //createCombiner,将88变成(1,88)
(c1: MVType, newScore) => (c1._1 + 1, c1._2 + newScore), //mergeValue,针对每个分区:将相同key的分数聚合成新的tuple (2,88+91)
(c1: MVType, c2: MVType) => (c1._1 + c2._1, c1._2 + c2._2) //mergeCombiners,针对所有分区,将不同分区相同key的结果进行聚合
).foreach(println)
println("=========================================")
//4.结果:{(1,3),(3,5),(3,7)}
val rdd4 = rdd1.mapValues(x => x + 1);
rdd4.foreach(e => print(e + ","))
println("=========================================")
//5.结果:{(1,2),(1,3),(1,4),(1,5),(3,4),(3,5)}
val rdd5 = rdd1.flatMapValues(x => x to 5);
rdd5.foreach(e => print(e + ","))
println("=========================================")
//6.结果:{1,3,3}
val rdd6 = rdd1.keys
rdd6.foreach(e => print(e + ","))
println("=========================================")
//7.结果:{2,4,6}
val rdd7 = rdd1.values
rdd7.foreach(e => print(e + ","))
println("=========================================")
//8.结果:{(1,2),(3,4),(3,6)}
val rdd8 = rdd1.sortByKey()
rdd8.foreach(e => print(e + ","))
println("=========================================")
//9.结果:(2,1003) (1,100321004)
val data=List((1,3),(1,2),(1,4),(2,3))
val rdd=sc.parallelize(data, 2)
//合并不同partition中的值,a,b得数据类型为zeroValue的数据类型
def combOp(a:String,b:String):String={
println("combOp: "+a+"\t"+b)
a+b
}
//合并在同一个partition中的值,a的数据类型为zeroValue的数据类型,b的数据类型为原value的数据类型
def seqOp(a:String,b:Int):String={
println("SeqOp:"+a+"\t"+b)
a+b
}
rdd.foreach(println)
//zeroValue:中立值,定义返回value的类型,并参与运算
//seqOp:用来在同一个partition中合并值
//combOp:用来在不同partiton中合并值
val aggregateByKeyRDD=rdd.aggregateByKey("100")(seqOp, combOp)
aggregateByKeyRDD.foreach(e => println(e))
println("=========================================")
}
}
4.针对两个pairRDD的转换操作(rdd={(1,2),(3,4),(3,6)} other={(3,9)})
a.subtractByKey
//删掉RDD中的键与otherRDD中的键相同的元素
rdd.subtractByKey(other) => new RDD: {(1,2)}
b.join
//内链接:连接操作,对两个rdd进行内链接
rdd.join(other) => new RDD: {(3,(Some(4),9)),(3,(Some(6),9))}
c.rightOuterJoin
//右外连接:连接操作,确保右侧rdd的键必须存在
rdd.rightOuterJoin(other) => new RDD: {(3,(4,Some(9))),(3,(4,Some(9)))}
d.leftOutrtJoin
//左外连接:连接操作,确保左侧rdd的键必须存在
rdd.leftOuterJoin(other) => new RDD: {(1,(2,None)),(3,(4,Some(9))),(3,(6,Some(9)))}
e.cogroup
//将两个rdd中拥有相同键的数据分组到一起
rdd.cogroup(other) => new RDD: {(1,([2],[])),(3,([4,6],[9]))}
f.代码演示
import org.apache.spark.{SparkConf, SparkContext}
object TestJoin {
def main(args: Array[String]): Unit = {
val sparkConf: SparkConf = new SparkConf().setAppName("AggregateByKey").setMaster("local")
val sc: SparkContext = new SparkContext(sparkConf)
val nameData = List((1,"tom1"),(2,"tom2"),(3,"tom3"),(4,"tom4"),(5,"tom5"))
val scoreData = List((1,600),(2,580),(3,450),(4,600))
val nameRdd = sc.parallelize(nameData, 2)
val scoreRdd = sc.parallelize(scoreData, 2)
/*
结果只包含两个rdd都有的元素,输出都非空
(4,(tom4,600))
(2,(tom2,580))
(1,(tom1,600))
(3,(tom3,450))
*/
val r = nameRdd.join(scoreRdd);
r.foreach(e => println(e));
/*
结果:保证左侧必须全部有
(1,(tom1,Some(600)))
(3,(tom3,Some(450)))
(5,(tom5,None))
(2,(tom2,Some(580)))
(4,(tom4,Some(600)))
*/
val r1 = nameRdd.leftOuterJoin(scoreRdd);
r1.foreach(e => println(e));
/*
结果:保证右侧必须全部有值
(1,(Some(tom1),600))
(3,(Some(tom3),450))
(4,(Some(tom4),600))
(2,(Some(tom2),580))
*/
val r2 = nameRdd.rightOuterJoin(scoreRdd);
r2.foreach(e => println(e));
/*
(4,(CompactBuffer(tom4),CompactBuffer(600)))
(2,(CompactBuffer(tom2),CompactBuffer(580)))
(1,(CompactBuffer(tom1),CompactBuffer(600)))
(3,(CompactBuffer(tom3),CompactBuffer(450)))
(5,(CompactBuffer(tom5),CompactBuffer()))
*/
val r3 = nameRdd.cogroup(scoreRdd);
r3.foreach(e => println(e));
println("-------------------------------------------------")
/*
(5,tom5)
*/
val r4 = nameRdd.subtractByKey(scoreRdd);
r4.foreach(e => println(e));
}
}
五、其他RDD变换
------------------------------------------------------
1.pipe
//将rdd的元素传递给脚本或者命令cmd/shell,执行结果返回形成新的RDD
val rdd9 = sc.parallelize(Array("D:\\calllog")).pipe("dir")
rdd9.foreach(println)
println("-----------------------------")
2.coalesce(numPartitions)
//减少分区数直到numPartitions
3.repartition
//再分区 -- 可增可减
4.repartitionAndSortWithinPartitions(partitioner)
//再分区并在分区内进行排序
5.代码演示
import org.apache.spark.{SparkConf, SparkContext}
object Test6 {
def main(args: Array[String]): Unit = {
val sparkConf: SparkConf = new SparkConf().setAppName("Test-1").setMaster("local")
val sc: SparkContext = new SparkContext(sparkConf)
val data = List(5,5,7,7,1,67,122,34,1,2,3,3);
val rdd1 = sc.parallelize(data, 4)
//"rdd1 parts:4"
println("rdd1 parts:" + rdd1.partitions.length)
val rdd2 = rdd1.coalesce(3);
//"rdd2 parts:3"
println("rdd2 parts:" + rdd2.partitions.length)
val rdd3 = rdd1.coalesce(5);
//"rdd3 parts:4",coalesce只能减少分区,不能增加分区
println("rdd3 parts:" + rdd3.partitions.length)
//需要增加分区,使用再分区
val rdd4 = rdd1.repartition(5);
//"rdd4 parts:5",coalesce只能减少分区,不能增加分区,repartition可以增加分区也可以减少分区
println("rdd4 parts:" + rdd4.partitions.length)
}
}
五、RDD的Action行动
--------------------------------------------------------
1.针对单个元素RDD的行动操作,以{1,2,3,3}为例 -- spark是懒操作,只有触发行动,才会进行行动之前的所有计算过程
a.collect()
//展示所有元素
rdd.collect() => new Add: {1,2,3,3}
b.count()
//统计元素个数
rdd.count() => Int:4
c.countByValue()
//统计各元素在rdd中,出现的次数,返回一个Map[T,Long]的映射
rdd.countByValue() => Map[T,Long]: {(1,1),(2,1),(3,2)}
d.take(num)
//take用于获取RDD中从0到num-1下标的元素,不排序
//def take(num: Int): Array[T]
rdd.take(2) => Array[T]: {1,2}
e.top(num)
//top函数用于从RDD中,按照默认(降序)或者指定的排序规则,返回前num个元素。
//def top(num: Int)(implicit ord: Ordering[T]): Array[T]
rdd.top(2) => Array[T]: {3,3}
//如果top之前定义了隐式排序法则,那么就会先按照法则进行排序,然后取出前num个元素
implicit val myOrd = implicitly[Ordering[Int]].reverse
rdd.top(2) => Array[T]: {1,2}
f.takeOrdered(num)(ordering)
//按提供的顺序,返回最前面的num个元素的集合,缺省表示升序
rdd.takeOrdered(2)(myOrdering) => Array[T]: {3,3}
//如果takeOrdered之前定义了隐式排序法则ordering,那么就会先按照法则进行排序,然后返回最前面的num个元素的集合
implicit val myOrd = implicitly[Ordering[Int]].reverse
rdd.takeOrdered(2)(myOrdering) => Array[T]: {1,2}
g.takeSample(withReplacement,num,[seed])
//从rdd中返回任意一些元素
rdd.takeSample(false,1) => Array[T] : { 非确定的 }
h.reduce(func)
//按照函数法则,整合RDD中的所有数据,循环应用法则
//def reduce(f: (T, T) => T): T
rdd.reduce((x,y)=>x+y) => T: 9 //(((1 + 2) + 3) + 3)
i.fold(zero)(func)
//和reduce一样,但是需要初始值
rdd.fold(1)((x,y)=>x+y) => T: 13 //注意,zero作为op函数的初始值,是每个分区的初始值。注意:是每个分区初始值是1,然后4个分区,结果就是13
j.aggregate(zeroValue)(seqOp,combOp)
//和reduce()相似,但是通常返回不同类型的函数
//将每个分区里面的元素进行聚合,然后用combine函数将每个分区的结果和初始值(zeroValue)进行combine操作。
//这个函数最终返回的类型不需要和RDD中元素类型一致。
//seqOp操作会聚合各分区中的元素,然后combOp操作把所有分区的聚合结果再次聚合,两个操作的初始值都是zeroValue.
//seqOp的操作是遍历分区中的所有元素(T),第一个T跟zeroValue做操作,结果再作为与第二个T做操作的zeroValue,直到遍历完整个分区。
//combOp操作是把各分区聚合的结果,再聚合。
//aggregate函数返回一个跟RDD不同类型的值。
//因此,需要一个操作seqOp来把分区中的元素T合并成一个U,另外一个操作combOp把所有U聚合。
scala> rdd.par.aggregate((0,0))(
(acc,number) => (acc._1 + number, acc._2 + 1),
(par1,par2) => (par1._1 + par2._1, par1._2 + par2._2)
)
res0: (Int, Int) = (45,9)
scala> res0._1 / res0._2
res1: Int = 5
过程大概这样:
首先,初始值是(0,0),这个值在后面2步会用到。
然后,(acc,number) => (acc._1 + number, acc._2 + 1),number即是函数定义中的T,这里即是List中的元素。
所以acc._1 + number,acc._2 + 1的过程如下。
1. 0+1, 0+1
2. 1+2, 1+1
3. 3+3, 2+1
4. 6+4, 3+1
5. 10+5, 4+1
6. 15+6, 5+1
7. 21+7, 6+1
8. 28+8, 7+1
9. 36+9, 8+1
结果即是(45,9)
这里演示的是单线程计算过程,实际Spark执行中是分布式计算,可能会把List分成多个分区,假如3个,p1(1,2,3,4),p2(5,6,7,8),p3(9),
经过计算各分区的的结果(10,4),(26,4),(9,1),这样,执行(par1,par2) =>(par1._1 + par2._1, par1._2 + par2._2)就是(10+26+9,4+4+1)
即(45,9),
再求平均值就简单了。
k.foreach(func)
//遍历,对RDd中的每个元素使用给定的元素
rdd.foreach(func) => 无
l.first
//取出第一个元素take(1)
m.代码演示
import org.apache.spark.{SparkConf, SparkContext}
object Test4 {
def main(args: Array[String]): Unit = {
val sparkConf: SparkConf = new SparkConf().setAppName("Test-1").setMaster("local")
val sc: SparkContext = new SparkContext(sparkConf)
val data1 = List(1,2,3,3);
val rdd1 = sc.parallelize(data1, 2)
//1.结果:{1,2,3,3}
val rdd2 = rdd1.collect();
rdd2.foreach(e => print(e + ","))
println("=========================================")
//2.结果:4
val rdd3 = rdd1.count();
println(rdd3)
println("=========================================")
//3.结果:{(2,1),(1,1),(3,2)}
val rdd4 = rdd1.countByValue();
rdd4.foreach(e => print(e + ","))
println("=========================================")
//4.结果:{1,2}
val rdd5 = rdd1.take(2);
rdd5.foreach(e => print(e + ","))
println("=========================================")
//5.结果:{3,3}
val rdd6 = rdd1.top(2);
//自定义排序函数
//implicit val myOrd = implicitly[Ordering[Int]].reverse
rdd6.foreach(e => print(e + ","))
println("=========================================")
//6.结果:本来该是{1,2},但是自定义了反转函数,所以变成了{3,3}
//自定义排序函数
implicit val myOrd = implicitly[Ordering[Int]].reverse
val rdd7 = rdd1.takeOrdered(2);
rdd7.foreach(e => print(e + ","))
println("=========================================")
//7.结果:{1,3,3},取三个元素,但是不确定是哪3个
val rdd8 = rdd1.takeSample(false,3);
rdd8.foreach(e => print(e + ","))
println("=========================================")
//8.结果:9
val rdd9 = rdd1.reduce((x,y)=>x+y);
println(rdd9)
println("=========================================")
//9.结果:12,与分区数有关系
val rdd10 = rdd1.fold(1)((x,y)=>x+y);
println(rdd10)
println("=========================================")
//10.结果:(45,9)
val data = List(1,2,3,4,5,6,7,8,9);
val rdd11 = sc.parallelize(data, 2)
val rdd12 = rdd11.aggregate((0,0))(
(acc,number) => (acc._1 + number, acc._2 + 1),
(par1,par2) => (par1._1 + par2._1, par1._2 + par2._2)
);
println(rdd12)
println("=========================================")
}
}
2.针对PairRDD 的行动操作,以键值对集合{(1,2),(3,4),(3,6)} 为例
a.countByKey()
对每个键对应的元素分别计数
rdd.countByKey() => {(1,1),(3,2)}
b.collectAsMap()
//将结果以映射表的形式返回,以便查询
rdd.collectAsMap() => Map : {(1,2),(3,4),(3,6)}
c.lookup(key)
返回给定键对应的所有值
rdd.lookup(3) => [4,6]
d.代码演示
import org.apache.spark.{SparkConf, SparkContext}
object Test5 {
def main(args: Array[String]): Unit = {
val sparkConf: SparkConf = new SparkConf().setAppName("Test-1").setMaster("local")
val sc: SparkContext = new SparkContext(sparkConf)
val data1 = List((1,2),(3,4),(3,6));
val rdd1 = sc.parallelize(data1, 2)
//1.结果:{(1,1),(3,2)}
val rdd2 = rdd1.countByKey();
rdd2.foreach(e => print(e + ","))
println("=========================================")
//2.结果:
// 1:2'
// 3:4
// 3:6
val rdd3:scala.collection.Map[Int,Int] = rdd1.collectAsMap();
rdd3.foreach(
u =>{
println((u _1).toString() + ":" + u._2);
}
)
println("=========================================")
//3.结果:{4,6}
val rdd4 = rdd1.lookup(3);
rdd4.foreach(e => print(e + ","))
println("=========================================")
}
}
六、其他RDD Action
------------------------------------------------------
1.saveAsTextFile(path)
//保存到文件[aa是一个文件夹,用于保存MR运行的结果]
//rdd4.saveAsTextFile("d:\\aa")
2.saveAsSequenceFile(path)
//保存成序列文件
3.saveAsObjectFile(path)
//(Java and Scala)
//reduceKey的过程中指定分区数
val rdd4 = rdd3.reduceByKey(_ + _,3)
rdd4.saveAsObjectFile("d:\\aa")
七、解决spark的数据倾斜问题
-----------------------------------------------------------
import org.apache.spark.{SparkConf, SparkContext}
import scala.util.Random
/**
* spark数据倾斜
*/
object Test7 {
def main(args: Array[String]): Unit = {
val sparkConf: SparkConf = new SparkConf().setAppName("Test-1").setMaster("local")
val sc: SparkContext = new SparkContext(sparkConf)
val rdd1 = sc.textFile("d:\\1.txt")
val rdd2 = rdd1.flatMap(line => line.split(" "))
val rdd3 = rdd2.map(word => (word,1))
//对key进行改造,增加hash分区后缀
val rdd4 = rdd3.map(t => {
val word = t._1;
val r = Random.nextInt(10);
(word + "_" + r.toString , t._2)
});
//首次聚合
val rdd5 = rdd4.reduceByKey((x,y) => x + y,10);
//第二次映射,去掉“_num”hash分区数
val rdd6 = rdd5.map(
t =>{
val word = t._1.split("_")(0);
(word,t._2)
}
)
//第二次聚合
val rdd7 = rdd6.reduceByKey(_ + _,10);
//打印结果
rdd7.foreach(println)
}
}
八、spark集成hadoop的HA
-------------------------------------------------------
1.复制core-site.xml + hdfs-site.xml到spark/conf目录下
2.分发文件到spark所有work节点
3.启动spark集群
4.启动spark-shell,连接spark集群上
$>spark-shell --master spark://s100:7077
$scala>sc.textFile("hdfs://mycluster/data/spark/1.txt").collect();