1. 大数据计算体系
- 大数据计算体系:数据存储系统、数据处理系统、数据应用系统
- 数据存储系统:包括数据采集层(系统日志、网络爬虫、无线传感器网络、物联网、以及各种数据源);数据清洗、抽取与建模(将各种类型的结构化、非结构化、异构数据转化为标准存储格式数据,并定义数据属性及值域);数据存储架构(集中式/分布式文件系统、关系型数据库/分布式数据库、行存储数据结构/列存储数据结构、键值对结构、哈希表检索);数据统一接口等。
- 数据处理系统:包括针对不同类型数据的计算模型,如针对海量数据的MapReduce批处理模型、针对动态数据流的流计算模型、针对结构化数据的大规模并发处理模型、基于物理大内存的内存计算模型;针对机器学习算法的数据流图模型;各类分析算法实现(回归分析、聚合算法、关联规则算法、决策树算法、贝叶斯分析等),及提供各种开发工具包和运行环境的计算平台,如Hadoop, Spark, Storm等。
- 数据应用系统:基于上述计算架构和处理平台提供各行业各领域的大数据应用技术解决方案。目前,互联网、电子商务、电子政务、金融、电信、医疗卫生等行业是大数据应用最热门的领域,制造业、教育、能源、环保、智慧交通、社会公共安全则是大数据技术即将或已经开始拓展的行业。
2. Spark与Hadoop的区别
-
时间节点上来看
- Hadoop在2006·1开始开发,2008·1成为apache顶级项目,2011年发布1.0版本,2013年10月发布2.x版本(Yarn)
- Spark2009年诞生,2013·6称为apache项目,2014·2成为顶级项目,2015至今开始大量重点使用Spark
-
组成上来看
- Hadoop由Java编写,是一个在分布式服务器集群上存储海量数据并运行分布式分析应用的开源框架。HDFS是其分布式文件系统,MapReduce是Hadoop的分布式计算模型,HBase是基于HDFS的分布式数据库。
- Spark是一种快速、通用、可扩展的大数据分析引擎,Spark Core提供基础和核心功能,SparkSQL用来操作结构化数据,SparkStreaming是针对实时数据的流式计算组件
-
框架上看:MapReduce和Spark都是数据处理框架
- MR作业流程中,对于计算模型只有map和reduce两个模型,对于上层应用来说就需要拆分算法或者是实现多个任务的串联。而任务之间的关联需要磁盘交互,这种情况下,磁盘I/O会影响性能
- Spark中含有丰富的数据处理模型,而且它可以基于内存做多次迭代,能够更好的支持刚才所说的数据挖掘算法以及图计算等。只考虑数据处理的过程,Spark把任务的计算结果放在Memory内存中进行,为下一次计算提供了更加便利的处理方式,效率高。
-
从功能上看
- Spark数据存储在内存中:Spark更适合于迭代计算较多的机器学习和数据挖掘,因为在Spark中有RDD的抽象概念
- Spark比Hadoop更通用:Spark提供的数据集操作类型很多,且节点间通信模型不再只有Shuffle一种模式
- 容错性:分布式数据集计算时通过checkpoint来实现容错,有两种方式:checkpoint data、logging the updates
- 可用性:Spark提供丰富的Scala、Java、python API;Spark可以直接对HDFS读写,可以于MR运行于同一个集群中,共享存储于计算资源,且几乎与hive完全兼容。
-
总结/MR与Spark:
-
数据存储机构:MR/HDFS Spark/RDD
-
编程范式:MR/Map+Reduce Spark/DAG(有向无环图)
-
中间结果存储:MR/磁盘 Spark/内存
-
运行方式:MR/task以进程方式维护,任务启动慢 Spark/task以线程方式维护,任务启动快
-
3. Spark的特点
-
先进架构:
- Spark采用Scala语言编写,底层采用了actor model的akka作为通讯框架,代码十分简洁高效。
- 基于DAG图的执行引擎,减少多次计算之间中间结果写到Hdfs的开销。
- 建立在统一抽象的RDD(分布式内存抽象)之上,使得它可以以基本一致的方式应对不同的大数据处理场景。
-
高效快速:
- 提供Cache机制来支持需要反复迭代的计算或者多次数据共享,减少数据读取的IO开销。
- 与Hadoop的MapReduce相比,Spark基于内存的运算比MR要快100倍;而基于硬盘的运算也要快10倍!
-
易用:
- Spark提供广泛的数据集操作类型(20+种),不像Hadoop只提供了Map和Reduce两种操作。
- Spark可以支持Java,Scala,Python,R以及SQL多种语言。还支持超过80种高级算法,使得用户可以快速构建不同的应用。支持交互式的Python和Scala的shell。
-
通用(提供整体解决方案):
- 以其RDD模型的强大表现能力,逐渐形成了一套自己的生态圈,提供了full-stack的解决方案。
- 主要包括Spark内存中批处理,Spark SQL交互式查询,Spark Streaming流式计算, GraphX和MLlib提供的常用图计算和机器学习算法。
-
与Hadoop无缝衔接:
- Spark可以使用YARN作为它的集群管理器
- 读取HDFS,HBase等一切Hadoop的数据
4. Spark部署模式
- 本地模式Local Mode:将Spark应用程序中任务Task运行在一个本地JVM Process进程中,通常供开发测试或小数据集使用。
- Standalone(Spark)集群模式(开发测试及生产环境使用):类似Hadoop Yarn架构,典型的Master/Slaves模式,使用Zookeeper搭建,避免Master单点故障。
- Hadoop YARN集群模式(生产环境使用):运行在Yarn集群之上,由Yarn负责资源管理,Spark负责任务调度和计算。好处是计算资源按需伸缩,集群利用率高,共享底层存储,避免数据跨集群迁移。
- Apache Mesos集群模式(国内使用较少):运行在mesos资源管理器框架之上,由mesos负责资源管理,Spark负责任务调度和计算。
- 选择哪种模式?
- 只是测试Spark Application,你可以选择local模式。而如果你数据量不是很多,Standalone 是个不错的选择。当你需要统一管理集群资源(Hadoop、Spark等),那么你可以选择Yarn或者mesos,但是这样维护成本就会变高。
- 从对比上看,mesos似乎是Spark更好的选择,也是被官方推荐的
- 但如果你同时运行hadoop和Spark,从兼容性上考虑,Yarn是更好的选择。
- 如果你不仅运行了hadoop,spark。还在资源管理上运行了docker,Mesos更加通用
- Standalone对于小规模计算集群更适合!
5. Spark运行架构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AfmR9dP8-1624432177364)(C:\Users\Lee\AppData\Roaming\Typora\typora-user-images\image-20210623142436005.png)]
- Spark的Driver Program (简称Driver)包含用户的应用程序
- Driver完成task的解析和生成
- Driver向Cluster Manager(集群资源管理器)申请运行task需要的资源。
- 集群资源管理器为task分配满足要求的节点,并在节点按照要求创建Executor
- 创建的Executor向Driver注册。
- Driver将spark应用程序的代码和文件传送给分配的executor
- Executor运行task,运行完之后将结果返回给Driver或者写入HDFS或其他介质。
6. Spark核心组件
包括RDD、Scheduler、Storage、Shuffle四部分
- RDD是Spark最核心最精髓的部分,spark将所有数据都抽象成RDD。
- Scheduler是Spark的调度机制,分为DAGScheduler和TaskScheduler。
- Storage模块主要管理缓存后的RDD、shuffle中间结果数据和broadcast数据
- Shuffle分为Hash方式和Sort方式,两种方式的shuffle中间数据都写本地盘
7. RDD概念
- RDD (resilient distributed dataset) 是Spark的核心概念,指的是一个只读的,可分区的分布式数据集(分布式弹性数据集),这个数据集的全部或部分可以缓存在内存中,在多次计算间重用。Spark的核心思路就是将数据集缓存在内存中加快读取速度,同时用RDD以较小的性能代价保证数据的鲁棒性。
- RDD特点如下:
- 一是数据全集被分割为多个数据子集,每一个数据子集被分发到集群中的任意节点进行处理;
- 二是RDD使得所有计算的中间结果被保存下来,出于可靠性的考虑,同一个计算结果也会在集群中的多个节点进行保存备份。这样如果其中的某一数据子集在计算过程中出现了问题,针对该数据子集的处理会被重新调度,进而完成容错机制。
- 三是RDD这一数据结构都是可序列化的,在内存不足的时候,可以自动降级为磁盘存储。
- RDD有五个主要的属性
- 一组分片Partition:即数据集的基本组成单位。对于RDD来说,每个分片都会被一个计算任务处理,并决定并行计算的粒度。用户可以在创建RDD时指定RDD的分片个数,如果没有指定,那么就会采用默认值。默认值就是程序所分配到的CPU Core的数目
- 一个计算每个分片的函数:对于给定的数据集,需要做哪些计算;Spark中RDD的计算是以分片为单位的,每个RDD都会实现compute函数以达到这个目的。compute函数会对迭代器进行复合,不需要保存每次计算的结果。
- 依赖Dependencies:RDD的依赖关系,描述了RDD间的lineage。RDD的每次转换都会生成一个新的RDD,所以RDD之间就会形成类似于流水线一样的前后依赖关系。在部分分区数据丢失时,Spark可以通过这个依赖关系重新计算丢失的分区数据,而不是对RDD的所有分区进行重新计算。
- preferredLocations(可选):对于data partition的位置偏好,存储存取每个Partition的优先位置(preferred location)。对于一个HDFS文件来说,这个列表保存的就是每个Partition所在的块的位置。按照“移动数据不如移动计算”的理念,Spark在进行任务调度的时候,会尽可能地将计算任务分配到其所要处理数据块的存储位置
- partitioner(可选):对于计算出来的数据如何分发,即RDD的分片函数/分区器。当前Spark中实现了两种类型的分片函数,一个是基于哈希的HashPartitioner,另外一个是基于范围的RangePartitioner。只有对于key-value的RDD,才会有Partitioner,非key-value的RDD的Parititioner的值是None。Partitioner函数不但决定了RDD本身的分片数量,也决定了parent RDD Shuffle输出时的分片数量。
8. RDD操作
-
创建RDD
- 以行为单位读取:sc.textFile()
- 以文件为单位读取:sc.wholeTextFiles()
- 通过并行集合创建:sc.parallelize(seq) / sc.makeRDD(seq,numSlices)
-
转换算子 transform:功能的补充和封装,将旧的RDD包装成新的RDD
- map(func):将处理的数据逐条进行映射转换,可以是类型转换也可以是值的转换
- flatMap(func):将处理的数据进行扁平化后再进行映射处理,也即扁平映射.(将整体拆分成个体)
- groupBy(func):将数据根据指定的规则进行分组,分区默认不变,但是数据会被打乱重新组合
- filter(func):将数据根据指定的规则进行筛选过滤,符合规则的数据保留,不符合规则的数据丢弃
- reduceByKey(func):将数据按照相同的key对value进行聚合
- groupByKey(func):将分区的数据直接转换为相同类型的内存数组进行后续处理
- keys/values:把pair RDD中的key/value返回形成一个新的RDD
- sortByKey(boolean):返回一个根据键值排序的RDD,默认升序false降序
- sortBy(func)
- mapValues(func):对键值对中的每个value都应用一个函数,key值不会发生变化
- join():把几个RDD中元素key相同的进行连接
-
行动算子action:触发任务的调度和作业的执行
- count():返回RDD中元素的个数
- collect():在driver program中,以数组array的形式返回数据集中的所有元素
- take(num):返回一个由RDD的前n个元素组成的数组
- reduce(func):聚集RDD中的所有元素,先聚合分区内数据,再聚合分区间数据
- foreach(func):分布式遍历RDD中的每一个元素,调用指定函数
-
reduceByKey和groupByKey区别
- groupByKey会导致数据打乱重组,存在shuffle操作。
- 在Spark中,shuffle操作必须落盘处理,不能再内存中数据等待,否则可能会导致内存溢出。Shuffle操作的性能非常低
- reduceByKey在分区内进行预聚合操作,有效减少shuffle落盘时的数据量。
- 从Shuffle的角度: reduceByKey和 groupByKey都存在 shuffle 的操作,但是reduceByKey可以在 shuffle前对分区内相同key的数据进行预聚合(combine)功能,这样会减少落盘的数据量,而groupByKey只是进行分组,不存在数据量减少的问题,reduceByKey性能高。
- 从功能的角度:reduceByKey其实包含分组和聚合的功能。GroupByKey只能分组,不能聚合,所以在分组聚合的场合下,推荐使用reduceByKey,如果仅仅是分组而不需要聚合。那么还是只能使用groupByKey
- groupByKey会导致数据打乱重组,存在shuffle操作。
-
RDD持久化
- rdd.cache() == rdd.persist(MEMORY_ONLY)
- rdd.persist(StorageLevel.level)
- MEMORY_ONLY
- MEMORY_ONLY_SER
- MEMORY_AND_DISK
- MEMORY_AND_DISK_SER
- DISK_ONLY
-
RDD分区
- 作用:增加程序并行度实现分布式计算、减少通信开销
- 创建时设置默认分区数:conf.set(“spark.default.parallelism”,“5”)
- 创建RDD时可以传入第二个参数设置
- rdd.repartition()
- 自定义分区
// 自定义分区类 import org.apache.spark.{Partitioner,SparkContext,SparkConf} class MyPartitioner(numParts:Int) extends Partitioner { override def numPartitions: Int = numParts override def getPartition(key: Any): Int = { key.toString.toInt % 10 } }
// 使用自定义分区测试 //构建原始RDD val dataRDD = sc.makeRDD(1 to 10,5) // 使用自定义分区方法分区 // 由于只有key-value类型数据有分区器,需要先将数据转换为键值对类型 val PartitionRDD = dataRDD.map((_, 1)).partitionBy( new MyPartitioner(10) ) // 将数据还原 val MapRDD = PartitionRDD.map(_._1) //存储为文件 MapRDD.saveAsTextFile("output2")
-
RDD读写
- 读见创建RDD
- 写:rdd.saveAsTextFile(“文件夹路径”) 文件夹路径必须不存在
- HDFS:sc.textFile(“hdfs://localhost:9000/filepath”)
- JSON:
- 解析JSON文件——JSON.parseFull()方法
- Scala当中有个自带的json库,叫scala.util.parsing.json.JSON,可以利用这个自带库,对json文本文件进行解析。那这里面解析用的是JSON.parseFull这样一个方法,这个方法括号里传递的就是json文本中一行行的文本。第一行是{name: michael},把这行文本传到JSON.parseFull里面去,就会把name,age的字段解析出来。这就是它的功能。但是要注意的是,有些数据符合格式,有些数据可能不是json格式,那么就有可能解析成功或者解析失败。那对于parseFull这个函数,当解析成功的时候它会把解析的结果封装成some对象返回,如果解析失败会返回none。
val inputFile = "filepath" val conf = new SparkConf().setMaster("local[*]").setAppName("json") val sc = new SparkContext(conf) val jsonFile = sc.textFile(inputFile) val result = jsonFile.map(s => JSON.parseFull(s)) // 打印RDD result.foreach({r => r match{ //解析成功 case Some(map:Map[String,Any]) => println(map) //解析失败 case None => println("Parsing Failed") //无法识别 case other => println("Unkonwn data structure:" + other) }})
-
spark-submit命令
首先用打包编译的命令去打包,编译之后得到jar包,然后再用spark-submit命令去运行。/usr/local/spark/bin/spark-submit 用这个命令提交,提交给参数,–class告诉我们用的程序的类,这里是JSONRead的类,后面是打包程序jar包的地址,这样回车之后,就得到执行结果,打印输出,第一个结果map(name->michael)第二个是map(name->andy,age->30.0)第三个是map(name->justin,age->19.0)这就是对于json文件相关的解析完整过程。
/usr/local/spark/bin/spark-submit \ --class "ClassName" \ /usr/local/spark/targetpath/targetName.jar
(name->michael)第二个是map(name->andy,age->30.0)第三个是map(name->justin,age->19.0)这就是对于json文件相关的解析完整过程。
/usr/local/spark/bin/spark-submit \
--class "ClassName" \
/usr/local/spark/targetpath/targetName.jar