Spark重点总结
简介
Spark和MapReduce的功能差不多,主要做分布式计算的,而分布式存储还是由HDFS来做,其中Spark进行数据转换时最核心的概念就是RDD,既然是做分布式计算的,那就要搞懂
- Spark是怎么进行分布式计算的以及工作流程
- Spark各个模块解决的问题以及特点
- Spark RDD中API的使用场景
上面说了,spark进行分布式计算是基于HDFS的,所以不光要启动spark集群,还是要启动HDFS集群
//hdfs start-dfs.sh //spark sh ~/bigdata/spark-2.2.0-bin-hadoop2.7/sbin/start-all.sh //进入spark交互命令行 spark-shell --master spark://master:7077
查看一些spark任务的运行状态,可以到监控界面上查看http
:
//master:8080/
,监控界面更多详细信息阶段:戳这
重点
- RDD的特点
- RDD的Transformation API和Action API
- Spark依赖设计
- Spark的分区器
- Spark中stage划分
- 提交spark任务的运行模式以及Spark Job提交流程
- 延迟调度和动态资源管理
- Shuffle性能提升
- Api详细 一 二
1.RDD的特点
RDD是一个只读且分区的数据集
- 分区并行计算
- RDD分区计算都有自己的计算函数
- 维持RDD依赖列表,通过这个依赖列表进行容错
- 可以按照一定规则自行分区,这些在RDD分区元数据信息有体现
- 分区数据有机器存储地址,根据地址到对应的机器上计算,以实现计算任务的本地性
- spark的计算是流式计算,就是取一条计算一条,主要就是用Iterator来实现的
具体的RDD都会实现抽象接口中的这几个方法
- compute 计算分区的数据得到一个Iterator结果
- getPartitions 获取RDD的分区列表,用于并行计算
- getDependencies 获取依赖列表
- getPreferredLocations 获取分区数据存储在哪些机器上等位置信息,用于本地性计算
- partitioner 分区器,默认是没有指定的,可以自行指定分区器
2.TopN的实现:
单个TopN的实现:(1)两次mapPartition,第一次各个分区求TopN,第二次重分区为一个分区后,求这个分区的TopN (2)一次mapPartition + PriorityQueue,PriorityQueue这个东西,从翻译上看,就是优先级队列,它里面是用二叉堆实现的,这种数据结构有个特点,就是默认情况下,第一个 数是最小的,这个特点在维持topn数据用于判断新元素是否进队列;第一个mapPartition就是各分区维护一个TopN优先级队列,最后再把各个队列汇总到一个队列中 (3)使用 RDD的top接口,这个本质上就是用的第二种 方式
3.Spark SQL中,API演化的合理性
在Spark SQL操作中,它支持SQL、DataFrame、DataSet三种数据类型的操作方式,那它们有什么区别呢,那这个就要从错误检查角度来说明了,在执行SQL语句时,常见有语法错误和解析错误 两种错误,语法错误你就理解为,比如关键词select写错,而解析错误则是,比如表字段写错,对于这两种错误,如果是SQL字符串,那么都只在运行时才会发现,DataFrame由于提供DSL API,但是它不是强制类型,所以语法错误能在编译时发现,但发现不了解析错误,而DataSet是强制类型,所以这两种错误都能在编译时发现
Spark SQL重点总结:
- DataFrame和DataSet的创建
DataFrame的创建 主要有5种方式:前四种调用createDataFrame,可传入RDD[Product子类],Seq[Product子类],RDD[Java类]+classOf[Java类],RDD[Row]+schema,其中 case class和tuple都是Product子类,第五种方式就是读文件接口,默认就是DataFrame数据类型,比如read.json
DataSet的创建 主要有3种方式:第一种是spark.range默认返回的就是DataSet数据类型,另外两种调用createDataset,可传类型Seq[T]和RDD[T],由于JVM对象类型与spark SQL内部数据类型需要转换,在创建DataSet时,需要通过柯里化的形式传入Encoders,它就是负责这个转换动作的,那么这个柯里化参数是一个隐式参数,对于T如果是常用的数据类型就可以不用传,那么通过隐式转换import spark.implicits._ 就可以,而对于自定义的类(比如Java类),需要自己通过柯里化的方式传入Encoders.bean(classOf[类])
RDD和DF和DS三者互转 : 1.转RDD -> 两种数据类型调用.rdd 2.转DF -> rdd和ds都是调用.toDF 3.转DS -> rdd.toDS和df.as[类型],三种数据结构的特点比较:RDD和DS都是强类型数据结构,而DF和DS都有schema信息(表头等信息),所以在由其他转成DF数据,数据类型丢失,所以DF转DS需要给定类型,而DF转RDD,则是默认成Row类型,而不是原来类型,另外由其他转成RDD,schema信息会丢失,但是RDD转成这两种数据不用传schema,是因为会根据RDD的特点创建默认的schema,rdd调用转换方法时,需要隐式转换 其他注意的
2.Spark SQL支持的外部数据源
Spark SQL内置支持的数据源就有parquet,orc,json,csv,text,jdbc,table(hive) 代码详情
在未指定文件格式,默认是parquet,指定文件格式是.format,当然也有特定格式的api,比如.json
其中JDBC读取数据库的方式要重点说一下,在启动spark-shell时,需要注意spark的jar目录要有数据库的驱动包或者通过--jars指定,在进行读写操作时,有细节需要注意
(1)写:创建表时默认的引擎不是Innodb;schema翻译成数据库字段的类型 不符合自己预期;设置事务隔离级别和批量写入;覆写表时的两种方式(是先删还是先清空),这些都可以通过option来设置
(2)读:默认读是读到一个分区里,可以通过四个参数 -> 分区数,分区字段,上界,下界来划定多个分区,这四个参数可以通过option传入,也可以通过jdbc读接口传入,另外jdbc读接口支持传入一个条件列表来划定分区 ,另外 读 也可以设置批量读
(3) 不管读还是写,都会有分区的概念,读的时候是numPartitions参数决定的,写则是df的分区数决定的,不管读还是写,分区数不宜过多,因为每个分区都会打开一个jdbc连接,连接一多容易搞垮数据库,写可以df的coalease接口来减少分区数
(4)使用partitionBy提升性能 在写操作时通过partitionBy(分区字段名...)进行存储,数据就会按照分区字段层级建好目录进行存储,目录形式如year=2018,这样分区了的数据,使用分区目录的父目录进行读,照样可以读取出来,而schema没变,这种情况使用分区字段过滤,效率比较高(因为不用全表扫描),sql形式的过滤,分区字段会在表中显示,如果是在读取就读某个分区目录,那么分区字段不会在表中显示(因为目录路径上有体现),最后除了partitionBy可以提高性能,还可以在这分区基础上进行分bucketBy,不过此功能只能用在hive表中
3.Spark SQL Column
使用细节 :Spark SQL中DataFrame数据结构提供了DSL API让我们可以像操作表一样操作数据,那这里就设计很重要的一个概念,就是列,如果要创建列对象有三种主要的方式:1.DF.col(列名),当然可以简写成DF(列名),当然select可以直接传入字符串形式的列名,这个本质还是调用DF.col(列名) 2.使用 $"列名",这个要使用隐式转换,import spark.implicits._ 3.使用col(列名)或者column(列名)或者函数表达式,这里也需要隐式转换,import org.apache.spark.sql.functions._
untype API 和 type API :通俗来说,就是没有类型,说就是DataFrame数据结构,它没有明确类型,untype API就是说DF的API,type API就是说的DataSet这种强类型数据结构的API,其中他们两者col对应Column和TypedColumn,Column => TypedColumn: col(列名).as[类型] TypedColumn => Column:typeCol.as(列名)
4.Spark SQL group分组
使用 :关注agg和pivot,agg可以同时进行多个列表操作,pivot把某一列的值转成列,有多少值就有多少列
5.Spark SQL join关联
使用 : 主要API关注join和joinWith,其中join中DF数据结构连接字段可以用字符串列,也可以用列对象的===方法,不管是两个DataSet还是DataFrame join操作返回的都是DataFrame[Row],而joinWith会返回一个每个表行值的二元组,并连接字段只能用列对象的===方法
问题:
- 什么代码是在Driver端执行,什么代码在Executor端执行?
一般初始化动作,比如sc和配置信息以及参数,还有就是生成RDD链在Driver,而Task运行,就是分区数据计算就是在Executor上
- 一个Job有多少个Task?
Task的数量决定了job程序运行时的并行度,而Job的Task数量主要由两个方面决定,一个就是Stage的划分,一个就是是否指定分区器(未完。。。。。)
答卷: