spark 最佳实践学习笔记

大数据的特征4v
1、大量
2、多样
3、快速
4、价值

如何存储和计算大数据
大数据交易


加州大学伯克利分校AMP实验室

spark 读取HDFS的数据到内存,在内存中使用弹性分布式数据集RDD组织数据

支持常见的mapreduce范式,还支持图形计算、流计算

spark支持scala、python、R

RDD主要分两类操作:
1、转换(transformation)
2、动作  (action)


RDD生成方式:
1、由Driver程序的数据集生成,不适合处理大量数据,适合交互式或数据量较小
2、由外部数据集生成,由分布式数据直接生成分布式数据集

默认,RDD的transformation只有等到action时才会进行,可以对RDD进行持久化或cache操作,也会触发RDD进行真正的计算

RDD持久化操作persist()可以指定内存或磁盘,缓存操作cache()只在内存中

spark最核心的抽象是弹性分布式数据集RDD

spark严重依赖传递函数类型的参数,
有两种建议的方法:
1、匿名函数,适用于小片段的代码
2、object对象中的静态方法
不建议的方法:
1、传入普通类的方法,必顺将方法所属的实例一起传进去,方法的第一个隐含参数是this指向实例
普通类必顺具备序列化的能力(继承java.io.serializable),因为需要将代码分发到各个计算节点,普通类也可以不继承序列化,只需用function(val)代替method(def),因为function本身就支持序列化

spark的核心是RDD,RDD是分布式计算的

每个JOB的执行都会经历序列化、网络传输、反序列化、运行

在序列化时,会将JOB运行所依赖的变量、方法(闭包)全部打包一并序列化

RDD操作不能嵌套,即不能在RDD操作传入的函数参数的函数体中进行RDD调用

spark工作机制
调度管理、内存管理、容错机制、监控管理、配置管理

调度管理的主要目的是资源分配
spark集群上主要资源是cpu core数理和内存

spark调度按场景分两类:
1、spark程度之间的调度,是最主要的调度场景
2、spark程度内部的调度

集群模式下的spark程序
1、Driver程序: sprarkcontext
2、集群管理器
3、工作节点:
     执行器 task
     缓存


1、Driver程序
集群模式下,用户编写的spark程序称为Driver程序.每个Driver程序包含一个代表集群环境的sparkcontext对象与之连接,程序的执行从Driver程序开始,中间过程会调用RDD操作,这些操作通过集群资源管理器来调度执行,一般在worker节点上执行,所有操作执行结束后回到Driver程序中,在Driver程序中结束.
2、sparkcontext对象
每个驱动程序都 有一个sparkcontext对象,担负着与集群沟通的职责.
1、sparkcontext对象联系集群管理器,分配cpu、内存等资源
2、集群管理器在工作节点上启动一个执行器(专属本驱动程序)
3、程序代码会被分发到相应的工作节点上
4、sparkcontext分发任务(task)至各执行器执行

3、集群管理器
集群管理器负责集群的资源调度
spark支持3种集群部署方式,每种部署对应一种资源管理器
1、standalone模式(资源管理器是Master节点),只支持先进先出模式
2、Hadoop YARN(资源管理器是YARN集群),主要用来资源管理.YARN支持动态资源管理,更适合多用户场景下的集群管理,而且YARN可以同时调度spark计算和hadoop MR计算,还可以调度其它实现了YARN调度接口的集群计算,非常适合多个集群同时部署的场景,是目前最主流的一种资源管理系统
3、Apache Mesos(资源管理器是Mesos集群),Mesos是一个专门用于分布式系统资源管理的开源系统,可以对集群中的资源做弹性管理

执行器:每个spark程序在每个节点上启动的一个进程,专属于一个spark程序,与spark程序有相同的生命周期,负责spark在节点上启动的task,管理内存和磁盘.如果一个节点上有多个spark程序在运行,那么会相应地就会启动多个执行器.
Job:一次RDD action对应一次Job,会提交至资源管理器调度执行。
stage:Job在执行过程中被分为多个阶段,task集合。
task:在执行器上执行的最小单元。

spark程序之间的调度
spark程序之间的调度资源分配策略:
1、静态分配
2、动态分配

静态分配:spark程序启动时即一次性分配所有资源,运行过程中固定不变,直至程序退出。是一种简单可靠的分配策略,强烈建议优先使用这种策略
动态分配:运行过程中不断调整分配的资源,可以按需增加或减少。动态资源分配的粒度是执行器,即增加或减少执行器.spark程序在机器的每个节点上只有一个执行器,所以增加或减少执行器意味着spark程序服务的节点的增加或减少

