SparkCore

SparkCore


========================================
MapReduce
  分布式计算框架
  缺点:
   -1.执行速度慢
      IO瓶颈:磁盘IO、网络IO
      shuffle的机制:数据需要输出到磁盘,而且每次都需要进行排序的操作
   -2. 框架的缺陷
      只有map和reduce两个操作算子,结构只能是:map -> reduce或者map -> [map ->]* -> reduce [-> map]*
      针对比较复杂的任务,需要构建多个job来执行,而当多个job有依赖关系的时候,会增加数据的读写、网络数据传输等方面的问题
   -3. Task是以进程的方式来启动,处理小规模数据集的时候速度就会比较慢
   
Spark:基于内存的分布式计算引擎/框架
   起源:加州大学伯克利分校AMPLlib实验室
   官网:http://spark.apache.org/
   官方博客:https://databricks.com/blog
   
   
====================================================
Spark编译
  http://spark.apache.org/docs/1.6.1/building-spark.html
  源码下载位置:
     http://spark.apache.org/downloads.html
     http://archive.apache.org/dist/spark/
  编译过程:

./make-distribution.sh --tgz \
-Phadoop-2.4 \
-Dhadoop.version=2.5.0-cdh5.3.6 \
-Pyarn \
-Phive -Phive-thriftserver


============================================   
Spark的运行模式(Spark应用在哪儿运行)
  local: 本地运行
  standalone:使用Spark自带一个资源管理框架,将spark应用可以提交到该资源管理框架上运行(分布式的)
  yarn:将spark应用提交到yarn上进行运行
  mesos:类似yarn的一种资源管理框架


============================================
local
  Spark local on linux(spark-shell命令)
  Spark Local的环境配置:
    -1. 安装好JDK、SCALA、Hadoop等依赖环境
    -2. 解压spark编译好的压缩包
      cd /opt/cdh-5.3.6
      tar -zxvf /opt/tools/workspace/spark/spark-1.6.1-cdh5.3.6/spark-1.6.1-bin-2.5.0-cdh5.3.6.tgz
    -3. 创建软连接
      cd /opt/cdh-5.3.6
      ln -s /opt/cdh-5.3.6/spark-1.6.1-bin-2.5.0-cdh5.3.6/ spark 
    -4. 进入spark根目录,修改conf中的配置文件
      cd spark
      mv conf/spark-env.sh.template conf/spark-env.sh
      vim spark-env.sh
JAVA_HOME=/opt/modules/java
SCALA_HOME=/opt/modules/scala
HADOOP_CONF_DIR=/opt/cdh-5.3.6/hadoop/etc/hadoop
SPARK_LOCAL_IP=bigdata-01.yushu.com
### 
  上面四个配置项中,除了HADOOP_CONF_DIR外,其它的必须给定;HADOOP_CONF_DIR给定的主要功能是:给定连接HDFS的相关参数(实际上本地运行的时候,只需要给定core-site.xml和hdfs-site.xml)
###
    -5. 测试linux上的本地环境
      启动HDFS
        
      ./bin/run-example SparkPi 10
      ./bin/spark-shell

    -6. 上传README.md到HDFS根目录
    -7. 测试
val textFile = sc.textFile("/README.md")
textFile.count()
textFile.first()
val linesWithSpark = textFile.filter(line => line.contains("Spark"))
linesWithSpark.count()

=======================================
Spark源码导入IDEA
  -1. 最好指定maven源为ali的maven源
  -2. 将《repository(Windows 开发环境依赖maven本地仓库).zip》解压到本地maven源,默认文件夹位置是:~/.m2


=======================================
WordCount程序


## 1. 读取hdfs上的数据形成RDD
val lines = sc.textFile("/user/yushu/spark/data/word.txt")
## 将RDD的lines对象看成list集合进行操作就可以求解出wordcont的值
lines.filter(_.nonEmpty).flatMap(_.split(" ")).filter(_.trim.nonEmpty).map(word => (word.trim, 1)).groupBy(_._1).map {
        case (word, iter) => {
          val sum = iter.map(_._2).sum
          (word, sum)
        }
      }.foreach(println)
## 考虑使用RDD的相关API进行WordCount开发
val words = lines.flatMap(line => line.split(" "))
val filteredWrods = words.filter(word => word.trim.nonEmpty)
val wordAndNums = filteredWrods.map(word => (word.trim, 1))
val result = wordAndNums.reduceByKey((a, b) => a + b)

## 结果保存hdfs(要求文件夹不存在)
result.saveAsTextFile("/yushu/spark/core/wordcount/01")
   
===============================================   
## 获取出现次数最多的前10个单词 ===> TopN的几种方式
result.sortBy(t => t._2 * -1).take(10)
result.sortBy(t => t._2, ascending = false).take(10) //降序排列
result.map(_.swap).sortByKey(ascending = false).map(_.swap).take(10)
result.map(_.swap).top(10)
result.top(10)(ord = new scala.math.Ordering[(String,Int)]{
  override def compare(x: (String,Int), y: (String,Int)): Int = {
     // 如果x>y, 返回1, x = y, 返回0, x < y, 返回-1
     x._2.compare(y._2)
  }
})


