Spark 有着 DAG(有向无环图)执行引擎,支持离散数据流和内存计算
spark 组件:
1. Spark core :所有功能在其上进行构建
2. SQL:在 core 之上引入的一个新的数据集抽象(SchemaRDD),支持结构和半结构化数据。RDD:弹性可复原的分布式数据 集
3. Spark Streaming:平衡 spark 内核的快速调度功能执行流分析。
4. MLib:机器学习框架。
5. GraphX:Spark 之上的分布式图处理计算
Spark 有自己的集群计算基数,扩展了 Hadoop MR 模型,主要特性是内存的集群计算。
spark 部署三种模式:
1.standalone : 独立模式
2.hadoop yarn(hadoop V2)
3.spark in mapreduce (hadoop V1)
spark 安装:
1.下载 spark-2.3.2-bin-without-hadoop.tgz
2.tar | mv | ln
3.配置环境变量
将 spark/conf/spark-env.sh.template 拷贝成 spark-env.sh ,gedit 开 spark-env.sh,添加如下内容:
export SPARK_DIST_CLASSPATH=$(hadoop classpath)
然后配置 spark 的环境变量
sudo gedit /etc/environment
添加:
SPARK_HOME=/soft/spark
/soft/spark/bin:/soft/spark/sbin
即刻生效,spark-shell启动后,spark的查看端口是4040
4.设置日志级别
将 spark/conf/log4j.properties 修改:
log4j.rootCategory=INFO, console
改成 log4j.rootCategory=WARN, console
spark 类
1.SparkContext:
spark上下文对象,是spark程序的主入口点,负责连接到spark cluster。可用创建RDD,在集群上创建累加器 和广播变量。
每个JVM只能激活一个 SparkContext 对象,创建新的 SparkContex 时必须 stop 掉原来的对象
sc 的 textFile() 方法返回一个 RDD
2.RDD:Resilient(弹性) Distributed(分布式) Dataset(数据集)
是不可变,可分区的元素集合,可进行并行操作。该类包含了可用于与收所有 RDD 之上的基本操作,如 map, filter, Persist
在 RDD 内部,每个 RDD有5个特征:
1.有一个分区列表
2.每个 split 都有一个计算函数
3.存放 parent 的 RDD 依赖列表
4.(可选)基于 key-value 的分区器
5.(可选)首选的位置列表
spark 集群中所有调度和执行都是基于这些方法
$scala>val lines = sc.textFile("/user/ubuntu/xx.txt") //读取HDFS文件系统的文件
$scala>lines.count()
$scala>val lines = sc.textFile("files:///home/ubuntu/xx.txt") //读取本地文件
$scala>lines.count()
spark-shell :默认使用 local 模式运行 spark 程序,没有用到 spark 的集群,类似于 hadoop 的本地模式。
spark-shell --master local [4] 4表示在本地模式下启动的线程数,模拟 spark 集群。
使用 maven 编译和运行 scala 的 spark 程序:
val conf = new SparkConf().setAppName("wordCount") //创建 sparkConf 对象
val sc = new SparkContext(conf) //创建 SparkContext 对象
val lines = sc.textFile("file:/home/ubuntu/a.txt") //加载文件
val words = lines.flatMap(x => x.split(",") //按照 , 切割行,然后进行压扁
val counts = words.map(w => (w,1)).reduceByKey(case (x,y) => x+y) //计算单词个数
counts.saveAsTextFile("file:/home/ubuntu/b.txt")
RDD 支持两种类型的操作: 转化操作和行动操作,转化操作会由一个RDD 生成一个新的RDD,行动操作会对RDD 计算出一个结果,并把结果返回到驱动器程序中,或把结果存储到外部存储系统(如HDFS)中。
默认情况下,Spark 的RDD 会在你每次对它们进行行动操作时重新计算。如果想在多个行动操作中重用同一个RDD,可以使用RDD.persist() 让Spark 把这个RDD 缓存下来。
以下总结了常见的RDD 转化操作:
以下总结了常见的RDD 行动操作:
可以用aggregate() 来计算RDD 的平均值:
val result = input.aggregate((0, 0))(
(acc, value) => (acc._1 + value, acc._2 + 1),//累加器 (元组累加元组结果,RDD单个元素值)=>(元组累加结果+RDD单个元素,元组累加计数+1)
(acc1, acc2) => (acc1._1 + acc2._1, acc1._2 + acc2._2))
val avg = result._1 / result._2.toDouble
假如List(1,2,3,4,5,6,7,8,9,10),对List求平均数,首先,初始值是(0,0),这个值在后面2步会用到,然后,(acc,value) => (acc._1 + value, acc._2 + 1),value即是函数定义中的T,这里即是List中的元素,所以acc._1 + value, 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),这样,执行(acc1,acc2) => (acc1._1 + acc2._1, acc1._2 + acc2._2)就是(10+26+9,4+4+1)即(45,9).再求平均值就简单了。
使用reduceByKey() 和mapValues() 计算每个键对应的平均值
rdd.mapValues(x => (x, 1)).reduceByKey((x, y) => (x._1 + y._1, x._2 + y._2))
combineByKey() 是最为常用的基于键进行聚合的函数。大多数基于键聚合的函数都是用它实现的。和aggregate() 一样,combineByKey() 可以让用户返回与输入数据的类型不同的返回值。
在Scala 中使用combineByKey() 求每个键对应的平均值:
val result = input.combineByKey(
(v) => (v, 1),
(acc: (Int, Int), v) => (acc._1 + v, acc._2 + 1),
(acc1: (Int, Int), acc2: (Int, Int)) => (acc1._1 + acc2._1, acc1._2 + acc2._2)
).map{ case (key, value) => (key, value._1 / value._2.toFloat) }
result.collectAsMap().map(println(_))