0. 零碎概念

(1)

这个有点疑惑,有可能是错误的。

(2)

此处就算地址写错了也不会报错,因为此操作只是读取数据的操作(元数据),表示从此地址读取数据但并没有进行读取数据的操作

(3)分区(有时间看HaDoopRDD这个方法的源码,用来计算分区数量的)

物理切片:实际将数据切分开,即以前的将数据分块(每个数据块的存储地址不一样),hdfs中每个分块的大小为128m

逻辑切片:指的是读取数据的时候,将一个数据逻辑上分成多块(这个数据在地址上并没有分开),即以偏移量的形式划分(各个Task从某个数据的不同位置读取这个数据)

分区数与最小分区数有关,如果最小分区数为1 ,输入切片小于128m,就不在进行逻辑切分多个输入切片了。如果最小分区数为2,有两个文件,一个文件比较大,一个文件比较小,大的文件会被逻辑划分为两个输入切片,即大的文件对应两个分区,小的文件对应一个分区

   一般来说,一个文件对应于一个分区数,但是若两个文件的大小相差很大,则一个文件会有2个分区,即将这个大的文件进行了逻辑切片,如下

文件夹下有2个文件:1.txt,words.txt(words.txt比1.txt大很多)

在IDEA中执行wc任务后,日志文件如下,可见words.txt被逻辑切片成了两个分区

如果自己指定的分区数小于输入的切片数,则分区数会默认使用切片数作为分区数

注意:文件进行逻辑切片的条件:

  (一个文件的字节数据 / (目录下文件总的字节数 / 最小分区的数量)) > 1.1 就划分多个输入切片

最小分区数一般为2,但当自己设置并行度为1时,则最小分区数为1,原因如下:

 补充: 为什么最小分区数一般为2呢?

  spark本身就是个分布式的运算框架,其进行分布式运算的核心即为分布式数据集(RDD),rdd分区数有多少意味着task会有多少,并并行度有多少。若是rdd的分区数为1,则不是分布式运算了,就没意义了

例:

wc1目录中有3个文件,大小如下

代码(用来测试分区数)

object WordCount {
def main(args: Array[String]): Unit = {
// val conf: SparkConf = new SparkConf().setAppName("WordCount")
//往HDFS中写入数据,将程序的所属用户设置成更HDFS一样的用户
System.setProperty("HADOOP_USER_NAME", "root")
//Spark程序local模型运行,local[*]是本地运行,并开启多个线程
val conf: SparkConf = new SparkConf()
.setAppName("WordCount")
.setMaster("local[*]") //设置为local模式执行
// 1 创建SparkContext,使用SparkContext来创建RDD
val sc: SparkContext = new SparkContext(conf)
// spark写Spark程序,就是对神奇的大集合【RDD】编程,调用它高度封装的API
// 2 使用SparkContext创建RDD
val lines: RDD[String] = sc.textFile("hdfs://feng05:9000/wc1", 1)
println(lines.partitions.length)
lines.saveAsTextFile("E:/javafile/wc/out4")
// 释放资源
sc.stop()
}
}

运行结果:3个分区

查看日志文件,如下,发现words.txt没有进行逻辑切片(符合上面逻辑切片发生的条件)

上面自己设定的并行度为1,导致最小分区为1,下面不设置并行度(其他代码同上),即最小分区会变成2,这个时候,按照上面逻辑切分的条件,words.txt会被逻辑切分为两个切片,即最终会得到4个分区,事实也是如此,如下日志文件

如果最小分区数为1 ,输入切片小于128m,就不在进行逻辑切分多个输入切片了,若大于128m,则会被逻辑切分成2个切片(几乎平均切分)

总结:

  从hdfs中读取数据创建rdd,其并没有立即读取数据,而是记录以后要从hdfs中某个地址读取数据(读取数据的操作由其调度的task来执行),分区的数量有输入切片来决定。输入切片并不是看到的文件数量以及分块数,而是在读取数据的时候,尽量让每个task读取的数据大小均匀,所以相对大的文件会进行逻辑切片,即输入切片的数目就会增大

  • 并行化的方式创建rdd
 // 使用parallelize
val rdd1 =sc.parallelize(List(1,2,3,4,5,6,7))

此处的并行度与一开始设置的executor-cores有关,若设置为3,则会有3个分区,则会有3个Task