// 获取出现次数最少的前10个单词
result.top(10)(ord = new scala.math.Ordering[(String,Int)]{
  override def compare(x: (String,Int), y: (String,Int)): Int = {
     // 如果x>y, 返回1, x = y, 返回0, x < y, 返回-1
     y._2.compare(x._2)
  }
})


====================================================
Spark on Standalone
  将spark应用运行在standalone上
  Standalone是一个Spark自带的资源管理框架,功能类似yarn
  Yarn的框架:
    ResourceManager:管理集群的资源,包括:监控、申请...
    NondManager: 管理当前节点的资源以及启动container中的task任务
    资源:
      CPU&内存
  Standalone的框架:
    Master:集群资源管理,包括:监控、申请...
    Worker: 任务执行节点的资源管理,包括资源管理以及executor启动
    资源:
      CPU&内存
  Standalone的配置:
    -1. 前提要求:Spark的local本地模式可以成功运行
    -2. 修改${SPARK_HOME}/conf中的配置文件
      vim spark-env.sh
SPARK_MASTER_IP=bigdata-01.yushu.com
SPARK_MASTER_PORT=7070
SPARK_MASTER_WEBUI_PORT=8080
SPARK_WORKER_CORES=2  ## 一个worker服务中可以包含多少核的CPU,逻辑上的
SPARK_WORKER_MEMORY=2g  ## 一个worker服务中可以包含多少内存,逻辑上的
SPARK_WORKER_PORT=7071
SPARK_WORKER_WEBUI_PORT=8081
SPARK_WORKER_INSTANCES=2 ## 指定一台服务器可以启动多少个work服务

    mv slaves.template slaves
    vim slaves
bigdata-01.yushu.com ## 给定work服务所在机器的IP地址或者主机名,一行一个
    
    -3. 启动服务
       ./sbin/start-master.sh
       ./sbin/start-slaves.sh
       ===> ./sbin/start-all.sh
       (关闭服务使用: ./sbin/stop-all.sh)
    -4. 测试
       jps: 能够看到master和worker服务
       webui: http://bigdata-01.yushu.com:8080/
       spark-shell测试:
         bin/spark-shell --master spark://bigdata-01.yushu.com:7070
val lines = sc.textFile("/user/yushu/spark/data/word.txt")
val words = lines.flatMap(line => line.split(" "))
val filteredWrods = words.filter(word => word.trim.nonEmpty)
val wordAndNums = filteredWrods.map(word => (word.trim, 1))
val result = wordAndNums.reduceByKey((a, b) => a + b)
result.take(10)


=================================================
Spark Standalone分布式配置
  -1. 配置hadoop分布式环境前置条件均配置(eg:ssh、ip地址映射...)
  -2. 先配置一台成功的单节点的standalone机器
  -3. 将所有worker节点的ip地址或者主机名copy到slaves文件中
  -4. 将配置好的Spark环境copy到所有的worker服务所在节点上,并且保证在所有的节点上,spark的local模式均可以正常运行
  -5. 使用sbin/start-all.sh启动所有服务


=============================================
Spark Standalone Master(HA):
  http://spark.apache.org/docs/1.6.1/spark-standalone.html#high-availability
  -1. Single-Node Recovery with Local File System
    基于本地文件系统的Master恢复机制
    修改${SPARK_HOME}/conf/spark-env.sh
SPARK_DAEMON_JAVA_OPTS="-Dspark.deploy.recoveryMode=FILESYSTEM -Dspark.deploy.recoveryDirectory=/tmp"
  -2. Standby Masters with ZooKeeper
    基于zk提供热备(HA)的master机制
    修改${SPARK_HOME}/conf/spark-env.sh
SPARK_DAEMON_JAVA_OPTS="-Dspark.deploy.recoveryMode=ZOOKEEPER -Dspark.deploy.zookeeper.url=bigdata-01.yushu.com:2181,bigdata-01.yushu.com:2181,bigdata-01.yushu.com:2181 -Dspark.deploy.zookeeper.dir=/spark"


=============================================
应用监控
  -1. 运维人员会使用专门的运维工具进行监控,eg: zabbix等,(网络、磁盘、内存、cpu、进程等)
  -2. 使用CM(CDH)、Ambari(Apache、HDP)大数据专门的运维管理工具
  -3. 通过软件自带的web页面进行监控,eg: spark 8080、 hdfs 50070、mapreduce job history 19888....
  -4. 通过oozie等调度工具进行监控(当job执行失败的时候可以通知开发人员,通过email、短信)
  -5. Linux上的进程好像是可以自动恢复的(supervise方式启动)