共享变量
广播变量,计数器

广播变量是只读的,创建之后再修改没有意义,一般用val定义
计数器只能增加,可用于计数或求和,默认数值类型,可自定义类型,只有Driver程序可以读到这个计算器变量,

spark master容错分两种情况:standalone集群模式、单点模式.
standalone集群模式master容错是通过zookeeper实现的,多个master,一个是active,其它是standby.
slave节点运行着worker、执行器和Driver程序.

监控管理:web界面、metrics、外部系统

web界面:
每一个Driver的sparkcontext都会启动一个web界面,默认端口是4040
spark还提供了rest api接口,son格式
metrics指标体系:
spark还支持基于coda hale metrics library的指标体系,可以主动将运行状态发送给其它系统,方便与其它系统集成
其它监控工具:
集群级别:ganglia 监控各节点的cpu、磁盘、网络负载等
操作系统级别:操作系统自带的工具(dstat、iostat、iotop)
JVM工具:jstack,jmap,jstat,jconsole等

spark程序配置管理:
灵活配置,可以使用环境变量、配置文件、命令行参数,还可以直接在spark程序中直接指定,优先给不同,可以相互覆盖


spark程序配置加载过程:
spark程序是通过脚本bin/spark-submit来提交的,交互式编程也由它提交
1、设置SPARK_HOME环境变量为bin/spark-submit脚本父目录
2、配置文件目录,由环境变量SPARK_CONF_DIR指定,默认为${SPARK_HOME}/conf
3、执行配置文件spark-env.sh,设置基本的环境变量
4、加载配置文件目录下默认配置文件spark-defaults.conf
5、读取命令行参数,覆盖前面配置
6、使用sparkconf对象的配置覆盖前面的配置


环境变量配置:
可以在提交前通过export设置也可以在配置文件目录下的spark-env.sh文件中指定
常用的通用配置项:
1、SPARK_LOCAL_IP: 绑定的ip
2、SPARK_PUBLIC_DNS: Driver程序使用的DNS服务器
3、SPARK_CLASSPATH:额外追加的CLASSPATH

spark属性项配置:
1、spark-defaults.conf
2、命令行参数
3、sparkconf对象
优先级依次由低到高

spark日志配置:
使用配置文件目录下的log4j.properties作为配置文件

spark内核讲解

spark核心数据结构RDD:

RDD是spark最重要的抽象,掌握了RDD,就掌握了spark计算的精髓。

RDD定义:
一个RDD包含5个核心属性.
1、一个分区列表,每个分区里是RDD的部分数据(或数据块)
2、一个依赖列表,存储依赖的RDD
3、一个名为compute的计算函数,用于计算RDD各分区的值
4、分区器(可选),用于键值类型的RDD,比如某个RDD是按散列来分区的
5、计算各分区时的选先的位置列表(可选)

spark支持两种分区方式:hash、range

5、RDD依赖分两种类型:
窄依赖:依赖父分区的部分分区
shuffle依赖:依束父分区的全部分区

对依赖链条过长可以设置检查点,对中间结果保存一份快照,流式计算依赖链条会无限扩大需要设置检查点

transformation代表计算的中间结果,而action代表计算的最终结果,action不可以在transformantion的内部调用
transformation只建立计算关系,而action才是真正的执行者,action才会真正的向集群提交Job,每一个action代表一个Job

shuffle就是将分布在不同节点上的数据汇聚到一个节点的过程
shuffle是一个非常消耗资源的操作,涉及大量的IO和大量内存,还会生成大量临时文件,用于避免错误恢复时重新计算
shuffle使用的本地磁盘目录由spark.local.dir指定

sparkcontext是spark程序的主要入口,用于与集群建立连接,所有的spark程序都必顺创建一个sparkcontext对象
初始化sparkcontext时只需要sparkconf配置对象作为参数或无参(会默认生成一个sparkconf对象)

DAG调度与Task调度:
DAG是最高层级的调度,为每个Job绘制出一个有向无环图,跟踪各task的输出,计算完成Job的最短路径,并将task提交到task调度器来执行,而task调度器只负责接收DAG调度器的请求,负责task的实际调度执行
DAG调度的粒度是stage,具体执行过程是将stage下的task提交至task调度器
task调度器从DAG调度器的stage接收一组task,并负责将它们提交至集群,运行它们,出错进行重试,最后返回消息给DAG调度器


