Spark

398 阅读8分钟

mapreduce不适用迭代的计算:数据挖掘,机器学习

hadoop早期版本架构

mapreduce缺点

spark 和 yarn 结合

所以yarn非常重要

spark standalone模式

这种模式只要配置master和worker的节点各自在哪里即可

1.配置文件的路径 /opt/install/spark-standalone/conf
2.mv spark-env.sh.template spark-env.sh
3.vim spark-env.sh
      SPARK_MASTER_HOST=sunyjhost
      PARK_MASTER_PORT=7077
4. mv slaves.template slaves 
5. vim slaves
      sunyjhost
      sunyjhost2
      sunyjhost3
      
6. 分发配置
scp -r spark-standalone/ sunyjhost:/opt/install/

7.启动集群
./sbin/start-all.sh

8.提供了一个ui界面可以访问spark启动的页面
master:sunyjhost:8080
worker:sunyjhost:8081,sunyjhost2:8081,sunyjhost3:8081

webui

standalone模式下提交spark任务

[sunyj@sunyjhost spark-standalone]$ ./bin/spark-submit \
> --master spark://sunyjhost:7077 \
> --class org.apache.spark.examples.SparkPi \
> ./examples/jars/spark-examples_2.11-2.1.1.jar 1000


deploy-mode 可以为 cluster or client  代表driver运行的位置  cluster:driver运行在集群的节点上
[sunyj@sunyjhost spark-standalone]$ ./bin/spark-submit \
> --master spark://sunyjhost:7077 \
> --deploy-mode cluster  \
> --class org.apache.spark.examples.SparkPi \
> ./examples/jars/spark-examples_2.11-2.1.1.jar 1000

driverui:只有程序run的时候才能看到driverui

spark配置历史服务器

1.在hadoop上创建一个目录,用于保存spark运行日志信息
    hdfs dfs -mkdir -p /spark/historylog
2.   配置spark-defaults.conf ,使spark history server从此目录下读取信息
     mv spark-defaults.conf.template spark-defaults.conf
     vim spark-defaults.conf
              spark.eventLog.enabled           true      --开启日志
              spark.eventLog.dir               hdfs://mycluster/spark/historylog  --日志存储位置hdfs中
 3.配置spark-env.sh
     vim spark-env.sh
        export SPARK_HISTORY_OPTS="-Dspark.history.retainedApplications=2 -Dspark.history.fs.logDirectory=hdfs://mycluster/spark/historylog -Dspark.history.ui.port=18080"
        
export HADOOP_CONF_DIR=/opt/install/hadoop-2.7.2/etc/hadoop

4.分发配置

5.启动历史服务器
 ./sbin/start-history-server.sh
              

配置项: spark.history.retainedApplications spark.history.fs.logDirectory:配置了该属性后,在start-history-server.sh时就无需再显式的指定路径,Spark History Server页面只展示该指定路径下的信息 spark.history.ui.port:历史服务器端口

如果不暴露HADOOP_CONF_DIR的配置地址,会报如下错误

删除日志 但是我没有验证

spark HA

配置spark-env.sh即可 然后分发配置 ./sbin/start-master.sh 需要单独启动master集群模式

// 集群模式需要将master地址的配置注释掉
#SPARK_MASTER_HOST=sunyjhost
#PARK_MASTER_PORT=7077


export SPARK_HISTORY_OPTS="-Dspark.history.retainedApplications=2 -Dspark.history.fs.logDirectory=hdfs://mycluster/spark/historylog -Dspark.history.ui.port=18080"


export HADOOP_CONF_DIR=/opt/install/hadoop-2.7.2/etc/hadoop

配置zk集群模式 锁的节点  以及zk集群的地址
export SPARK_DAEMON_JAVA_OPTS="-Dspark.deploy.recoveryMode=ZOOKEEPER  -Dspark.deploy.zookeeper.url=sunyjhost:2181,sunyjhost2:2181,sunyjhost4:2181  -Dspark.deploy.zookeeper.dir=/spark"

spark on yarn

不需要额外的spark集群

配置: yarn-site.xml

<property>
  <name>yarn.nodemanager.pmem-check-enabled</name>
  <value>false</value>
</property>

<property>
  <name>yarn.nodemanager.vmem-check-enabled</name>
  <value>false</value>
</property>

在原来的yarn上增加了如上的配置,因为我的虚拟机内存少 所以将内存检查关闭,防止内存超出限制,将进程杀死, hadoop集群的配置就做这么点修改即可,然后分发配置

spark-env.sh

YARN_CONF_DIR=/opt/install/hadoop-2.7.2/etc/hadoop

因为我们要讲spark程序提交到yarn集群,所以要配置hadoop的环境