===========================================
Spark应用监控:
  http://spark.apache.org/docs/1.6.1/monitoring.html
  -1. 针对正在运行的应用,可以通过webui来查看,默认端口号是4040,当4040被占用的时候,端口号往上递推.
  -2. 针对于已经执行完的job/应用,可以通过spark的job history server服务来查看

MapReduce Job History服务:
  -1. 开启日志聚集功能
  -2. 给定日志上传的hdfs文件夹
  -3. 启动mr的job history服务(读取hdfs上的文件内容,然后进行展示操作)

Spark Job History服务:
  -1. 创建HDFS上用于存储spark应用执行日志的文件夹
    hdfs dfs -mkdir -p /spark/history
  -2. 修改配置文件(开启日志聚集功能)
    mv spark-defaults.conf.template spark-defaults.conf
    vim spark-defaults.conf
spark.eventLog.enabled           true
spark.eventLog.dir               hdfs://bigdata-01.yushu.com:8020/spark/history
   -3. 测试spark的应用是否会将执行的日志数据上传到HDFS上
     ./bin/spark-shell
   -4. 配置spark的job history相关参数(指定从哪儿读取数据)
     vim spark-env.sh
SPARK_HISTORY_OPTS="-Dspark.history.fs.logDirectory=hdfs://bigdata-01.yushu.com:8020/spark/history"
   -5. 启动history 服务
      sbin/start-history-server.sh
      关闭:sbin/stop-history-server.sh
   -6. 测试
      访问webui: http://bigdata-01.yushu.com:18080/


Spark Job History Rest API:
   http://:18080/api/v1 
   eg:
http://bigdata-01.yushu.com:18080/api/v1/applicationshttp://bigdata-01.yushu.com:18080/api/v1/applications/local-1498985874725/logs 


===================WORDCOUNT案例:=================

def main(args: Array[String]) {
  val lines=List("hadoop , spark, hbase,hive" ,
    "hadoop,spark,hbase,hive",
    "",
    "spark,hive,spark,spark',hdfs,hadoop!")
  lines
    .filter(_.nonEmpty)
    .flatMap(_.split(","))
    .filter(_.nonEmpty)
    .map(word=>(word.trim.replaceAll("!","").replaceAll("'",""),1))
    .groupBy(_._1)
    .map{
      case (word,iter)=>{
        val sum=iter.map(_._2).sum
        (word,sum)
      }
  }
  .foreach(println)
}
===================PV&&UV统计========================

数据格式类似

2013-05-19 13:00:00 http://www.taobao.com/17/?tracker_u=1624169&type=1 B58W48U4WKZCJ5D1T3Z9ZY88RU7QA7B1   http://hao.360.cn/ 1.196.34.243   NULL   -1
 
  
def main(args: Array[String]) {
  val conf=new SparkConf()
  .setMaster("local[*]")
  .setAppName("pv_uv")
  val sc=new SparkContext(conf)
  val time=System.currentTimeMillis()
 val path="hdfs://bigdata-01.yushu.com:8020/user/yushu/spark/data/page_views.data"
  val rdd=sc.textFile(path)
  //公共的计算
  val mapredRdd=rdd.map(line=>{
    var a=line.split("\t")
    a
  })
    .filter(arr=> {
        var flag=arr.length>3&& arr(0).trim.length>10
         flag
    })
    .map(arr=>{
    (arr(0).trim.substring(0,10),arr(1).trim,arr(2).trim)
  })
  //pv的第一种计算方式
 val pvRdd1=mapredRdd
    .map(t=>(t._1,t._2))
    .groupByKey()
    .map{
    case(date,urls)=>{
      val pv=urls.size
      (date,pv)
    }
  }
 pvRdd1.saveAsTextFile(s"/user/yushu/spark/core/pv_uv/${time}/pv/1")
//PV的第二种计算方式
  val pvRdd2=mapredRdd
    .map(t=>(t._1,1))
    .reduceByKey(_+_)
  pvRdd2.saveAsTextFile(s"/user/yushu/spark/core/pv_uv/${time}/pv/2")
  //uv的计算
  val uvRdd=mapredRdd
    .map(t=>(t._1,t._3))
    .distinct()
    .map(t=>(t._1,1))
    .reduceByKey(_+_)

  uvRdd.saveAsTextFile(s"/user/yushu/spark/core/pv_uv/${time}/uv")

  sc.stop()
}
===================分组排序TOPN案例========================
数据格式:
aa 78
bb 98
aa 80
cc 98
aa 69
cc 87
bb 97
cc 86
aa 97
bb 78
bb 34
cc 85
bb 92
cc 72
bb 32
bb 23
def main(args: Array[String]) {
  val conf=new SparkConf()
      .setMaster("local[*]")
      .setAppName("GroupSortedTopN2")
  val sc=new SparkContext(conf)
  val rdd=sc.textFile("datas/groupsort.txt")
  val result1=rdd.map(line=>{line.split(" ")})
      .filter(arr=>arr.length==2)
      .map(arr=>(arr(0).trim,arr(1).trim.toInt))
      .groupByKey()
      .map(t=>{
        val key=t._1
        val values=t._2.toList.sorted.takeRight(3).reverse
        (key,values)
      })
  result1.foreachPartition(iter=>{
    iter.foreach(println)
  })

  println("re3------第二种方式:两阶段聚合------")
  val result3=rdd
    .map(line=>line.split(" "))
    .filter(arr=>arr.length==2)
    .mapPartitions(iter=>{
    val random=Random
    iter.map(arr=>((random.nextInt(3),arr(0).trim),arr(1).trim.toInt))
  })
    .groupByKey()
   .flatMap{
    case((_,key),iter) =>{
      val r=iter.toList.sorted.takeRight(3)
      r.map(v=>(key,v))
    }
  }
   .groupByKey()
    .map{
    case(key,iter)=>{
      val r=iter.toList.sorted.takeRight(3).reverse
      (key,r)
    }
  }
  result3.foreachPartition(iter=>iter.foreach(println))
}
================================================
 RDD的几个API的应用:
