spark
     UC Berkeley AMP lab所开源的类Hadoop MapReduce的通用的并行计算框架。
     1、Spark Streaming:支持高吞吐量、支持容错的实时流数据处理
     2、Spark SQL, Data frames: 结构化数据查询
     3、MLLib:Spark 生态系统里用来解决大数据机器学习问题的模块
     4、GraphX是构建于Spark上的图计算模型
     
 Spark的运行模式
     单机上以本地模式local运行、或以伪分布式模式运行
     分布式的方式运行在Cluster集群中时,底层的资源调度可以使用Mesos 或者Yarn,也可以使用Spark自带的Standalone模式。
     另外一种分法:master有local(调试方便)、yarn-client(普遍)、yarn-cluster、spark、mesos模式
     Spark Standalone:需要部署Spark到相关节点,包括Master和Worker
     Yarn-Cluster:Driver和Executor都运行在Yarn集群中
     Yarn-Client:Driver运行在本地,Executor运行在Yarn集群中基本概念
 (1)    Application:用户编写的Spark应用程序,其中包含Driver功能的代码和分布在集群中多个节点上运行的Executor代码。
 (2)    Driver:运行Application的main()函数并且创建SparkContext,创建SparkContext的目的是为了准备Spark应用程序的运行环境。
     SparkContext负责和ClusterManager通信,进行资源的申请、任务的分配和监控等。
     当Executor部分运行完毕后,Driver同时负责将SparkContext关闭。通常用SparkContext代表Driver。
 (3)Executor: 某个Application运行在Worker节点上的一个进程,该进程负责运行某些Task,并且负责将数据存在内存或者磁盘上。
     每个Application都有各自独立的一批Executor。
 (4)Cluster Manager:指的是在集群上获取资源的外部服务,目前有:
     Standalone:Spark原生的资源管理,由Master负责资源的分配
     Apache Mesos:与Hadoop MapReduce兼容性良好的一种资源调度框架;
     Hadoop Yarn:主要是指的Yarn中的ResourceManager;
 (5)Worker:集群中任何可以运行Application代码的节点
     在Standalone模式中指的就是通过slave文件配置的Worker节点,在Spark on Yarn模式中指的就是NodeManager节点。
 (6)Job:包含多个Task组成的并行计算,往往由Spark Action触发产生。一个Application中可能会产生多个Job。
 (7)Stage:Job被拆分很多组Task,作为一个TaskSet,其名称为Stage。
     Stage的划分和调度由下面的DAGScheduler负责。
     Stage有非最终的Stage即Shuffle Map Stage和最终的Stage即Result Stage两种。
     Stage的边界就是发生Shuffle的地方。
 (8)Task:被送到某个Executor上的工作单元,是运行Application的基本单位。
     多个Task组成一个Stage,而Task的调度及管理等由下面的TaskScheduler负责。
     一个Job被拆分成若干个Stage,每个Stage执行一些计算,产生一些中间结果。
     它们的目的是最终生成这个Job的计算结果。
     而每个Stage是一个task set,包含若干个task。Task是Spark中最小的工作单元,在一个executor上完成一个特定的事情。    
 (9)RDD:Spark的基本计算单元,可以通过一系列算子进行操作(主要有Transformation 延迟执行 和Action操作)。
     它表示已被分区、被序列化的、不可变的、有容错机制的并且能够被并行操作的数据集合。
     A list of partitions
     A function for computing each split
     A list of dependencies on other RDDs
     其存储级别可以是内存,也可以是磁盘,可通过spark.storage.StorageLevel属性配置。
     1、直接从集合转化 sc.parallelize(List(1,2,3,4,5,6,7,8,9,10))
     2、从各种(分布式)文件系统来 sc.textFile(“README.md”) sc.textFile(“hdfs://xxx”)
     3、从现存的任何Hadoop InputFormat而来 sc.hadoopFile(keyClass, valClass, inputFormat,conf)
 (10)共享变量:在Spark Application运行时,可能需要共享一些变量,提供Task或Driver等使用。
     Spark提供了两种共享变量,一种是可以缓存到各个节点的广播变量,一种是只支持加法操作、可以实现求和的累加变量。
 (11)宽依赖:ShuffleDependency,依赖需要计算好所有父RDD对应分区的数据,然后在节点之间进行Shuffle。
     指父RDD的每个分区都可能被多个子RDD分区所使用,子RDD分区通常对应所有的父RDD分区(O(n),与数据规模有关)
 (12)窄依赖:RDD其分区最多被子RDD中的一个分区依赖。此种情况只有Map任务,是不需要发生Shuffle过程的。窄依赖又分为1:1和N:1两种。
 (13)DAGScheduler:根据Job构建基于Stage的DAG,并提交Stage给TaskScheduler。其划分Stage的依据是根据RDD之间的依赖关系。
     1、为每个Job分割stage,同时会决定最佳路径,并且DAG Sheduler会记录哪个RDD或者stage的输出被物化,从而来找到一个最优调度方案。
     2、将TaskSet传给TaskScheduler3、重新提交那些输出lost的stage
 (14)TaskScheduler:将Taskset提交给Worker(集群)运行,每个Executor运行什么Task就是在此处分配的。
     1、提交tasks到集群并执行,假如出错就重试。
     2、假如shuffle输出lost就报告fetch failed错误 3、遇到straggle task需要放到别的node上重试spark的2个优点:
 内存计算(Job中间输出和结果可以保存在内存中,从而不再需要读写HDFS)   
 DAGSpark Streaming的核心思想是把流式处理转化为“微批处理”,即以"时间"为单位切分数据流,每个切片内的数据对应一个RDD,
 最小抽象单位是Discretized Stream,常用操作foreachRDD
 代码例子 object NetworkWordCount {  
     def main(args: Array[String]) {  
         StreamingExamples.setStreamingLogLevels()  
         val conf = new SparkConf().setMaster("local[2]").setAppName("NetworkWordCount")
         val ssc = new StreamingContext(conf, Seconds(1))   
         val lines = ssc.socketTextStream("localhost", 9999)  
         val words = lines.flatMap(_.split(" "))  
         val wordCounts = words.map(x => (x, 1)).reduceByKey(_ + _)  
         wordCounts.print()  
         ssc.start()  
         ssc.awaitTermination()  transformation 返回值还是一个RDD
     窄依赖:
     map  List("dog", "salmon", "salmon", "rat", "elephant").map(_.length)
     flatMap List(1, 2, 3), 2).flatMap(x => List(x, x, x))  = Array(1, 1, 1, 2, 2, 2, 3, 3, 3)
     mapPartitions 对rdd中的每个分区的迭代器进行操作 while(x.hasNext){ 操作运算x.next()...
     mapPartitionsWithIndex
     
     filter  (1 to 10).filter(_ % 2 == 0)
     
     union 将两个RDD进行合并,不去重 等价操作符++ (sc.parallelize(datas1) ++ sc.parallelize(datas2)).foreach(println)
     intersection 取交集 sc.parallelize(datas1).intersection(sc.parallelize(datas2)).foreach(println);
     distinct 去重复 datas.distinct().foreach(println)
     
     repartition 就是coalesce函数第二个参数为true的实现
     coalesce 将RDD进行重分区,使用HashPartitioner。且该RDD的分区个数等于numPartitions个数。如果shuffle设置为true,则会进行shuffle。
     
     partitions
     cartesian 笛卡尔积
     
     sample  data.sample(withReplacement = false, 0.5, System.currentTimeMillis)
     groupwith     
     crossProduct
     mapvalues
     sort
     
     宽依赖: 常见于K-V style RDD
     groupByKey  [(3,dog),(4,lion)] [(3,cat)]..... => (4,[lion])(6,[spider])(3,[dog, cat])(5,[tiger, eagle])
     reduceByKey List("dog", "tiger", "lion", "cat", "panther", "eagle").map(x => (x.length, x).educeByKey(_ + _) = Array((4,lion), (3,dogcat), (7,panther), (5,tigereagle))
     cogroup 对多个RDD中的KV元素,每个RDD中相同key中的元素分别聚合成一个集合。与reduceByKey不同的是针对两个RDD中相同的key的元素进行合并。
     sortByKey List((3, 3), (2, 2), (1, 4), (2, 3))).sortByKey(true)
     aggregateByKey
     sortBy sortBy(_._1) sortBy(_._2)...
     
     其他
     join 可能是宽依赖也可能是窄依赖,其区别是,当要对RDD进行join操作时,如果RDD进行过重分区则为窄依赖,否则为宽依赖。
     (List((1, "苹果"), (2, "梨"), (3, "香蕉"), (4, "石榴"))).join(sc.parallelize(List((1, 7), (2, 3), (3, 8), (4, 3), (5, 9))))
     = (4,(石榴,3)) (1,(苹果,7)) (3,(香蕉,8)) (2,(梨,3))    
     left outer join 左外连接,左边表中的值无论是否在b中存在时,都输出;右边表中的值,只有在左边表中存在时才输出。
     right outer join 和 left outer join 相反。
     举例:rdd={(1,2),(3,4),(3,6)} other={(3,9)}
     join    对两个RDD进行内连接    rdd.join(other)    {(3,(4,9)),(3,(6,9))}
     rightOuterJoin    对两个RDD进行连接操作,右外连接rdd.rightOuterJoin(other)    {(3,(4,9)),(3,(6,9))}
     leftOuterJoin    对两个RDD进行连接操作,左外连接rdd.rightOuterJoin(other)    {(1,(2,None)),(3,(4,9)),(3,(6,9))}    
action 返回值是一个值,
     reduce  reduce(_ + _) 求和 满足结合律 交换律
     
     count 计数
     countByKey 仅适用于(K, V)类型,对key计数,返回(K, Int)
     
     take 取前若干位
     first 即take(1)
     takeSample
     
     saveasfile
     saveAsTextFile
     saveassequence    
     
     lookup
     
     collect 转数组
     foreach collect是从远程拉取到本地,经过网络传输,foreach是在远程集群上遍历 如果是在本地的话,差别不大
     foreachPartition 这里function的传入参数是一个partition对应数据的iterator
     
     
     
 spark工作流程
     Spark的Application在运行的时候,首先在Driver程序中会创建sparkContext作为调度的总入口,
     在其初始化过程中会分别创建DAGScheduler进行Stage调度 TaskScheduler进行Task调度两个模块。
     DAGScheduler模块是基于Stage的调度模块,它为每个Spark Job计算具有依赖关系的多个Stage任务阶段,
     然后将每个Stage划分为具体的一组任务(通常会考虑数据的本地性等)以TaskSet 的形式提交给底层的TaskScheduler模块来具体执行。
     TaskScheduler负责具体启动任务,监控和汇报任务运行情况。而任务运行所需要的资源需要向Cluster Manager申请。
     目前,用spark-submit工具来提交Spark Application,其基本提交格式为:spark-submit [options] <app jar file> [app options] 在其附加参数中可以指定Driver和Executor相关的配置信息。    
     获取rdd的方法    
     1、从共享的文件系统获取,(如:HDFS、HBase) var rdd = sc.textFile("hdfs:///qgzang/1.txt")
     2、通过已存在的RDD转换
     3、将已存在scala集合(只要是Seq对象)并行化 ,通过调用SparkContext的parallelize方法实现    var rdd = sc.parallelize(1 to 10)
     wordcount例子:sc.textFile("README.md").flatMap(_.split(' ')).map((_,1)).reduceByKey(_+_) 其他方法是使用countByValue 和AggregateByKeyspark优化
     1.减少shuffle
     2.内存管理优化
     3.配置项优化
     4.数据序列化,数据结构优化
 代码调优
     1、复用相同RDD数据,避免重复创建多份RDD数据。
     2、persist(StorageLevel.MEMORY_AND_DISK_SER RDD持久化操作调用cache()和persist(), cache相当于persist(StorageLevel.MEMORY)
     3、尽量避免使用shuffle类算子(不是不使用) reduceByKey、join、distinct、repartition、groupByKey,cartesian ….
     4、对于非巨型的大变量,使用广播变量Broadcast机制。好处:(1)每个excutor只存放份数据,而不是1个task一份(2)减小task序列化时间
     场景:lookup表, mapside join
     常规做法:
     val rdd_A=…
       val rdd_B=…
     val rdd_C = rdd_A.join(rdd_B)
     优化做法:
     val rdd_A=…
       val rdd_B=…  .collect()
       val rdd_B_Brd = sc.broadcast(rdd_B)
     val rdd_C = rdd_A.map(x=> {
     val key=x._1
              val value1 = x._2
              val value2 = rdd_B_Brd.value.getOrElse  
           (key, (value1, value2) )
     })
     5、使用高性能的RDD算子。
     (1)使用reduceByKey/aggregateByKey替代groupByKey
     (2)使用mapPartitions替代普通map(优化点,但小心OOM)
     (3)使用foreachPartitions替代foreach(尤其是将结果数据输出到数据库时强烈建议使用)
     (4)变化太多可能会产生很多小任务,使用coalesce(TRUE, n)代替repartition
     6、使用Kryo优化序列化性能。spark默认的序列化机制是JAVA的ObjectOutputStream 兼容性好。但又大又慢
     7、数据结构,尽量少使用Java对象。尽量使用字符串代替对象,使用原始类型(Int,long)替代字符串,使用数组替代集合类型,这样尽可能地减少内存占用,从而降低GC频率,提升性能。
         只是尽量但也得兼顾代码可读性
     8、计算并发度优化 管理操纵RDD、Partition数控制task计算的并行度 调优shuffle的并发度
     9、其他。(1)无效数据处理能先过滤就先过滤,即filter前置。
         (2)能减小数据处理的数据记录冗余则尽量减小。
         比如: (key1,key2,value1,value2))需要基于key
         ((key1,key2),(key1,key2,value1,value2))
             好的写法:((key1,key2),(value1,value2)
         (3)有用本地运行则不用shuffle
         如:A.map().reduceByKey().map().reduceByKey()
               A.map().reduceByekey()
 参数调优
     1.driver-memory (1g-4g)
     --防止spark应用OOM
     2.num-executors
         --控制节点并发
     3.executor-memory (2-6G)
     4.executor-cores (2-4个)
     --决定task执行时长
     5.spark.default.parallelism(默认同RDD分区数同)
     --决定shuffle并发数
     6.spark.yarn.executor.memoryOverhead
     7. spark.kryoserializer.buffer.max (默认64M)环境调优
     1、优化jar分发性能,防止jar重复上传。 如:
     spark-submit  hdfs://nameservice1/work/wis_2/wis.jar
     spark-submit  wis.jar //运行时会上传jar包到hdfs
     2、数据本地化 HDFS和Mongodb
     3、dir目录配置spark.local.dir(多目录,ssd) spark生态圈
HDFS
     Hadoop分布式文件系统(Hadoop Distributed File System  HDFS)被设计成适合运行在通用硬件(commodity hardware)上的分布式文件系统。
     HDFS是一个高度容错性的系统,适合部署在廉价的机器上。
     三个部分:
     客户端(访问hdfs的入口)
     nameserver(可理解为主控和文件索引类似linux的inode)一般有个secondary备份
     datanode(存放实际数据的存server)
     第一个block副本放在和client所在的node里(如果client不在集群范围内,则这第一个node是随机选取,当然系统会尝试不选择哪些太满或者太忙的node);
     第二个副本放置在与第一个节点不同的机架中的node中(随机选择);
     第三个副本和第二个在同一个机架,随机放在不同的node中。
     优点:    1)处理超大文件     2)流式的访问数据
     缺点:    1)不适合低延迟数据访问     2)无法高效存储大量小文件       3)不支持多用户写入及任意修改文件  
     
     Sc.textFile(path: String, minPartitions: Int = defaultMinPartitions)
     Sc.objectFile[T](path: String, minPartitions: Int = defaultMinPartitions)
     Rdd.saveAsTextFile(path: String): Unit
     Rdd. saveAsObjectFile(path: String): Unit
     
     RDD save到Hdfs后,RDD的每个分区都会对应于一个HDFS文件。如果一个RDD数据小,但分区多,则最好对RDD调整分区后,再保存。
     RDD. Coalesce(N)
     Coalesce用法注意:
     1、如果将1000个分区转换成100个分区,这个过程不会发生shuffle,相反如果10个分区转换成100个分区将会发生shuffle。
     2、也不能将N值设置太小,例如合并成一个分区,在数据量较大时会OOMKafka
     Kafka是一个消息系统,原本开发自LinkedIn,用作LinkedIn的活动流(Activity Stream)和运营数据处理管道(Pipeline)的基础。
     Broker:Kafka集群包含一个或多个服务器,这种服务器被称为broker
     Topic:每条发布到Kafka集群的消息都有一个类别,这个类别被称为Topic,被分成多个partition,每个partition是个fifo队列
     Partition:物理上的概念,每个Topic包含一个或多个Partition.
     Producer:负责发布消息到broker,负责确定topic,负责确定partition,异步发送
         producer的ack的3种模式:
         0:这意味着生产者producer不等待来自broker同步完成的确认就继续发送下一条(批)消息。此选项提供最低的延迟但最弱的耐久性保证,因为其没有任何确认机制。
         1:这意味着producer在leader已成功收到数据并得到确认后,再发送下一条消息。此模式leader的确认后就返回,而不管partion的follower是否已经完成。
         -1:这意味着producer在follower副本确认接收到数据后才算一次发送完成。 此选项提供最好的耐久性,保证至少一个同步副本保持存活。
     Consumer:消息消费者,向Kafka broker读取消息的客户端。
     Consumer Group:每个Consumer属于一个特定的Consumer Group(可为每个Consumer指定group name,若不指定group name则属于默认的group)。
     
     spark stream读取kafka数据,2个方式(1)利用接收器(receiver)和kafaka的高层API实现。(2)直接用kafka底层的API来实现。(生产上常用)