spark_yarn 历史配置

spark-defaults.conf

spark.eventLog.enabled           true
spark.eventLog.dir               hdfs://mycluster/spark/historylog-yarn

spark-env.sh

HADOOP_CONF_DIR=/opt/install/hadoop-2.7.2/etc/hadoop

export SPARK_HISTORY_OPTS="-Dspark.history.retainedApplications=2 -Dspark.history.fs.logDirectory=hdfs://mycluster/spark/historylog-yarn -Dspark.history.ui.port=18080"

为了从yarn上关联spark的历史服务器,需要增加如下配置 spark-defaults.conf

spark.yarn.historyServer.address=sunyjhost:18080
spark.history.ui.port=18080

提交任务

./bin/spark-submit --master yarn --class org.apache.spark.examples.SparkPi ./examples/jars/spark-examples_2.11-2.1.1.jar 1

配置yarn历史

yarn-site.xml

<property>
  <name>yarn.log.server.url</name>
  <value>http://sunyjhost2:19888/jobhistory/logs</value>
</property>

RDD

RDD 弹性分布式数据集,对这个数据集提供一些列算子(方法),对数据集进行处理。

算子:转换算子和行动算子

RDD创建的方式

从集合中创建: parallelize,makeRDD makeRDD底层调用的还是parallelize

 def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf().setMaster("local[2]").setAppName("sparkcoretest").set("spark.testing.memory", "2147480000")
    val sc: SparkContext = new SparkContext(conf)
    //创建一个集合 。数据保存在内存中
    val list: List[Int] = List(1, 2, 3, 4)
    
    //根据集合创建RDD方式1  parallelize
    val rdd: RDD[Int] = sc.parallelize(list)
    val result: Array[Int] = rdd.collect()
    result.foreach(println)
    sc.stop()
  }
def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf().setMaster("local[2]").setAppName("sparkcoretest").set("spark.testing.memory", "2147480000")
    val sc: SparkContext = new SparkContext(conf)
    //创建一个集合 。数据保存在内存中
    val list: List[Int] = List(1, 2, 3, 4)
    //根据集合创建RDD方式2  makeRDD
    val rdd: RDD[Int] = sc.makeRDD(list)
    val result: Array[Int] = rdd.collect()
    result.foreach(println)

    sc.stop()
  }

从外部文件创建RDD

def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf().setMaster("local[2]").setAppName("sparkcoretest").set("spark.testing.memory", "2147480000")
    val sc: SparkContext = new SparkContext(conf)

    // 读取每一行,将每一行内容“放到”RDD 读取本地文件系统
    val lineRDD:RDD[String] = sc.textFile("F:\\2020study\\spark-study\\in\\word.txt")

    lineRDD.collect().foreach(println)
    sc.stop()
  }

执行结果

读取hdfs文件创建RDD

def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf().setMaster("local[2]").setAppName("sparkcoretest").set("spark.testing.memory", "2147480000")
    val sc: SparkContext = new SparkContext(conf)
    sc.hadoopConfiguration.set("fs.defaultFS", "hdfs://mycluster")
    sc.hadoopConfiguration.set("dfs.nameservices", "mycluster")
    sc.hadoopConfiguration.set("dfs.ha.namenodes.mycluster", "nn1,nn2")
    sc.hadoopConfiguration.set("dfs.namenode.rpc-address.mycluster.nn1", "sunyjhost:8020")
    sc.hadoopConfiguration.set("dfs.namenode.rpc-address.mycluster.nn2", "sunyjhost2:8020")
    sc.hadoopConfiguration.set("dfs.client.failover.proxy.provider.mycluster", "org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider")

    // 读取每一行,将每一行内容“放到”RDD
    val lineRDD:RDD[String] = sc.textFile("hdfs://mycluster/wcinput")
    lineRDD.collect().foreach(println)
    
    sc.stop()
  }

RDD数据集 分区

在spark中切片数和分区数是一样的概念

切片:切片大小 偏移量 所在节点等信息

transformation 转换算子

RDD整体分为value型,双value型,key-value型

value类型

map算子:

  def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf().setMaster("local[2]").setAppName("map").set("spark.testing.memory", "2147480000")
    val sc: SparkContext = new SparkContext(conf)

    val rdd: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4), 2)

    // 对rdd进行映射 元数据乘2
    //val rdd2: RDD[Int] = rdd.map((a: Int) => a * 2)
    //简化写法
    val rdd2: RDD[Int] = rdd.map((a) => a * 2)
    val rdd3: RDD[Int] = rdd.map(a => a * 2)
    val rdd4: RDD[Int] = rdd.map(_ * 2)
  }