map:数据转换,给定的f返回的数据类型是啥,最终rdd中的数据类型就是啥
flatMap: 数据转换 + 扁平化操作,给定的f返回的集合中的数据类型是啥,最终rdd中的数据类型就是啥
filter:数据过滤,对于给定函数f返回为false的数据进行过滤,保留返回结果为true的数据
reduceByKey: 对于key/value的数据进行按照key进行数据分组,然后对每组数据进行给定函数的聚合操作
====================Linux上执行spark的jar文件==================

  http://spark.apache.org/docs/1.6.1/submitting-applications.html
通过bin/spark-submit命令进行任务的提交操作 代码打包的jar文件要先上传上Linux上
命令:
./bin/spark-submit \
  --class com.yushu.bigdata.spark.app.core.LogPvAndUvCountSpark \
  --master local[*] \
  /home/yushu/logs-analyzer.jar 


./bin/spark-submit \
  --class com.yushu.bigdata.spark.app.core.LogPvAndUvCountSpark \
  --master spark://bigdata-01.yushu.com.com:7070 \
  /home/yushu/logs-analyzer.jar 


./bin/spark-submit \
  --class com.yushu.bigdata.spark.app.core.LogPvAndUvCountSpark \
  --master spark://bigdata-01.yushu.com:7070 \
  --deploy-mode cluster \
  /home/yushu/logs-analyzer.jar


### 当提交访问为standalone+cluster的时候,最好选择rest url
./bin/spark-submit \
  --class com.yushu.bigdata.spark.app.core.LogPvAndUvCountSpark \
  --master spark://bigdata-01.yushu.com:6066 \
  --deploy-mode cluster \
  /home/yushu/logs-analyzer.jar


./bin/spark-submit \
  --class com.yushu.bigdata.spark.app.core.LogPvAndUvCountSpark \
  --master spark://bigdata-01.yushu.com:6066 \
  --deploy-mode cluster \
  --executor-cores 1 \
  --total-executor-cores 2 \
  --driver-cores 1\
  --executor-memory 1g \
  --driver-memory 512m \
  /home/yushu/logs-analyzer.jar  
=============================================================
=============================================================
Windows上执行spark的应用
  异常信息
    -1. Exception in thread "main" org.apache.spark.SparkException: A master URL must be set in your configuration
      解决方案:在SparkkConf中指定master的信息
    -2. Exception in thread "main" org.apache.spark.SparkException: An application name must be set in your configuration
      解决方案:在SparkConf中指定应用名称的信息
    -3. null/bin/winutil.exe
      原因:windows上没有配置HADOOP环境导致的
      解决方案:将hadoop的安装包解药到本机上的任意路径,路径要求不能有空格,不能包含中文;然后将winutils放入hadoop根目录的bin文件夹中,最后配置HADOOP_HOME环境变量
        操作完后,重启IDEA,如果重启IDEA无效,重启机器
    -4. (windows上可能出现)有可能遇到其它异常,NullPointerException, 这种异常有可能需要修改源码(Hadoop源码) 
    
================================================================
================================================================
Spark应用的参数配置
  可以在三个地方进行配置,分别的优先级如下:
    1: ${SPARK_HOME}/conf/spark-defaults.conf
    2: bin/spark-submit 参数
    3:代码中通过SparkConf来给定
   优先级:3 > 2 > 1
   
      
==================================================================
==================================================================
Spark应用的结构
  Driver + Executors
    Driver:运行SparkContext上下文的地方,进行RDD初始化的地方,资源申请、RDD Job调度地方(JVM中)
        其实就是main函数运行的地方
    Executor:运行具体Task的地方,一个Executor中可以并行的运行多个Task任务
  一个Application包含多个Job
     一个Job包含多个Stage
       一个Stage包含多个Task
         Task是最小运行单位,是运行在executor中的线程


deploy-mode: 指定driver在哪儿运行
  client:默认,driver运行的机器就是执行spark-submit脚本命令的机器
  cluster:在集群中任选一台机器作为节点来执行driver进程


