一、spark概述
1.Spark 的安装与部署
Spark 的安装部署方式有以下几种模式:
Standalone
YARN
Mesos
Amazon EC2()Spark Standalone 伪分布的部署
配置文件:conf/spark-env.sh
export JAVA_HOME=/root/training/jdk1.7.0_75
export SPARK_MASTER_HOST=spark81
export SPARK_MASTER_PORT=7077
下面的可以不写,默认
export SPARK_WORKER_CORES=1
export SPARK_WORKER_MEMORY=1024
()Spark Standalone 全分布的部署
配置文件:conf/spark-env.sh
export JAVA_HOME=/root/training/jdk1.7.0_75
export SPARK_MASTER_HOST=spark82
export SPARK_MASTER_PORT=7077
下面的可以不写,默认
export SPARK_WORKER_CORES=1
export SPARK_WORKER_MEMORY=1024
配置文件:conf/slave
spark83
spark84
3、Spark HA 的实现
()基于文件系统的单点恢复
主要用于开发或测试环境。当 spark 提供目录保存 spark Application 和 worker 的
注册信息,并将他们的恢复状态写入该目录中,这时,一旦 Master 发生故障,就可以
通过重新启动 Master 进程(sbin/start-master.sh),恢复已运行的 spark Application 和
worker 的注册信息。
基于文件系统的单点恢复,主要是在 spark-en.sh 里对 SPARK_DAEMON_JAVA_OPTS设置
配置参数
参考值
spark.deploy.recoveryMode
设置为 FILESYSTEM 开启单点恢复功能,默认值:NONE
spark.deploy.recoveryDirectory Spark 保存恢复状态的目录
参考:
export SPARK_DAEMON_JAVA_OPTS="-Dspark.deploy.recoveryMode=FILESYSTEM
-Dspark.deploy.recoveryDirectory=/root/training/spark-2.1.0-bin-hadoop2.
4/recovery"
测试:
1、在 spark82 上启动 Spark 集群
2、在 spark83 上启动 spark shell
MASTER=spark://spark82:7077 spark-shell
3、在 spark82 上停止 master
stop-master.sh
4、观察 spark83 上的输出:
5、在 spark82 上重启 master
start-master.sh
()基于 Zookeeper 的 Standby Masters
ZooKeeper 提供了一个 Leader Election 机制,利用这个机制可以保证虽然集群
存在多个 Master,但是只有一个是 Active 的,其他的都是 Standby。当 Active 的
Master 出现故障时,另外的一个 Standby Master 会被选举出来。由于集群的信息,
包括 Worker, Driver 和 Application 的信息都已经持久化到 ZooKeeper,因此在切
换的过程中只会影响新 Job 的提交,对于正在进行的 Job 没有任何的影响。加入
ZooKeeper 的集群整体架构如下图所示。
参考:export
SPARK_DAEMON_JAVA_OPTS="-Dspark.deploy.recoveryMode=ZOOKEEPER
-Dspark.deploy.zookeeper.url=spark82:2181,spark83:2181,spark84:2181
-Dspark.deploy.zookeeper.dir=/spark"
另外:每个节点上,需要将以下两行注释掉
执行 Spark Demo 程序
1、执行 Spark Example 程序
()示例程序:EXAMPLE_HOME/examples/src/main
有 Java、Scala 等等等
()Demo:蒙特卡罗求 PI
命令:
spark-submit
–master
spark://spark81:7077
–class
org.apache.spark.examples.SparkPi examples/jars/spark-examples_2.11-2.1.0.jar 100
2、使用 Spark Shell
spark-shell 是 Spark 自带的交互式 Shell 程序,方便用户进行交互式编程,用户可
以在该命令行下用 scala 编写 spark 程序。
()启动 Spark Shell:spark-shell
也可以使用以下参数:
参数说明:
–master spark://spark81:7077 指定 Master 的地址
–executor-memory 2g 指定每个 worker 可用内存为 2G
–total-executor-cores 2 指定整个集群使用的 cup 核数为 2 个
例如:
spark-shell
–master
spark://spark81:7077
–executor-memory
2g
–total-executor-cores 2
()注意:
如果启动 spark shell 时没有指定 master 地址,但是也可以正常启动 spark shell
和执行 spark shell 中的程序,其实是启动了 spark 的 local 模式,该模式仅在本机
启动一个进程,没有与集群建立联系。
请注意 local 模式和集群模式的日志区别:
(*)在 Spark Shell 中编写 WordCount 程序程序如下:
sc.textFile(“hdfs://192.168.88.111:9000/data/data.txt”).flatMap(.split("
")).map((,1)).reduceByKey(+).saveAsTextFile(“hdfs://192.168.88.111:9000/output/spark
/wc”)
说明:
sc 是 SparkContext 对象,该对象时提交 spark 程序的入口
textFile(“hdfs://192.168.88.111:9000/data/data.txt”)是 hdfs 中读取数据
flatMap(.split(" "))先 map 在压平
map((,1))将单词和 1 构成元组
reduceByKey(+)按照 key 进行 reduce,并将 value 累加
saveAsTextFile(“hdfs://192.168.88.111:9000/output/spark/wc”) 将 结 果 写 入 到hdfs 中
二、IDEA实现WC
<?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">
<parent>
<artifactId>sparkfuxi</artifactId>
<groupId>com.ccj.pxj</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>sparkcore</artifactId>
<dependencies>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.11</artifactId>
<version>2.2.1</version>
<!--<scope>provided</scope>-->
</dependency>
<!-- 导入spark sql的依赖 -->
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-sql_2.11</artifactId>
<version>2.2.1</version>
<!-- <scope>provided</scope>-->
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-hive_2.11</artifactId>
<version>2.2.1</version>
<!--
<scope>provided</scope>
-->
<!-- 导入spark streaming的依赖-->
</dependency>
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-clients</artifactId>
<version>0.10.2.1</version>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-streaming-kafka-0-10_2.11</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>org.spark-project.hive</groupId>
<artifactId>hive-jdbc</artifactId>
<version>1.2.1.spark2</version>
<!--
<scope>provided</scope>
-->
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-streaming_2.11</artifactId>
<version>2.1.1</version>
</dependency>
</dependencies>
<build>
<finalName>Test_Spark</finalName>
<plugins>
<!-- 设置项目编译版本-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<!-- 用于编译scala代码到class -->
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<version>3.2.2</version>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<archive>
<manifest>
<mainClass></mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
package com.ccj.pxj.wc
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object WCSparkCore {
def main(args: Array[String]): Unit = {
val sparkConf = new SparkConf().setAppName("pxj").setMaster("local[4]")
val sc: SparkContext = new SparkContext(sparkConf)
val datas: RDD[String] = sc.textFile("data/wc.txt")
val ss = datas.flatMap(_.split(",")).map((_, 1)).reduceByKey(_ + _).saveAsTextFile("data/wcOut.txt")
sc.stop()
}
}
三、sparkcore
3.1RDD
什么是 RDD?
RDD(Resilient Distributed Dataset)叫做弹性分布式数据集,是 Spark 中最基本的数据抽象,它代表一个不可变、可分区、里面的元素可并行计算的集合。RDD
具有数据流模型的特点:自动容错、位置感知性调度和可伸缩性。RDD 允许用户在执行多个查询时显式地将工作集缓存在内存中,后续的查询能够重用工作集,
这极大地提升了查询速度
3.2 RDD五大特征
一组分片(Partition),即数据集的基本组成单位。对于 RDD 来说,每个分片都会被一个计算任务处理,并决定并行计算的粒度。用户可以在创建 RDD时指定 RDD 的分片个数,如果没有指定,那么就会采用默认值。默认值就是程序所分配到的 CPU Core 的数目。
一个计算每个分区的函数。Spark 中 RDD 的计算是以分片为单位的,每个 RDD都会实现 compute 函数以达到这个目的。compute 函数会对迭代器进行复合,不需要保存每次计算的结果。
RDD 之间的依赖关系。RDD 的每次转换都会生成一个新的 RDD,所以 RDD 之间就会形成类似于流水线一样的前后依赖关系。在部分分区数据丢失时,Spark 可以通过这个依赖关系重新计算丢失的分区数据,而不是对 RDD 的所有分区进行重新计算。
一个 Partitioner,即 RDD 的分片函数。当前 Spark 中实现了两种类型的分片函 数 , 一 个 是 基 于 哈 希 的 HashPartitioner , 另 外 一 个 是 基 于 范 围 的RangePartitioner。只有对于于 key-value 的 RDD,才会有 Partitioner,非 key-value的 RDD 的 Parititioner 的值是 None。Partitioner 函数不但决定了 RDD 本身的分片数量,也决定了 parent RDD Shuffle 输出时的分片数量。
一个列表,存储存取每个 Partition 的优先位置(preferred location)。对于一个 HDFS 文件来说,这个列表保存的就是每个 Partition 所在的块的位置。按照“移动数据不如移动计算”的理念,Spark 在进行任务调度的时候,会尽可能地将计算任务分配到其所要处理数据块的存储位置
3.3 RDD创建
RDD 的创建方式
通过外部的数据文件创建,如 HDFS val rdd1 = sc.textFile(“hdfs://192.168.88.111:9000/data/data.txt”)
通过 sc.parallelize 进行创建val rdd1 = sc.parallelize(Array(1,2,3,4,5,6,7,8))
RDD 的类型:Transformation 和 Action
3.4 RDD 的基本原理
四、算子
4.1 Transformation
RDD 中的所有转换都是延迟加载的,也就是说,它们并不会直接计算结果。相反的,它们只是记住这些应用到基础数据集(例如一个文件)上的转换动作。只有当发生一个要求返回结果给 Driver 的动作时,这些转换才会真正运行。这种设计让 Spark更加有效率地运行。
转换
含义
map(func)
返回一个新的 RDD,该 RDD 由每一个输入元素经过 func 函数转
换后组成
filter(func)
返回一个新的RDD,该RDD 由经过func 函数计算后返回值为true
的输入元素组成
flatMap(func)
类似于 map,但是每一个输入元素可以被映射为 0 或多个输出
元素(所以 func 应该返回一个序列,而不是单一元素)
mapPartitions(func)
类似于 map,但独立地在 RDD 的每一个分片上运行,因此在类
型为 T 的 RDD 上运行时,func 的函数类型必须是 Iterator[T] =>
Iterator[U]
mapPartitionsWithIndex(func)
类似于 mapPartitions,但 func 带有一个整数参数表示分片的索引值,因此在类型为 T 的 RDD 上运行时,func 的函数类型必须
是(Int, Interator[T]) => Iterator[U]
sample(withReplacement, fraction, seed)
根据fraction 指定的比例对数据进行采样,可以选择是否使用随
机数进行替换,seed 用于指定随机数生成器种子
union(otherDataset)
对源 RDD 和参数 RDD 求并集后返回一个新的 RDD
intersection(otherDataset)
对源 RDD 和参数 RDD 求交集后返回一个新的 RDD
distinct([numTasks]))
对源 RDD 进行去重后返回一个新的 RDD
groupByKey([numTasks])
在一个(K,V)的 RDD 上调用,返回一个(K, Iterator[V])的 RDD
reduceByKey(func, [numTasks])
在一个(K,V)的 RDD 上调用,返回一个(K,V)的 RDD,使用指定的reduce 函数,将相同 key 的值聚合到一起,与 groupByKey 类似,
reduce 任务的个数可以通过第二个可选的参数来设置
aggregateByKey(zeroValue)(seqOp,combOp,[numTasks])
sortByKey([ascending], [numTasks])
在一个(K,V)的 RDD 上调用,K 必须实现 Ordered 接口,返回一个
按照 key 进行排序的(K,V)的 RDD
sortBy(func,[ascending], [numTasks])
与 sortByKey 类似,但是更灵活
join(otherDataset, [numTasks])
在类型为(K,V)和(K,W)的 RDD 上调用,返回一个相同 key 对应的
所有元素对在一起的(K,(V,W))的 RDD
cogroup(otherDataset, [numTasks])
在 类 型 为 (K,V) 和 (K,W) 的 RDD 上 调 用 , 返 回 一 个
(K,(Iterable,Iterable))类型的 RDD
cartesian(otherDataset)
笛卡尔积
pipe(command, [envVars])
coalesce(numPartitions)
repartition(numPartitions)
repartitionAndSortWithinPartitions(partitioner)
Action算子
动作
含义
reduce(func)
通过 func 函数聚集 RDD 中的所有元素,这个功能必须是课交换且可并联的
collect()
在驱动程序中,以数组的形式返回数据集的所有元素
count()
返回 RDD 的元素个数
first()
返回 RDD 的第一个元素(类似于 take(1))
take(n)
返回一个由数据集的前 n 个元素组成的数组
takeSample(withReplacement,num, [seed])
返回一个数组,该数组由从数据集中随机采样的 num 个元素组成,可以选
择是否用随机数替换不足的部分,seed 用于指定随机数生成器种子
takeOrdered(n, [ordering])
saveAsTextFile(path)
将数据集的元素以 textfile 的形式保存到 HDFS 文件系统或者其他支持的文
件系统,对于每个元素,Spark 将会调用 toString 方法,将它装换为文件中
的文本
saveAsSequenceFile(path)
将数据集中的元素以 Hadoop sequencefile 的格式保存到指定的目录下,可
以使 HDFS 或者其他 Hadoop 支持的文件系统。
saveAsObjectFile(path)
countByKey()
针对(K,V)类型的 RDD,返回一个(K,Int)的 map,表示每一个 key 对应的元素
个数。
foreach(func)
在数据集的每一个元素上,运行函数 func 进行更新。
package com.ccj.pxj.core.zq
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object RDDCoreDemo {
def main(args: Array[String]): Unit = {
/*
* //过滤出大于等于十的元素 val rdd3 = rdd2.filter(_ >= 10)
*/
val conf = new SparkConf().setAppName("pxj").setMaster("local[4]")
val sc = new SparkContext(conf)
val data = sc.parallelize(List(1,5,8,9,10,20))
data.map(_ * 2).sortBy(x=>x,true).filter(x=>x>10).collect().foreach(println)
print("-----------------------------------------------")
//将 rdd1 里面的每一个元素先切分在压平
val buffer1: Array[Array[String]] = sc.parallelize(Array("a b c ", "d e f ", "r t y ")).map(_.split(" ")).collect()
for(r<-buffer1){
for(l<-r){
println(l.toArray.toBuffer)
}
}
sc.parallelize(Array("a b c ", "d e f ", "r t y ")).flatMap(_.split(" ")).collect().foreach(println)
println("-------------------------------------------------------------------------------------------------------------")
// val rdd1 = sc.parallelize(List(5, 6, 4, 3)) val rdd2 = sc.parallelize(List(1, 2, 3, 4)) //求并集 val rdd3 = rdd1.union(rdd2) //求交集 val rdd4 = rdd1.intersection(rdd2) //去重 rdd3.distinct.collect rdd4.collect
val rdd1 = sc.parallelize(1 to 10)
val rdd2 = sc.parallelize(2 to 15)
rdd1.union(rdd2).collect().foreach(print)//并集
println("\t")
rdd1.intersection(rdd2).collect().foreach(println)
println("------------------------------------------------------")
val rdd3 = sc.parallelize(List(("tom", 1), ("jerry", 3), ("kitty", 2)))
val rdd4 = sc.parallelize(List(("jerry", 2), ("tom", 1), ("shuke", 2)))
val rdd5: RDD[(String, (Int, Int))] = rdd3.join(rdd4)
rdd5.foreach(println)
val rdd6: RDD[(String, Int)] = rdd3.union(rdd4)
rdd6.groupByKey().foreach(println)
println("-----------------------------------")
rdd6.foreach(println)
println("-------------------------------------------------------")
val rdd7 = sc.parallelize(List(("tom", 1), ("tom", 2), ("jerry", 3), ("kitty", 2)))
val rdd8 = sc.parallelize(List(("jerry", 2), ("tom", 1), ("shuke", 2)))
val cogroup: RDD[(String, (Iterable[Int], Iterable[Int]))] = rdd7.cogroup(rdd8)
println("-------------")
cogroup.foreach(println)
println("reduce 聚合")
val sum: Int = sc.parallelize(1 to 10).reduce(_ + _)
println(sum)
println("----------------------------------------------------------------------------")
val rdd9 = sc.parallelize(List(("tom", 1), ("jerry", 3), ("kitty", 2), ("shuke", 1)))
val rdd10 = sc.parallelize(List(("jerry", 2), ("tom", 3), ("shuke", 2), ("kitty", 5)))
val rdd11: RDD[(String, Int)] = rdd9.union(rdd10)
rdd11.reduceByKey(_+_).sortByKey(false)
rdd11.map(t=>(t._2,t._1)).sortByKey(false).map(x=>(x._2,x._1)).foreach(println)
println("---------------------------------------------------------------------")
sc.stop()
}
}