其它功能接口
sparkcontext除了初始化环境、连接集群外,
1、创建RDD
2、RDD持久化
3、创建共享变量
4、stop(),停止sparkcontext
5、runjob,提交action操作


spark sql是spark的一个模块,专门用于处理结构化数据

使用spark sql有两种方式:
1、作为分布式sql引擎
2、在spark程序中,通过领域api来操作数据(抽象dataframe)

分布式sql引擎:
有两种运行方式:jdbc/odbc server,使用spark sql命令行

jdbc/odbc server默认tcp协议,默认绑定host为localhost,表示所有ip,默认端口为10000,也可以设置为http协议,hive.server2.transport.mod=http,默认端口为10001
jdbc/odbc server模式:
1、DB,用于存储数据库元数据
   a、创建mysql服务,创建用户并赋权
   b、配置conf/hive-site.xml
2、启动一个jdbc/odbc server作为对外服务的接口
   a、./sbin/start-thriftserver.sh
3、测试,使用交互式测试工具beeline
   ./bin/beeline
   beeline> !connect jdbc:hive2://localhost:10000
   然后输入用户名和密码
   如果出现查询结果中文乱码:LANG=zh_CN.UTF-8; ./bin/beeline 启动时指定客户端编码

spark sql命令行:另外一种运行spark sql方式,适合本地调试
1、./bin/spark-sql
2、也支持与jdbc/odbc server一样的配置

spark sql支持绝大多数hive特性

catalyst执行优化器
所有的spark sql语句最终由catalyst解析、优化并最终生成可以执行的java字节码
catalyst是spark sql最核心的部分,其最主要的数据结构是树,所有的sql语句都由树结构来存储,树中的每一个节点都由一个class,以及0或多个子节点.
catalyst另外一个重要且基础的概念是规则,基本上所有优化都是基于规则的
catalyst的执行过程分为四个阶段:
1、分析阶段,分析逻辑树,解决引用
2、逻辑优化阶段
3、物理计划阶段,catalyst会生成多个计划,并基于成本进行对比
4、代码生成阶段,将查询编译成java字节码

spark实时流式计算spark streaming
spark streaming以spark为核心,具备可扩展性、高吞量、自动容错等特点,数据源支持hdfs/s3/nfs,kafaka,flume,twitter,zeromq,kinesis或tcp socket,处理时可以使用map,reduce,join,window等高级函数实现复杂逻辑,结果可以写入文件系统、数据据、实时展示系统等
在内部,spark streaming接收实时数据,按周期将数据分成多批次(batch),按批次提交给spark核心来调度计算
spark streaming使用的数据抽象是DStream,表示连续的数据流,但内部其实是通过RDD序列来存储的
spark streaming支持java、python、scala语言,暂不支持R

StreamingContext是spark streaming编程最基本的环境对象,提供最基本的功能入口,包括从各途径创建最基本对象DStream(类似spark的RDD)
StreamingContext创建:
1、val conf = new SparkConf().setAppName("SparkStreamingWordCount")
   注:spark-shell下,需先调用sc.stop(),来停止默认的sparkcontext,
   val ssc = new StreamingContext(conf,Seconds(5))
   注:运行周期为5秒,表示流式计算间隔5秒执行一次,时间设置应该大于每次运行时间
2、从sc中创建
   val ssc = new StreamingContext(sc,Seconds(5))
一个完整个流式计算需要以下几个步骤:
1、创建一个输入DStream,用于接入数据
2、使用作用于DStream上Transformatin和output操作来定义流式计算(spark程序使用Transformation和action)
3、启动计算,使用streamingContext.start();
4、等待计算结束,使用streamingContext.awaitTermination();
5、也可以手工结束计算,streamingContext.stop();

DStream内部是一个RDD序列,每个RDD对应一个计算周期