===================Spark的资源调优=========================
  主要来讲:调整CPU、内存、executor的数量;通过spark的参数来调整的
  http://spark.apache.org/docs/1.6.1/configuration.html
  spark-submit的脚步参数资源调优:(相关参数)
--master:给定spark应用运行在哪个地方(eg: local、standalone、yarn、mesos)
--deploy-mode:指定driver在哪儿运行(cluster、client)
--class:指定spark应用的类全称是那个(包名 + 类名),如果该参数不给定,那么应用默认是jar文件中默认的main class
--conf PROP=VALUE: 给定spark应用的参数信息,eg: --conf "spark.ui.port=5050"
--properties-file: 给定应用读取spark的配置信息,默认是conf/spark-defaults.conf
--driver-memory:给定driver的内存,默认1024M
--executor-memory:给定每个executor的内存,默认1024M
--proxy-user:给定运行spark应用的代理用户名称,默认为空
standalone + cluster:
  --driver-cores:指定driver的数量,默认1
standalone/mesos + cluster:
  --supervise:指定当driver宕机的时候,会自动进行恢复操作
standalone/mesos:
  --total-executor-cores NUM: 指定所有的executor的总core的数量,默认情况下是所有core的数量
standalone/yarn:
  --executor-cores NUM: 给定每个executor的core的数量,如果是yarn上,默认值为;standalone上默认值为all
yarn:
  --driver-cores NUM: 指定cluster的情况下,driver需要的core数量,默认为1
  --queue QUEUE_NAME:指定提交到yanr上的时候,提交到那个队列,默认是default
  --num-executors NUM:指定executor的数量,默认2个
  
==========================================================
==========================================================
Spark的内存管理模型(Executor的内存管理)
  老模型和新模型比较而言,老模型的资源没有动态分配而已,有些情况下,存在资源利用不充分的问题
  spark1.6之前的版本
    storage:
      指定的是数据缓存所应用到的内存,比如: rdd.cache、rdd.persist、广播变量、共享变量等
      由参数:spark.storage.memoryFraction(0.6)控制,默认表示60%的executor的内存用于storage
    shuffle:
      指定的是在shuffle过程中应用到的内存大小,比如:上一个阶段的数据会先写入内存、shuffle的数据排序等
      由参数:spark.shuffle.memoryFraction(0.2)控制,默认表示20%的executor的内存用于shuffle过程
    user:
      代码中用到的集合、对象等应用到内存,默认占比是(0.2): 
        1 - spark.storage.memoryFraction - spark.shuffle.memoryFraction
  spark1.6+版本  
    使用老的内存管理模型(1.6之前的):
       spark.memory.useLegacyMode:将该参数设置为true即可,默认为false
    使用新的内存模型(1.6+)<内存自动分配模型>:
       Reserved Memory:
         固定内存部分,主要是spark框架使用,默认大小:300M,不可修改
       storage&shuffle Memory:
         大小由参数:spark.memory.fraction(默认0.75)
	 由两部分构造:storage memory 和 execution memory(shuffle memory)
	 动态调整的含义:
	   当storage部分的内存不够的情况下会自动从execution中获取内存,返之也行
	 参数:spark.memory.storageFraction(默认0.5),指定storage部分最少内存的占比
	 动态调整过程:
	   -a. 如果storage memory满了,execution有多的内存
	       Storage的操作就会将数据存储到Execution内存部分中
	   -b. 如果Execution Mmemory满了,同时之前Storage占用了execution部分的内存
	       将内存中storage部分进行删除,空出内存存储execution部分操作所产生的数据;但是Storage部分内存至少会保留spark.memory.storageFraction给定大小的内存
	   
       User Mempry:
         用户代码使用到的内存,大小是:1 - spark.memory.fraction(0.25)
     eg: 默认情况下一个executor的内存是1024M,使用新的模型来讲:
       Reserved:300M
       User: (1024 - 300) * (1 - 0.75) = 181M
       storage&shuffle:
          (1024 - 300) * 0.75 = 534M
	  最少storage占比:
	    534 * 0.5 = 267M
	  ====> 动态可分配大小为(534 - 267) = 267M


=======================================================
=======================================================
Spark的动态分配
  指的根据业务的需要/应用的情况动态的指定executor的数量
  应用场景:streaming或者一致运行的spark程序适应访问高峰和低谷不同情况下的资源利用的问题
  相关参数:(一般需要给定四个参数)
    http://spark.apache.org/docs/1.6.1/configuration.html#dynamic-allocation
    spark.dynamicAllocation.enabled:开启动态资源分配,默认false
    spark.dynamicAllocation.initialExecutors:初始化的时候,初始化多少个executor的数量,默认是spark.dynamicAllocation.minExecutors的参数值
    spark.dynamicAllocation.minExecutors:给定应用中保持的最小exeuctor的数量,默认0
    spark.dynamicAllocation.maxExecutors:给定该应用最多允许持有的executor的数量,默认无,需要给定
    
