spark学习

内容参考自尚硅谷免费教学视频

Spark2.1

spark-submit的使用
  • 执行SparkPi
    在这里插入图片描述
  • 读取和写入文件:
# 1. 读取和写入本地文件系统
bin/spark-submit \
--class com.atguigu.spark.WordCount \
--master spark://master:7077 \
--executor-memory 1G \
./wordcount.jar \
file:///home/xxx/pycharm_proj/software/spark/RELEASE \
file:///home/xxx/pycharm_proj/software/spark/out
# 2. 读取和写入hdfs文件系统
bin/spark-submit \
--class com.atguigu.spark.WordCount \
--master spark://master:7077 \
--executor-memory 1G \
./wordcount.jar \
hdfs://master:9000/RELEASE \
hdfs://master:9000/out
  • 命令行解析
    • 注意几种模式:local是本地;spark(standalone)是spark自身集群;yarn-client/yarn-cluster是yarn集群,区别是是否自动分配driver进程的位置;
      在这里插入图片描述
  • SparkPi实现
// scalastyle:off println
package org.apache.spark.examples

import scala.math.random

import org.apache.spark.sql.SparkSession

/** Computes an approximation to pi */
object SparkPi {
  def main(args: Array[String]) {
    val spark = SparkSession
      .builder
      .appName("Spark Pi")
      .getOrCreate()
    val slices = if (args.length > 0) args(0).toInt else 2
    val n = math.min(100000L * slices, Int.MaxValue).toInt // avoid overflow
    val count = spark.sparkContext.parallelize(1 until n, slices).map { i =>
      val x = random * 2 - 1  /*[-1,1]即边长为2的整个正方形采样*/
      val y = random * 2 - 1
      if (x*x + y*y <= 1) 1 else 0
    }.reduce(_ + _)
    println(s"Pi is roughly ${4.0 * count / (n - 1)}")
    spark.stop()
  }
}
// scalastyle:on println
spark-standalone运行机制
  • client创建driver进程和sparkcontext,并向master注册;master调度Worker创建Executor(一个worker可以多个Executor),Executor向client注册(注意Executor只向master报告状态,而直接跟client注册)
  • client接收Executor的注册后,直接给Executor分配Task(绕过master)
  • 不同工作模式(local、standalone、yarn、mesos),master根据设置不同对应不同的manager(driver、Executor保持不变)
    在这里插入图片描述
spark history server配置
  • spark-defaults.conf设置:设置以下两个选项,注意第二个目录要存在
    在这里插入图片描述
  • spark-env.sh设置:设置hs的访问端口,注意最后一个目录要和上面一致(下面要修改)
    在这里插入图片描述
  • 打开spark的历史服务器:sbin/start-history-server.sh,访问4000端口进行查看(注意上面配置后已经可以写,这里只是查看使用);
spark-HA高可用配置
  • 利用zookeeper进行管理在这里插入图片描述
  • 配置在这里插入图片描述
  • 启动在这里插入图片描述
  • 在另一个节点启动新的master服务(start-master.sh),该master会被zookeeper设置为standby(原master为alive);当发生alive-master意外停止时,会将standby-master设置为alive,由此保证HA在这里插入图片描述在这里插入图片描述
Yarn模式安装
  • 配置:注意第一个配置为关闭内存使用检查,因为yarn检查不太准确,容易报错;当然如果资源多,可以不用关闭;在这里插入图片描述
  • 运行
    在这里插入图片描述
WordCount过程解析

在这里插入图片描述在这里插入图片描述

RDD介绍

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
note:RDD的缓存,如果有血统关系如下:A->B->C 和 A->B->D,不缓存B的话计算C/D都要从A开始计算一遍,导致A->B重复计算多遍;

RDD的转换操作学习(容易出错部分)
1. mapPartitions & mapPartitionsWithIndex
  • 例子:rdd = sc.makeRDD(1 to 10, 4),请输出各partition的"|"拼接结果;
    // 正确写法,要注意Iterator的使用是为了满足partition的要求;
    => rdd.mapPartitions(x => Iterator(x.mkString("|"))).collect()
    =>
    // 错误写法
    => rdd.mapPartitions(Iterator(_.mkString("|"))) 
    => error: **missing parameter type** for expanded function ((x$1) => x$1.mkString("|"))
    =>
    // 例子:要求对每个partition输出 partition序号 + ":" + 各元素用"|"拼接 的结果,如 0:1|2 1:3|4|5
    => rdd.mapPartitionsWithIndex((x,y) => Iterator(x+":"+y.mkString("|"))).collect
    
  • 分析:
    • 上述错误写法涉及scala就近类型转换问题(又叫eta-extention)
    • 对于Iterator(_.mkString(“|”)),scala将其转换为 Iterator(x => x.mkString(“|”)),并最终转换为 y => Iterator( x => x.mkString(“|”)),此时x不知道从哪里来,因此报错;