mapPartitions算子

  def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf().setMaster("local[2]").setAppName("map").set("spark.testing.memory", "2147480000")
    val sc: SparkContext = new SparkContext(conf)

    val rdd: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4), 2)
    println(rdd.partitions.length)

    val newRdd: RDD[Int] = rdd.mapPartitions((datas: Iterator[Int]) => datas.map(_ * 2))
    println(newRdd.partitions.length)

  }

mapPartitionsWithIndex 算子 带分区号

 def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf().setMaster("local[2]").setAppName("map").set("spark.testing.memory", "2147480000")
    val sc: SparkContext = new SparkContext(conf)

    val rdd: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4, 5, 6, 7, 8), 3)
    // index 分区编号
    val newRDD: RDD[(Int, Int)] = rdd.mapPartitionsWithIndex((index, datas) => {
      datas.map(data => (index, data))
    })
    //简化写法
    val newRDD2: RDD[(Int, Int)] = rdd.mapPartitionsWithIndex((index, datas) => {
      datas.map(data => (index, data))
    })
    //简化写法
    val newRDD3: RDD[(Int, Int)] = rdd.mapPartitionsWithIndex((index, datas) => {
      datas.map((index, _))
    })
    newRDD.collect().foreach(println)
  }

输出结果:

flatmap 算子

rdd 压平

def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf().setMaster("local[2]").setAppName("map").set("spark.testing.memory", "2147480000")
    val sc: SparkContext = new SparkContext(conf)

    // 集合中含有集合
    val rdd: RDD[List[Int]] = sc.makeRDD(List(List(1, 2), List(3, 4), List(5, 6), List(7)), 2)
    val newRdd: RDD[Int] = rdd.flatMap(datas => datas)
  }

glom 算子

将分区中的数据转化为数组

 def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf().setMaster("local[2]").setAppName("map").set("spark.testing.memory", "2147480000")
    val sc: SparkContext = new SparkContext(conf)

    val rdd: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4), 2)

    // glom 将rdd中的每个分区的元素 转化为数组 
    val arrayRdd: RDD[Array[Int]] = rdd.glom()

    //取出数组中的最大值
    val maxRdd: RDD[Int] = arrayRdd.map(_.max)

    println(maxRdd.collect().sum)
  }

group by

将rdd中的元素按照一定的规则进行分组,

 def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf().setMaster("local[2]").setAppName("map").set("spark.testing.memory", "2147480000")
    val sc: SparkContext = new SparkContext(conf)

    val rdd: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4 , 5, 6,7,8,9), 2)

    // 将 rdd中的元素按照一定的规则进行分组 分3组,对3进行取模%3
    //返回的rdd是一个元祖  第一个元素是组的key  第二个元素是在这个组的所有元素的集合
    // group by 接受一个函数作为参数,这个函数的返回结果作为分组的条件 
    val groupRdd: RDD[(Int, Iterable[Int])] = rdd.groupBy(_ % 3)
    
  }

filter 算子

def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf().setMaster("local[2]").setAppName("map").set("spark.testing.memory", "2147480000")
    val sc: SparkContext = new SparkContext(conf)

    val rdd: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4 , 5, 6, 7, 8, 9), 2)

    // 不简化
    val filterRDD1: RDD[Int] = rdd.filter(elem => elem % 2 != 0)

    val filterRDD: RDD[Int] = rdd.filter(_ % 2 != 0)

    filterRDD.collect().foreach(println)

  }

sample 算子 和takeSample 采样

distinct 去重算子

def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf().setMaster("local[2]").setAppName("map").set("spark.testing.memory", "2147480000")
    val sc: SparkContext = new SparkContext(conf)

    val rdd: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4 ,3, 6, 2, 5, 2, 0), 4)

    rdd.mapPartitionsWithIndex{
      (index,datas)=>{
        println(index + ":::" +datas.mkString(" "))
        datas
      }
    }.collect()
    println("去重后,重新指定分区")
    val distinctRDD: RDD[Int] = rdd.distinct(2)
    distinctRDD.mapPartitionsWithIndex{
      (index,datas)=>{
        println(index + ":::" +datas.mkString(" "))
        datas
      }
    }.collect()
  }


coalesce算子和repartition算子 重新分区

repartition底层调用的是 coalesce

sortby 排序算子

  def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf().setMaster("local[2]").setAppName("map").set("spark.testing.memory", "2147480000")
    val sc: SparkContext = new SparkContext(conf)

    val rdd: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4 ,3, 6, 2, 5, 2, 0), 4)

    val sortbyRDD: RDD[Int] = rdd.sortBy(num => num)

    sortbyRDD.collect().foreach(println)

    val sortbyRDD2: RDD[Int] = rdd.sortBy(num => num,false)

    sortbyRDD2.collect().foreach(println)
  }