====================================================
====================================================
Spark on yarn
  http://spark.apache.org/docs/1.6.1/running-on-yarn.html
  前提:
    1. yarn的配置信息(yarn-site.xml)在spark的classpath中 
       ==> 配置HADOOP_CONF_DIR在spark-env.sh中<配置本地运行环境>
    2. 启动yarn
  
 
./bin/spark-submit \
     --class com.yushu.bigdata.spark.app.core.LogPvAndUvCountSpark \
    --master yarn \
    --deploy-mode cluster \
    --driver-memory 1g \
    --executor-memory 1g \
    --executor-cores 1 \
    --num-executors 3 \
    /home/yushu/logs-analyzer.jar  


./bin/spark-submit \
     --class com.yushu.bigdata.spark.app.core.LogPvAndUvCountSpark \
    --master yarn \
    --deploy-mode client \
    --driver-memory 1g \
    --executor-memory 1g \
    --executor-cores 1 \
    --num-executors 3 \
    /home/yushu/logs-analyzer.jar  


==============================================================
==============================================================
Spark应用的构建及提交流程:
  -1. Driver中构建RDD的DAG图
  -2. RDD的job被触发(需要将具体的rdd执行过程提交到executor上执行,此时可以从4040页面看到执行的内容)
  -3. Driver中的DAGScheduler将RDD划分为不同的Stage阶段
  -4. Driver中的TaskScheduler将一个一个的stage对应的task提交到executor上执行
Spark应用通过spark-submit命令提交的执行过程:
  -1. client向资源管理器申请资源(Yarn、Standalone等), 运行driver程序;如果是client模式,driver的运行资源不需要申请
    -a. 当运行模式为spark on yarn cluster的情况下,此时driver和ApplicationMaster功能合并
    -b. 当运行模式为spark on yarn client的情况下,driver不需要申请资源,此时申请ApplicationMaster运行的资源
       driver:DAG的构建、Job的调度
       ApplicationMaster: 负责executors的资源申请与管理
  -2. 启动Driver(ApplicationMaster)
  -3. Driver向资源管理器申请executors的资源
    -a. 一台机器上可以运行多个executor ==> 一个NodeManager上可以运行多个executor
  -4. executors执行启动
  -5. RDD的构建
  -6. RDD对应Job的执行
  
==================================================

==================================================
spark on yarn job history配置
  http://spark.apache.org/docs/1.6.1/running-on-yarn.html ===> Debugging your Application
  -1. 配置并启动spark的job history相关信息
    http://bigdata-01.yushu.com:18080/
  -2. 配置yarn-site.xml文件,然后重启yarn

  yarn.log-aggregation-enable
  true


  yarn.log.server.url
  http://bigdata-01.yushu.com:19888/jobhistory/job/

  -3. 修改spark-defaults.conf内容
spark.yarn.historyServer.address http://bigdata-01.yushu.com:18080


===============================================================

RDD
  Resilient Distributed Datasets ==> 弹性分布式数据集
    Resilient:可以存在给定不同数目的分区、数据的缓存的时候可以缓存一部分分区的数据也可以缓存整个rdd的数据、分区的数量可以由业务来定
    Distributed:分区的数据可以分布在不同的节点上,并且在不同的executor上执行
    Dataset:内部存储的数据类似是一个集合数据
  RDD中的数据是不可变的、是分区存在;也就是说每次调用一个api对数据进行操作的时候,实际上是产生一个新的RDD
  
RDD的底层构建原理:
  -1. RDD的分区: 
     其实是一个逻辑结构,只是一个映射到数据源的结构(怎么读取数据)
     分区的数量由:InputFormat的getSplit方法返回集合中的split数量决定,一个split就是一个分区
     RDD中其实存储的就是分区的数据,也就是存储的各个分区的位置信息(怎么读数据)
      ===> RDD中其实是不包含数据的

RDD的构建
  -1. RDD的构建底层调用的是MapReduce的InputFormat
  -2. sc.textFile模式使用TextInputFormat读取数据
  -3. 默认情况下SparkContext中的API,在读取数据的时候使用的是MapReduce的旧API


RDD的创建
   -1. 外部数据(非内存数据): 基于MapReduce的InputFormat进行rdd的创建
     sc.textFile ==> 底层使用TextInputFormat读取数据形成RDD,内部使用旧MR的API
     sc.newAPIHadoopFile ==> 底层使用TextInputFormat读取数据形成RDD,内部使用新的MR的API
     sc.newAPIHadoopRDD ==> API明确指定使用哪个InputFormat读取数据
   -2. 内存中数据:直接将集合中的数据转换为RDD
     val seq = List(1,3,5,6,7,9)
     val rdd = sc.parallelize(seq)