容错处理
RDD是只读、可重复计算的分布式数据集,它用链条记录了RDD从创建开始的中间每一步的计算过程,错误恢复就是重新计算的过程
spark操作的数据一般都存储在有容错功能的文件系统上,从这些系统上的数据生成的RDD也具有容错能力,但是这不适用于spark streaming.因为大部分场景下,spark streaming的数据来自网络,为了达到相同的容错能力,通过网络接收到的数据还被复制到其它节点上,这就导致错误发生时有两类数据需要恢复:
1、刚收到已经缓存,但还没有被复制到其他节点的数据。因为没有副本,恢复的唯一方法是从数据源重新获取一份
2、收了到且已经复制到其他节点的数据。可以从其他节点恢复
此外,还有两类可能发生的错误:
1、worker节点失效。一旦计算节点失效,所有内存中的数据都会丢失且无法恢复
2、Driver节点失效。如果运行Driver进程的节点失效,那么SparkContext也会随之失效,整个Streaming程序会退出,所有附属的执行节点都会退出,内存中的数据全部丢失。
关于容错保障的效果定义,一般都是用数据计算的次数来定义:
1、至多一次。每条记录最多被计算一次或根本没有计算就丢失了
2、至少一次。保证每条记录都不丢失,最少计算一次,但可能会重复多次计算
3、精准一次。保证每条记录都不丢失,并且只计算一次,不多不少,显示这是最佳的容错保障
一般流式计算分为3步:
1、输入数据流 数据接收
2、数据计算
3、结果输出
要想实现精准一次的容错效果,需要确保每一步都能实现精准一次的计算:
步骤一、数据接收,容错保障很大程度上依赖于数据源
步骤二、transformation计算,因为有RDD容错性的保证,所以可以实现精准一交的容错保障
步骤三、结果输出,默认只提供“至少一次”的容错保障,不到达到“精准一次”的级别,是因为还依赖输出操作的类型和下一级接收系统是否支持事务特性。
数据接收容错:
不同数据源提供不同程序的容错保障
1、对于HDFS,S3等自带容错功能的文件系统,我们可以保障精准一次的容错能力
2、Spark从1.3引入新的Kafka Direct API,也可以保障精准一次的容错能力
3、对于其他使用接收器来接收数据的场景,视接收机制是否可靠以及是否开启WAL功能而不同
结果输出容错:
结果输出操作本身提供至少一次级别的容错性能,就是说可能输出多次至外部系统,但可能通过一些辅助手段来实现精准一次的容错效果
当输出为文件时是可以接受的,因为重复的数据会覆盖前面的数据,结果一致,效果相当于精准一次
其它场景下的输出要想实现精准一次的容错,需要一些额外的操作,有两种方法:
1、冥等更新。确保多操作的效果与一次操作的效果相同,如saveAS***Files即便调用多次,结果还是同一个文件
2、事务更新。更新时带上事务信息,确保更新只进行一次,更新时判断是否已经更新
检查点:
流式计算7*24小时的运行特点,除了考虑具备容错能力,还要考虑容错的代价问题。为了避免错误恢复的代价与运行时间成正比增长,Spark提供了检查点功能,用户定期记录中间状态,避免从头开始计算的漫长恢复。
调用有状态的Transformation操作必顺启用检查点,如updateStateByKey或reduceByKeyAndWindow.因为有状态的操作是从程序开始时一直进行的,如果不进行检查点,那么计算连接会随着时间一直增长,重新计算代价太高。
另外,如果期望程序在因Driver节点失效后的重启之后可以继续运行,也建议开启检查点功能,可以记录配置、操作以及未完成的批次,重启后可以继续运行.
实际上大部分程序不需要启用检查点
开启检查点的方法,调用streamingContext.checkpoint(checkpointDirectory)即可,参数是一个支持容错的文件系统目录,如HDF3、S3.
检查点是有代价的,需要存储数据至存储系统,增加批次的计算时间,并且降低吞吐量,我们可以通过增加周期的时间间隔来降低影响,一般建议时间间隔至少为10秒
性能调优:
Spark流式计算程序想要运行流畅,需要一些基本的调优:
1、每个批次的处理时间尽可能短
   a、增加数据接收的并发数量,尤其是当瓶颈发生在数据接收时。
   默认每个InputDStream都只会创建一个接收器,运行在某个节点上,可以创建多个InputDStream,它他们接收不同的数据分区。以实现并行接收。
   b、数据处理的并发度,如果并发度不够,可能导致集群的资源不被充分利用,查看各机器CPU的所有核心是不是都在工作,可以调整 选项spark.default.parallelism来增加并发度
   c、数据序列化,数据接收后,当需要与磁盘交换数据时,数据可能会进行序列化与反序列化,好处是节省空间和内存,但会增加计算负载。因些我们应习可能地使用Kryo来完成这项工作,cpu和内存开销相对少一些
   最后要注意task启动的额外开销,如果task启动过于频繁(50s/次),那么额外开销可能非常高,甚至无法达到那样的实时计算要求
2、收到数据后,尽可能快地处理
    设置合理批次间隔时间。一般来说,短时间间隔导致更多的额外开销,以及无法完成的风险,所心前期可以采取相对保守的方法,如间隔设置为5~10秒,然后观察后进行按需缩短间隔时间