2. flatMap
  • 例子:rdd = sc.makeRDD(1 to 10, 4),使用flatMap进行各元素加1操作;
    => rdd.flatMap(x => Array(x+1))  
    => // 注意简写会报错;此外注意Array的使用(不一定是Array,但是要Iterable,参考源代码)
    
3.sample
  • 例子:rdd = sc.parallelize(1 to 20),从中随机采样30%的数据,可重复
    => rdd.sample(true, 0.3, seed=2)
    
4. sortBy
  • 例子:rdd = sc.parallelize(1 to 20),对5取余后升序
    // 注意要传入一个函数,用于生成排序的key
    => rdd.sortBy(_%5)  
    => res17: Array[Int] = Array(5, 10, 15, 20, 1, 6, 11, 16, 2, 7, 12, 17, 3, 8, 13, 18, 4, 9, 14, 19)
    
5. union/subtract/intersection/cartesian
  • 例子
    => val rdd = sc.parallelize(Array(1,1,2))
    => rdd.union(sc.parallelize(2 to 5)).collect  // 以下省略collect动作
    => res19: Array[Int] = Array(1, 1, 2, 2, 3, 4, 5)  // 注意没有去重
    => 
    => rdd.subtract(sc.parallelize(2 to 5))
    => res21: Array[Int] = Array(1, 1)  // 注意从前者去除后者出现的元素
    =>
    => rdd.intersection(sc.parallelize(2 to 5))  // 取交集
    =>
    => rdd.cartesian(sc.parallelize(2 to 5))  // 笛卡尔积
    => res23: Array[(Int, Int)] = Array((1,2), (1,3), (1,4), (1,5), (1,2), (1,3), (1,4), (1,5), (2,2), (2,3), (2,4), (2,5))
    
6.pipe
  • 可用于执行外部脚本;例子
    # p.sh
    #!/bin/sh
    echo "AA"
    while read LINE; do
    	echo ">>>"${LINE}
    done
    
    // 调用上述脚本
    => sc.parallelize(1 to 3, 2).pipe("p.sh").collect
    => Array[String] = Array(AA, >>>1, AA, >>>2, >>>3)  //注意到脚本是对分区执行的,所以才只打印2次AA(2、3在同一个分区)
    
7. join、cogroup
  • rdd间的结合;例子
    => val rdd1 = sc.parallelize(1 to 10).map((_, 1))
    => val rdd2 = sc.parallelize(6 to 15).map((_, 1))		
    // 共有部分进行拼接
    => rdd1.join(rdd2).collect
    => res25: Array[(Int, (Int, Int))] = Array((6,(1,1)), (7,(1,1)), (8,(1,1)), (9,(1,1)), (10,(1,1)))
    // 左外连接:以左边rdd为基准,进行拼接(注意key是左rdd所有key)
    => rdd1.leftOuterJoin(rdd2).collect
    => res26: Array[(Int, (Int, Option[Int]))] = Array((1,(1,None)), (2,(1,None)), (3,(1,None)), (4,(1,None)), (5,(1,None)), (6,(1,Some(1))), (7,(1,Some(1))), (8,(1,Some(1))), (9,(1,Some(1))), (10,(1,Some(1))))
    // 右外连接:以右边rdd为基准,进行拼接(注意key是右rdd所有key)
    => res27: Array[(Int, (Option[Int], Int))] = Array((6,(Some(1),1)), (7,(Some(1),1)), (8,(Some(1),1)), (9,(Some(1),1)), (10,(Some(1),1)), (11,(None,1)), (12,(None,1)), (13,(None,1)), (14,(None,1)), (15,(None,1)))
    // cogroup:去重合并所有key,并把value转化为Iterable
    => val rdd3 = sc.parallelize(Array(0,2,4,3,3).map((_,1)))
    => val rdd4 = sc.parallelize(Array(0,1,5,3).map((_,1)))
    => rdd3.cogroup(rdd4).collect
    => res30: Array[(Int, (Iterable[Int], Iterable[Int]))] = Array((0,(CompactBuffer(1),CompactBuffer(1))), (1,(CompactBuffer(),CompactBuffer(1))), (2,(CompactBuffer(1),CompactBuffer())), (3,(CompactBuffer(1, 1),CompactBuffer(1))), (4,(CompactBuffer(1),CompactBuffer())), (5,(CompactBuffer(),CompactBuffer(1))))
    
8. combineByKey
  • 函数及其说明如下
    在这里插入图片描述
  • 例子:某PairRDD存储了一系列(key,value),key可能重复,要求计算每个唯一key的平均值;
    => val rdd1 = sc.parallelize(List(("c", 1),("c", 2),("c", 3),("c", 4),("a", 10),("a", 20),("a", 30),("a", 40),("b", 50),("b", 60),("b", 70),("b", 80)))
    => val r2 = r1.combineByKey(x=>(x, 1), (x:(Int,Int),y)=>(x._1+y, x._2+1), (x:(Int,Int),y:(Int,Int))=>(x._1+y._1, x._2+y._2))
    => // 注意到上面的类型声明,不然会报错丢失类型
    => r2.collect
    => res1: Array[(String, (Int, Int))] = Array((a,(100,4)), (b,(260,4)), (c,(10,4)))
    => // 取平均
    => r2.map(x=>(x._1, x._2._1/x._2._2)).collect
    => res4: Array[(String, Int)] = Array((a,25), (b,65), (c,2))
    => // 另一种写法
    => r2.map{case (x,y)=>(x, y._1/y._2)}.collect
    