RDD的方法类型(API类型)
  -1. transformation(transformation算子):转换操作
     功能:由一个RDD产生一个新的RDD的API,不会触发job的执行
     在这类API调用的过程中,实质是在driver中构建了一个RDD的依赖图,也称为构建RDD的执行逻辑图(DAG图)
     是一个lazy的过程
  -2. action(action算子):动作/操作
     功能:触发rdd的job提交过程,并将rdd对应的job的dag图进行划分后提交到executor上执行
     该类型API的调用,会触发job的执行,执行结果会输出到其它文件系统中或者返回给driver中
  -3. pesist:(RDD的缓存/RDD的持久化)
     功能:将rdd的数据缓存到指定级别的存储系统中,或者将缓存的rdd进行清除缓存操作
     rdd.cache() 数据缓存到内存中
     rdd.persist(xx) 将rdd的数据缓存到指定级别的存储系统中(内存、磁盘)
     rdd.unpersist() 清除该rdd的缓存
     注意:rdd的缓存是一个lazy的过程,清空缓存操作是一个立即执行的操作

RDD的输出输出内部其实调用的是:MapReduce的OutputFormat类

=================================================================

=================================================================
Spark优化
  -1. 代码优化
    -a. 如果一个RDD只使用一次,可以不进行赋值操作,直接调用后续的API ===> 链式编程
    -b. 如果一个RDD被多次使用,记住一定要cache
    -c. 对于重复操作的API,将其提出并形成一个新的RDD,并将RDD进行缓存
    -d. 有限选择reduceByKey和aggregateByKey的API,尽量减少使用groupByKey(groupByKey容易出现OOM和数据倾斜的问题)
    -e. 尽量少用可能会产生shuffle的API
    -f. 优先使用foreachPartition而不是用foreach API
  -2. 资源优化
  -3. 数据倾斜优化
    导致原因:数据重复分配不均匀导致的,可能会导致某些task执行时间过长或者OOM异常
      -a. 更改分区的策略(增加分区的数量 + 根据数据定制开发Partitioner)
      -b. 两阶段聚合

===================================================================

===================================================================
RDD依赖
  窄依赖
    子RDD的每个分区的数据来自常数个父RDD的分区;父RDD的每个分区的数据到子RDD的时候一定在一个分区中
    常用方法:map、flatMap、filter、mapPartitions、union、join(要求两个父RDD具有相同的partitioner,同时两个父RDD的分区数量一致,并且子RDD的分区数量和父RDD的分区数量一致) 
  宽依赖:
    子RDD的每个分区的数据来自父RDD的所有分区;父RDD的每个分区的数据到子RDD的时候都有可能分配到任意一个子分区中
    常用方法:xxxxByKey、reparation、groupBy、sortBy、join等
    
==================================================================

==================================================================
Spark应用的结构
  Driver + Executors
    Driver:运行SparkContext上下文的地方,进行RDD初始化的地方,资源申请、RDD Job调度地方(JVM中)
        其实就是main函数运行的地方
    Executor:运行具体Task的地方,一个Executor中可以并行的运行多个Task任务
  一个Application包含多个Job
     一个Job包含多个Stage
       一个Stage包含多个Task
         Task是最小运行单位,是运行在executor中的线程
    
Stage的划分规则:
  当RDD的DAG图进行提交的时候,会由DAGScheduler对DAG进行stage的划分,划分规则如下:
    从后往前进行推断,如果遇到一个宽依赖,就形成一个stage,直到rdd的开头
    最后一个Stage叫做ResultStage,其它的Stage叫做ShuffleMapStage
Task:
  是Executor中执行的最小单位
  task实质上就是分区,一个分区的数据执行代码就是一个Task
    分区:从数据的分布情况来讲
    task:从数据的执行逻辑情况来讲
  一个Stage中的每个Task的执行逻辑(代码)是一样的,只是处理的数据不同而已,task的代码逻辑其实就是对应stage中的API组成的一个执行链
  
====================================================================

====================================================================
Spark Shuffle机制
  Spark的shuffle只存在宽依赖中,也就是说两个stage之间一定存在这儿shuffle过程;stage内部的执行时全部在内存中完成的,不需要考虑写磁盘、不同节点的数据流通
  由Spark Shuffle Manager进行管理,参数:spark.shuffle.manager:sort
   http://spark.apache.org/docs/1.6.1/configuration.html#shuffle-behavior
  Shuffle优化:
    -1. spark.shuffle.manager:sort
       当task的数量小于200的时候,会自动启用by_pass模式(没有数据排序的操作)
       spark.shuffle.sort.bypassMergeThreshold:200
    -2. spark.shuffle.manager:hash
       当应用中不需要数据排序的情况下,可以考虑使用hash shuffle manager
       当分区数量比较多的时候(超过100), 需要开启文件聚合功能:
          参数: spark.shuffle.consolidateFiles:默认为false,需要设置为true

==================================================================