图计算可以简单理解为以图这种数据结构为基础,基于其实现的相关算法及应用.
Spark的图计算库叫作GraphX
一个图由顶点集V和顶点间的关系集合E组成,可以用二元组定义为:G=(V,E)
图的计算量一般都比较大,而且通常会有多次迭代。
Spark GraphX依托Spark的强大计算能力,提供了图计算需要的便捷API,同时兼具并行计算的性能,是做大规模图计算的一把利器
GraphX的核心数据结构是Graph,是一种携带了每个点和边的属性的有向多重图.
多重图就是一对源、目的节点之间允许在多条边,以便表示不同的关系
Graph数据结构比较简单,由VertexRDD[VD]和EdgeRDD[ED]组成,VD和ED分别表示顶点和边的抽象数据结构,实际等价于RDD[(VertexID,VD)]和RDD[Edge[ED]]两种RDD
RDD是scala语言的核心数据结构
GraphX使用Vertex-Cut(点分割)方式,即将图的顶点集合划分到不同的计算节点上,这样可以减少分布式计算时的通信和存储消耗
GraphX内部维护了3个RDD来存储一个图:一个顶点表(Vertex Table),存储顶点信息;一个边表(Edge Table),存储边信息;一个是路由表(Routing Table),用来查询顶点存储在哪个计算节点上;
GraphX常用API分为:数据查询、数据转换、结构转换、关联聚合、缓存操作等
图计算中最常用的API有3类:数据查询类、关联类和聚合类.
关联类API是将一个图和一个RDD通过节点ID(VertexID)关联,来使图获得RDD中的信息
Spark MLlib机器学习库
MLlib是Spark为解决机器学习问题开发的库,这些问题包括:分类、回归、聚类、协同过滤等
本质上,MLlib就是RDD上一系列可供调用的函数的集合
如果一个系统能够通过执行某个过程而改进性能,这就是学习
机器学习能够自动地从数据中学习“程序”,而这个“程序”不是人来编写的
机器学习方法=模型+策略+算法
模型(model):计算机如何表达要解决的问题,就是机器学习的模型,这里使用一个函数f(x)来表达,具体函数实现是为求均值
策略(strategy):模型通常是有参数的,所以可能的模型有很多个,如何评估模型的优劣是机器学习的策略,这里通过计算误差平方和评估模型的优劣,这个误差平方和通常叫做平方损失函数
算法(algorithm):损失函数评估模型的优劣通常通过一个搜索算法来找到最优的模型,这里通过函数求导来搜索损失函数值最小的算法,借此来选择最优模型
机器学习的一般过程大概可以分为几个步骤:
1、准备训练数据集合
2、选择学习模型
3、行动学习策略
4、实现求解最优模型的算法
5、使用最优模型对新数据进行预测
每一步骤都有很多细致的工作要做:
首先,在准备训练数据时,训练数据要注意满足以下两点:
1、尽可能和应用场景同分布
2、尽可能充分,而且充足
模型选择一般还要考虑以下几点:
1、训练时间:随着观测数据的变化,需要重新训练模型,训练新模型的时间不能太长
2、预测时间:模型上线工作的时候,对于新的输入预测得分所需要的时间
3、模型的存储:模型运行的时候需要多少内存空间
机器学习参考资料:
李航的《统计学习方法》,通俗易懂,系统地介绍了机器学习的常见算法
Andrew Ng的视频课程Machine Learning是目前的高校经典课程
Christpher M.Bishop的著作Pattern Recognition And Machine Learning是模式识别与机器学习的经典教材
Toby Segaran的《集体智慧编程》和Peter Harrington的《机器学习实战》适合入门
MLlib库简介
基础数据类型:
1、向量
向量(vector)通过import mlib.linalg.Vectors使用,MLlib支持稠密向量和稀疏向量。稀疏向量只存储值非零的项.
如 Vectors.sparse(1095023,Array(1,7,31),Array(1.0,1.0,1.0)),1095023表示最大的下标,第一个数组表示非零值的下标,第二个数组表示非零项的具体值
2、labeled point
labeled point是一个带标注(label)的向量,也可以是稠密或者稀疏的,用于监督学习中表示一个特征向量和一个标注。这里规定标注是double类型
3、各种模型类
训练算法输出就是各种模型类
MLlib库主要有这几块:分类、回归、聚类、推荐等
 

猜你喜欢

转载自blog.csdn.net/happyzwh/article/details/82820362