分区和分区器的区别

  分区是rdd用来决定spark中task的并行度的,而分区器则是决定上游的数据到下游哪个地方的(类似MR程序中,maptask处理完的数据会被分到下游的哪个reduceTask中)

(3) RDD中的collect

  collect相当于将executor计算好的数据收集起来,放置于driver端,以下是具体的

1. RDD的使用

1.0  什么是RDD

  RDD(Resilient Distributed Dataset)是一个抽象数据集,RDD中不保存要计算的数据集,保存的是元数据,即数据的描述信息和运算逻辑,比如数据要从哪里去读取,怎么运算等。RDD可以理解为一个代理,你对RDD进行操作,相当于在Driver端先是记录下计算的描述信息,然后生成Task,将Task调度到Executor端才执行真正的计算逻辑

1.1 RDD的特点

  •  有一些连续的分区

  分区编号从0开始,分区数量决定了对应阶段Task的并行度

  • 有一个函数作用在每个输入切片上

  每一个分区都会生成一个Task,对该分区的数据进行计算,这个函数就是具体的计算逻辑

  •  RDD和RDD之间存在一些依赖关系

  RDD调用Transformation后会生成一个新的RDD,子RDD会记录父RDD的依赖关系,包括宽依赖(有shuffle)和窄依赖(没有shuffle)

  • (可选的)K-V的RDD在shuffle会有分区器,默认使用HashPartioner
  •  (可选的)如果从HDFS中读取数据,会有一个最优位置:  

  spark在调度任务之前会读取NameNode的元数据信息,获取数据的位置,移动计算而不是移动数据,这样可以提高计算效率

 1.2 创建RDD的方式

1.2.1 读取外部文件的方式

(1)读取HDFS中的文件创建RDD

private def makeRDDFromHDFS = {
val conf: SparkConf = new SparkConf().setAppName(this.getClass.getSimpleName).setMaster("local[*]")
val sc = new SparkContext(conf)
val rdd: RDD[String] = sc.textFile("hdfs://doit01:9000/word.txt")
sc.stop()
}

这里要注意分区问题,见0处的分区

(2)读取本地数据创建RDD

private def makeRDDFromDisk = {
val conf: SparkConf = new SparkConf().setAppName(this.getClass.getSimpleName).setMaster("local[*]")
val sc = new SparkContext(conf)
// 返回的是处理整个文件数据的RDD
val rdd: RDD[String] = sc.textFile("d://word.txt")
rdd.foreach(println)
sc.stop()
}

1.2.2 集合转换(一般使用parallelize方法)

private def arrayToRDD = {
val conf: SparkConf = new SparkConf().setAppName(this.getClass.getSimpleName).setMaster("local[*]")
val sc = new SparkContext(conf)
val arr = Array("java", "vue", "js", "hive", "scala", "hbase")
val rdd: RDD[String] = sc.makeRDD(arr) // 底层调用的就是parallelize
// 参数一数据集 参数二 将数据集划分成N区,有利于分布式运算(将不同区的数据交给不同的worker去处理)
val rdd2: RDD[String] = sc.parallelize(arr, 3)
rdd.foreach(println)
sc.stop()
}

注意:

(1)makeEDD或者是parallelize方法接收的参数只能是seq序列(Array,List等,set和map不属于),map和set想转成RDD需要先将之转换成Arrat或者是List

(2)集合转成RDD时,如果本地运行(local[n]),并且makeEDD或者是parallelize方法没有指定分区数时,则得到的RDD分区数为n

1.2.3  RDD调用转换算子获取RDD

1.3  RDD的算子分类

  • Transformation:即转换算子,调用转换算子会生成一个新的RDD,R=Transfoemation是Lazzy的,不会触发job的执行
  • Action:行动算子,调用行动算子会触发job执行,本质上调用了sc.runJob方法,该方法从最后一个RDD,根据其依赖关系,从后往前,划分Stage,生成TaskSet  

1.3.1 RDD常用的Transformation算子

  • map算子,功能是做映射

 将原数据的每个元素传给函数func进行格式化,返回一个新的分布式数据集。源码如下

测试(spark-shell中测试)

  •  flatMap算子,先map再压平,spark中没有flatten方法

测试

(1)

此处若将flatMap换成map则会有如下结果

flat相当于压平操作,外部的Array可比成一个大的气球,里面的各个Array相当于里面的小气球,而flat的操作就是将这些小气球压破,使所有元素都放在外部的气球(Array)中