==================Spark的调度==================================
Spark Schdeduler
  http://spark.apache.org/docs/1.6.1/configuration.html#scheduling
  http://spark.apache.org/docs/1.6.1/job-scheduling.html
  Spark的Job调度分为FIFO(先进先出, 默认)和FAIR(公平调度)
    FIFO: 按照job的提交时间来执行
    FAIR:并行的执行job任务
  相关参数:
    spark.scheduler.mode:FIFO, 给定调度的模式
    spark.speculation:是否开启spark的推测执行,默认为false,不开启
    spark.task.cpus:1,给定一个task允许需要多少core
    spark.task.maxFailures:4,给定一个task最多允许失败次数

==================Spark的优化之两阶段聚合====================================

适用场景:对RDD进行分组操作的时候,某些Task处理数据过多或者产生OOM异常等情况

实现思路:第一阶段给每个key加一个随机数,然后进行局部的聚合操作;第二阶段去除每个key的前缀,然后进行全局的聚合操作
实现原理:将key添加随机前缀的方式可以让一个key变成多个key,可以让原本被一个task处理的数据分布到多个task上去进行局部的聚合,进而解决单个task处理数据太多的问题;随后去掉前缀,进行全局集合,完成功能的实现


优缺点:对于聚合类shuffle操作(groupByKey、reduceByKey等)产生的问题能够很好的解决;但是对于非聚合类shuffle操作( join等)产生的问题很难使用该方式解决

==================================================
对于Driver中的数据需要在Executor中执行的,要求数据必须是可以序列化的,实现了java.io.Serializable;或者使用SerializableWritable将hadoop的writable对象转换为可序列化对象
注意:一般情况下,如果存在调用RDD相关API将executor上的数据获取到driver中,那么要求driver内存是单个executor的1-1.5倍
共享变量
  http://spark.apache.org/docs/1.6.1/programming-guide.html#shared-variables
  
  广播变量(broadcast variables)
    http://spark.apache.org/docs/1.6.1/programming-guide.html#broadcast-variables
     功能:减少driver到executor的数据传输量,将原来每个task拥有一个副本的方式改成一个executor拥有一个数据副本,一个executor中的所有task共享一个数据副本
注意:
   1. 广播变量是不允许被修改的,只读的
2. 广播变量在executor中存储在storage内存区
3. 当task执行的过程中发现对应的广播变量不存在了,会自动从driver中重新获取
4. 一般,如果一个广播变量不会使用了,会进行一个清空操作
  累加器(Accumulators)
    http://spark.apache.org/docs/1.6.1/programming-guide.html#accumulators
功能:类似MapReduce执行过程中的元数据累加操作;累加器在executor中是进行数据累加操作,在driver中进行数据的读取操作(execturo中的数据读取结果不保证<不同executor上结果读取结果不同>)
实现原理:
 -1. 每个executor会分别维护一个累加器
 -2. 当所有executor执行完成后,所有的累加器会合并形成一个新的累加器
 -3. driver中使用的累加器就是最终形成的新累加器

========================广播变量案例=====================

def main(args: Array[String]) {
  val conf=new SparkConf()
    .setMaster("local[*]")
    .setAppName("BVD")
  val sc=new SparkContext(conf)
  val rdd = sc.parallelize(Array(
    "hadoop, yushu' spark! hadoop",
    "hadoop; spark. spark hbase!!",
    "hadoop, spark hbase hive?",
    "hadoop hive spark hive"
  ))

  val list = Array(",", "\\.", ";", "!", "\\?", "'")
  val broadcast=sc.broadcast(list)
  rdd
    .flatMap(
      _.split(" ")
      .map( w=>broadcast.value.foldLeft(w)(
        (a,b)=>{
          a.replaceAll(b,"")}
          )
        )
    )
    .map((_,1))
    .reduceByKey(_+_)
    .foreachPartition(iter=>iter.foreach(println))

}
======================== SparkCore总结=========================================

  Spark的四种运行方式
    local
standalone
yarn
mesos
  Spark应用的监控
    4040 web ui 
job history server web ui
job history server rest api
  Spark的应用组成结构
    Driver + Executors
Application -> Jobs -> Stages -> Tasks(Partition)
  Driver的两种运行模式
    client
cluster
  What is RDD??
  RDD的三大类API
  RDD的创建、运行以及结果输出的原理/方式 ==>
     创建是由InputFormat来做
运行是构建DAG执行图
结果输出是由OutputFormat来做/结果输出可以使用foreachPartition来实现
  RDD的依赖 ==> lineage
     宽依赖
  存在shuffle过程
窄依赖
  全部在内存中计算
  Spark Shuffle
     sort:默认机制,基于数据排序的shuffle
hash:基于数据hash的shuffle
  Spark scheduler:
     FIFO和FAIR
  Spark的内存的管理机制+动态资源分配(分配executor)
  Spark Job提交的执行流程(结合wordCount来介绍)
  spark的优化:
  代码案例:
    -1. wordcount
-2. topN
-3. 分组排序TopN(类似MR的二次排序)
  额外:
    -a. RDD相关API的熟悉




你可能感兴趣的:(大数据)