9. aggregateByKey
  • 函数及其说明如下:与上一个函数不同的是,该函数可以指定默认值,此后遇到任何key,都在默认值上累积,不用像上一个函数一样先初始化新key的对象;
    在这里插入图片描述
  • 例子:同上
    => val r1 = sc.parallelize(List(("c", 1),("c", 2),("c", 3),("c", 4),("a", 10),("a", 20),("a", 30),("a", 40),("b", 50),("b", 60),("b", 70),("b", 80)))
    => val r3 = r1.aggregateByKey((0,0))((x,y)=>(x._1+y, x._2+1), (x,y)=>(x._1+y._1, x._2+y._2))
    => r3.collect
    => res8: Array[(String, (Int, Int))] = Array((a,(100,4)), (b,(260,4)), (c,(10,4)))
    => // 以下求平均值同上一个例子
    
RDD的运行
1. RDD的宽窄依赖
  • 针对父RDD->子RDD:窄依赖就是一对一,或者多对一;宽依赖就是一对多;宽依赖发生一个父RDD的partition被shuffle到子RDD的多个partition,故称为宽依赖(可理解为很宽松地被依赖)
  • 宽窄依赖的划分用于对确定RDD的运行阶段,见下面说明
    在这里插入图片描述
2. RDD任务切分
  • 说明:
    • Application:jar包
    • Job:每个行动算子算是一个Job
    • Stage:发生宽依赖的地方划分stage
    • Task:并行度,即partition个数
      在这里插入图片描述
3. RDD的运行规划图
  • 这里主要说明stage的划分:如下图,spark是倒着进行stage的划分的,遇到宽依赖(这里是reduceByKey),则把宽依赖及其前面操作整个视为一个stage(这里是stage1),并把宽依赖前面的所有操作又视为一个stage(这里是stage2);注意划分不含输入输出(这里是HDFS的读写操作)
    在这里插入图片描述
4. RDD的检查点机制

在这里插入图片描述

5. 键值对RDD数据分区

在这里插入图片描述

6. 数据读取与保存
  • TextFile, SequenceFile(调用HadoopFile), ObjectFile(本质上也是调用SequenceFile)
  • ObjectFile例子在这里插入图片描述
    在这里插入图片描述
  • JDBC读取sql数据
    在这里插入图片描述
7. 累加器
  • 例子
    => val sc = new SparkContext(conf)
    => var sum = 0
    => val arr = Array(1,2,3,4,5)
    => val rdd = sc.makeRDD(arr)
    => rdd.map{
     		sum += x
     		x
     		}.collect()
    => println(sum)  // 这里打印结果是0
    // 问题出在 driver、executor身上,各个数据分区(如3个分区数据为[1,2],[3,4],[5])的executor都有一个sum(分别为3、7、5),driver也有一个sum,没有拉取各executor的sum求和,所以始终为0;
    // 为了解决上述问题,就需要使用累加器,代码如下
    => var sum = sc.accumulator(0)  // 改写废弃
    => // 中间部分不变
    => println(sum.value)
    
  • 累加器要放在行动算子里,放在转换算子可能因为多次使用该转换算子,造成重复累加;
  • 自定义行动算子
    • 以下举例 添加string的自定义累加器
    class CustomerAccu extends AccumulatorV2[String, java.util.Set[String]]{
    	var logStr = new util.HashSet[String]()
    	// 判断当前对象是否为空
    	override def isZero: Boolean = logStr.isEmpty
    	// 复制
    	override def copy(): AccumulatorV2[String, util.Set[String]] = {
    		val accu = new CustomerAccu
    		accu.logStr.addAll(logStr)
    		accu
    	}
    	// 重置对象
    	override def reset(): Unit = logStr.clear()
    	// 分区内添加数据
    	override def add(v: String): Unit = logStr.add(v)
    	// 合并
    	override def merge(other: AccumulatorV2[String, util.Set[String]]): Unit = logStr.addAll(other.value)
    	// 返回当前值
    	override def value: util.Set[String] = logStr
    }
    // 使用该累加器
    val rdd = sc.makeRDD(Array("abc", "def", "ghi", "jkl"))
    val accum = new CustomerAccu
    sc.register(accum, "CustomerAccu")  // 需要先注册
    rdd.map{
    	x =>
    	accu.add(x)
    	x
    }.collect()
    accu.value  // 内部调用copy、merge,输出上面完整Array
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值