(2)测试的数据位集合中包含集合(使用了两次flatMap)

此处若将flatMap换成map,则运行的结果为

  • filter算子 ,功能为过滤数据

  •  mapPartitions

  将数据以分区的形式返回map操作,一个分区对应一个迭代器,该方法和map方法类似,只不过该方法的参数由RDD中的每一个元素变成了RDD中每一个分区的迭代器,如果在映射的过程中需要频繁创建额外的对象,使用mapPartions要比map高效的多

  使用map的话,若是要从数据库中拿数据,则每拿一条数据就要建立一次数据库的连接,比较耗费性能,但mapPartitions则是一个分区建立一次连接,这一个连接可以处理这个分区中的数据

  • mapPartitionsWithIndex

  类似于mapPartitions,不过函数要输入两个参数,第一个参数为分区的索引,第二个是对应分区的迭代器。函数返回的是一个经过该函数转换的迭代器

  • sortBy算子,用来排序

  此处若是将函数x=>x改为x=>x+“”,表示的是排序以字符串的形式排

  • sortByKey算子,按照key排序

  • groupBy算子,分组,既可以按照key也可以按照值分组

将_1换成_2就是按值排序了

  •  groupByKey 按照key进行分组

  只能按照key排序,不需要参数

   hello和flink的hashcode值一样,所以在一个分区中,但不在一个分组中,一个分区可以有多个分组

  • reduceByKey

  reduceByKey就是对元素为KV对的RDD中Key相同的元素进行binary_function的reduce操作(见action算子部分),因此,Key相同的多个元素的值被reduce为一个值,然后RDD中的Key组成一个新的KV对

groupByKey结合mapValues也能达到此聚合的目的,如下

那么,groupByKey和reduceByKey有什么区别呢?

  GroupByKey是直接将数据分组到下游,没有对数据进行处理,加大了shuffle的网络传输,但ReduceByKey则不一样,reduceByKey先是局部聚合再全局聚合,可以减少shuffle网络传输,提高聚合效率,两者的逻辑图如下

  • distinct算子,去重

  • union,intersection,subtract  

  •  join,相当于SQL中的内关联

  •  leftOuterJoin,相当于SQL中的左外关联

  •  rightOuterJoin,相当于SQL中的右外关联

  •  fullOuterJoin,相当于SQL中的全关联

  •  cartesian算子,笛卡尔积

  •  cogroup算子(用的比较多),协分组,有点跟fullOuterJoin类似,但是没有关联上的返回CompactBuffer()

源码如下

  Other,this,that,找个时间看下

  • aggregateByKey

  按照key进行聚合,跟reduceByKey类似,可以输入两个函数,第一个函数局部聚合,第二个函数全局聚合。初始值自在局部聚合时使用,全局聚合不使用。源码如下

例()

此处有两个分区,第一个分区中分为两个组cat部分和mouse部分,所以第一个分区的cat值为100,mouse也为100,同理第二个分区,所以得到如图所示的结果

  • combineByKey

  需要输入三个参数,第一个参数为分组后value的第一个元素,第二个参数为局部聚合函数,第三个参数为全局聚合函数

  reduceByKey、foldByKey、aggregateByKey、combineByKey底层调用的都是combineByKeyWithClassTag

 例1

 

x=>x:取出分区后各个组中value的第一个元素;(a: Int, b: Int) => a + b :将各个组的值相加,即局部聚合;(m: Int, n: Int) => m + n:此处m表示第一个分区中的值,n为第二个分区中的值,将各个分区中key相同的值相加,即全局聚合

例2

解法如下:

 x=>List(x):将分组后各组中value的第一个元素放入一个List;(a:List[String],b:String) => a:+b:将分组后各组中value的其他元素也加到各自的List中;(m:List[String],n:List[String]) => m ++ n):全局聚合

知识点补充

1.3.2 RDD常用的Action算子

  • reduce(binary_function)

  reduce将RDD中元素前两个传给输入函数,产生一个新的return值,新产生的return值与RDD中下一个元素(第三个元素)组成两个元素,再被传给输入函数,直到最后只有一个值为止

  与reduce对应的算子是fold,与reduce不同的是reduce可以给一个初始值,此处的初始值在局部聚合和全局聚合都会使用(foldByKey只是在聚不聚合时使用了初始值)

  •  collect,将数据以数组形式手机回收Driver端,数据按照分区编号有序返回

  

  •  count,返回rdd元素的数量

  •  top 将RDD中数据按照降序(默认降序)或者指定的排序规则,返回前n个数据

  •  take,返回一个由数据集的前n个元素组成的数组

  •  first,返回数据集中的第一个元素

  •  takeOrdered和top类似,默认升序返回

  •  saveAsTextFile以文本的形式保存到文件系统中