双value类型

合集

差集

交集

拉链

拉链的前提

key-value类型

partitionBy

 def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf().setMaster("local[2]").setAppName("map").set("spark.testing.memory", "2147480000")
    val sc: SparkContext = new SparkContext(conf)

    val rdd: RDD[(Int, String)] = sc.makeRDD(List((1, "aaa"), (2, "bbb"), (3, "ccc"), (4, "ddd")), 3)

    val partionRDD: RDD[(Int, String)] = rdd.partitionBy(new HashPartitioner(2))

    partionRDD.mapPartitionsWithIndex{
      (index,datas)=>{
        println(index + ":::" +datas.mkString(" "))
        datas
      }
    }.collect()
  }

也可以自定义分区逻辑 上面使用的是hashPartitioner

class MyPartitioner(partitions:Int) extends  Partitioner{
  override def numPartitions: Int = partitions

  override def getPartition(key: Any): Int = {
    // 分区业务逻辑
    if (key.isInstanceOf[String]){
      val k: String = key.asInstanceOf[String]
      if (k.startsWith("135")){
          0
      }else if(k.startsWith("136")){
          1
      }else{
          2
      }
    } else {
       9
    }
  }
}

reduceByKey

groupByKey

aggregateByKey

foldByKey

combineByKey

四种聚合运算底层调用的是相同的算子

sortByKey

mapValues

只对value进行映射

join 算子

如果key 只是某一个RDD有 则不会关联上

注意调用顺序

cogroup 算子

WORDCOUT

方案1

def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf().setMaster("local[2]").setAppName("map").set("spark.testing.memory", "2147480000")
    val sc: SparkContext = new SparkContext(conf)

    val strList: List[String] = List("Hello Scala", "Hello Spark", "Hello World")

    val rdd: RDD[String] = sc.makeRDD(strList)


    val flatMapRdd: RDD[String] = rdd.flatMap(_.split(" "))

    val mapRdd: RDD[(String, Int)] = flatMapRdd.map((_, 1))

    //将相同的单词放到一组,所以按照元祖的第一个元素进行分组
    val groupByRDD: RDD[(String, Iterable[(String, Int)])] = mapRdd.groupBy(_._1)

    val finalRdd: RDD[(String, Int)] = groupByRDD.map {
      case (word, datas) => {
        (word, datas.size)
      }
    }

    finalRdd.collect().foreach(println)


  }

方案2

  def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf().setMaster("local[2]").setAppName("map").set("spark.testing.memory", "2147480000")
    val sc: SparkContext = new SparkContext(conf)

    val strList: List[String] = List("Hello Scala", "Hello Spark", "Hello World")

    val rdd: RDD[String] = sc.makeRDD(strList)


    val flatMapRdd: RDD[String] = rdd.flatMap(_.split(" "))

    val groupbyRDD: RDD[(String, Iterable[String])] = flatMapRdd.groupBy(word => word)

    val finalRdd: RDD[(String, Int)] = groupbyRDD.map {
      case (word, datas) => {
        (word, datas.size)
      }
    }
    finalRdd.collect().foreach(println)
  }

WORDCOUNT 复杂

 def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf().setMaster("local[2]").setAppName("map").set("spark.testing.memory", "2147480000")
    val sc: SparkContext = new SparkContext(conf)

    // RDD中的元素是元组
    val rdd: RDD[(String, Int)] = sc.makeRDD(List(("Hello Scala", 2), ("Hello Spark", 3), ("Hello World", 2)))

    // 方式1
    val mapRDD: RDD[String] = rdd.map(tup => {
      (tup._1 + " ") * tup._2
    })

    // 方式2 模式匹配
    rdd.map{
      case (words,count) =>{
        (words + " ") * count
      }
    }

    // 方案2  模式匹配的話就得使用{}  不能使用()
    // ("Hello Scala", 2) 轉化成(Hello,2) (Scala,2)
    val flatRDD: RDD[(String, Int)] = rdd.flatMap {
      case (words, count) => {
        words.split(" ").map((_, count))
      }
    }
    val groupbyADD: RDD[(String, Iterable[(String, Int)])] = flatRDD.groupBy(_._1)

    val finalRDD: RDD[(String, Int)] = groupbyADD.map {
      case (word, datas) => {
        (word, datas.map(_._2).sum)
      }
    }
  }

求学生的平均成绩

方案1

方案2

方案3

topN

模式匹配用的6

隐式转换用的6

行动算子

行动算子触发job执行

reduce()聚合

collect() 收集

foreach() 遍历

aggregate

fold

countByKey

save相关算子