val rdd1 = sc.parallelize(List(3,2,4,1,5), 2)  //2个分区
rdd1.saveAsTextFile("hdfs://feng05:9000/haha")

结果查看

  • aggregate, 传入两个函数,第一个函数在分区内聚合,第二个全局聚合,可以传初始值,并且初始值在聚不聚he和全局聚合都会被使用

 

  • foreach  将数据一条一条的取出来进行处理,函数没有返回

task是在excutor中执行,此处的spark-shell为Driver端,数据并没有被收集到Driver端,前面能返回数据都是因为executor的数据被收集到Driver端,所以才能被显示,要想看到结果可以去executor的输出日志中看,如下

  •  foreachPartition, 和foreach类似,只不过是以分区为单位,一个分区对应一个迭代器,应用外部传的函数,函数没有返回值,通常使用该方法将数据写入到外部存储系统中,一个分区获取一个连接,效果更高

若直接打印迭代器,并不会将数据迭代出来,打印的只是迭代器的引用地址,所以使用foreach(迭代器中的foreach)将之遍历出来

 2.Spark中的一些重要概念

 2.1 Application

 使用SparkSubmit提交的计算应用,一个Application中可以触发Action,触发一次Action产生一个Job,一个Application中可以有一到多个Job

  Application是Driver在构建SparkContent的上下文的时候创建的, 就想申报员,现在要构建一个能完成任务的集群,需要申报的是这次需要多少个Executor,每个executor需要多少内存,以及所有executor可用的cpu数

 2.2 Job

  Driver向Executor提交的作业,触发一次Acition形成一个完整的DAG,一个DAG对应一个Job,一个Job中有多个Stage,一个Stage中有多个Task

2.3 DAG

  概念:有向无环图(即RDD有方向没有形成闭环,如下图),是对多个RDD转换过程和依赖关系的描述,触发Action就会形成一个完整的DAG,一个DAG对应一个Job

2.4 Stage

  概念:任务执行阶段,Stage执行是有先后顺序的,先执行前的,在执行后面的,一个Stage对应一个TaskSet,一个TaskSet中的Task的数量取决于Stage中最后一个RDD分区的数量

2.5 Task

  概念:Spark中任务最小的执行单元,Task分类两种,即ShuffleMapTask和ResultTask

Task其实就是类的实例,有属性(从哪里读取数据),有方法(如何计算),Task的数量决定决定并行度,同时也要考虑可用的cores

 2.6 TaskSet

  保存同一种计算逻辑多个Task的集合,一个TaskSet中的Task计算逻辑都一样,计算的数据不一样

大数据学习day19-----spark02-------0 零碎知识点(分区,分区和分区器的区别) 1. RDD的使用(RDD的概念,特点,创建rdd的方式以及常见rdd的算子) 2.Spark中的一些重要概念的更多相关文章

  1. 大数据学习(一) | 初识 Hadoop

    作者: seriouszyx 首发地址:https://seriouszyx.top/ 代码均可在 Github 上找到(求Star) 最近想要了解一些前沿技术,不能一门心思眼中只有 web,因为我目 ...

  2. 大数据学习之 LINUX

    ##大数据学习 古斌6.6 01. linux系统的搭建: 选用 Contos 6.5 x64位系统 (CentOS-6.5-x86_64-minimal.iso) 我选择的为迷你版 模板机: bla ...

  3. 大数据学习day31------spark11-------1. Redis的安装和启动,2 redis客户端 3.Redis的数据类型 4. kafka(安装和常用命令)5.kafka java客户端

    1. Redis Redis是目前一个非常优秀的key-value存储系统(内存的NoSQL数据库).和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list ...

  4. 大数据学习day29-----spark09-------1. 练习: 统计店铺按月份的销售额和累计到该月的总销售额(SQL, DSL,RDD) 2. 分组topN的实现(row_number(), rank(), dense_rank()方法的区别)3. spark自定义函数-UDF

    1. 练习 数据: (1)需求1:统计有过连续3天以上销售的店铺有哪些,并且计算出连续三天以上的销售额 第一步:将每天的金额求和(同一天可能会有多个订单) SELECT sid,dt,SUM(mone ...

  5. 大数据学习系列之七 ----- Hadoop+Spark+Zookeeper+HBase+Hive集群搭建 图文详解

    引言 在之前的大数据学习系列中,搭建了Hadoop+Spark+HBase+Hive 环境以及一些测试.其实要说的话,我开始学习大数据的时候,搭建的就是集群,并不是单机模式和伪分布式.至于为什么先写单 ...

  6. 大数据学习系列之九---- Hive整合Spark和HBase以及相关测试

    前言 在之前的大数据学习系列之七 ----- Hadoop+Spark+Zookeeper+HBase+Hive集群搭建 中介绍了集群的环境搭建,但是在使用hive进行数据查询的时候会非常的慢,因为h ...

  7. 大数据学习之Linux进阶02

    大数据学习之Linux进阶 1-> 配置IP 1)修改配置文件 vi /sysconfig/network-scripts/ifcfg-eno16777736 2)注释掉dhcp #BOOTPR ...

  8. 大数据学习系列之—HBASE

    hadoop生态系统 zookeeper负责协调 hbase必须依赖zookeeper flume 日志工具 sqoop 负责 hdfs dbms 数据转换 数据到关系型数据库转换 大数据学习群119 ...

  9. 大数据学习之Hadoop快速入门

    1.Hadoop生态概况 Hadoop是一个由Apache基金会所开发的分布式系统集成架构,用户可以在不了解分布式底层细节情况下,开发分布式程序,充分利用集群的威力来进行高速运算与存储,具有可靠.高效 ...

随机推荐

  1. SPOJ GSS8 - Can you answer these queries VIII | 平衡树

    题目链接 这一道题的修改操作用平衡树都很容易实现,难处理的是询问操作. 要想解决询问操作,只要知道如何在平衡树上快速合并左右两个区间的答案即可. 设$Ans_{[l,r]}^k=\sum\limits ...

  2. cf13A Numbers(,,)

    题意: Little Petya likes numbers a lot. He found that number 123 in base 16 consists of two digits: th ...

  3. Java中Lambda表达式的进化之路

    Lambda表达式的进化之路 为什么要使用Lambda表达式 可以简洁代码,提高代码的可读性 可以避免匿名内部类定义过多导致逻辑紊乱 在原先实现接口抽象方法的时候,需要通过定义一个实现接口的外部类来实 ...

  4. Node.js躬行记(14)——压力测试

    公司有个匿名聊天的常规H5界面,运营向做一次 50W 的推送,为了能配合她的计划,需要对该界面做一次压力测试. 一.JMeter 压测工具选择了JMeter,这是Apache的一个项目,它是用Java ...

  5. prometheus(7)之数据类型与PromQL语法

    Prometheus的四种数据类型 counter (只增不减 计数) Gauge (常规数值 可变化大小) histogram (柱状图 小于上边界的 总数与次数) summary (分位数  小于 ...

  6. Git+windows安装教程(一)

    一:Git是什么? Git是目前世界上最先进的分布式版本控制系统. 二:SVN与Git的最主要的区别? SVN是集中式版本控制系统,版本库是集中放在中央服务器的,而干活的时候,用的都是自己的电脑,所以 ...

  7. Qt5 项目程序打包发布 详细教程

    概述 当我们用QT写好了一个软件,要把你的程序分享出去的时候,不可能把编译的目录拷贝给别人去运行.编译好的程序应该是一个主程序,加一些资源文件,再加一些动态链接库,高大上一些的还可以做一个安装文件. ...

  8. MySQL高级篇 | MySQL逻辑架构

    思维导图 架构逻辑视图 每个虚线框为一层,总共三层. 第一层:连接层,所包含的服务并不是MySQL所独有的技术.它们都是服务于C/S程序或者是这些程序所需要的 :连接处理,身份验证,安全性等等. 第二 ...

  9. 问题 D: 某种序列

    题目描述 数列A满足An = An-1 + An-2 + An-3, n >= 3  编写程序,给定A0, A1 和 A2, 计算A99 输入 输入包含多行数据  每行数据包含3个整数A0, A ...

  10. 【JAVA】笔记(7)--- 数组精讲

    数组的静态初始化: 1.一维数组: int [ ] arr = { 1,2,3,4 } ; Object [ ] arr = {   new Object ( ) , new Object ( ) , ...