一、
1、spark是什么
spark是针对于大规模数据处理的统一分析引擎,它是基于内存计算框架,计算速度非常之快,但是它仅仅只是涉及到计算,并没有涉及到数据的存储,后期需要使用spark对接外部的数据源,比如hdfs。
2、spark四大特性
速度快
job的输出结果可以保存在内存
spark任务以线程的方式运行在进程中
易用性
可以快速去编写spark程序通过 java/scala/python/R/SQL等不同语言
通用性
一个==生态系统==,包含了很多模块,
sparksql:通过sql去开发spark程序做一些离线分析
sparkStreaming:主要是用来解决公司有实时计算的这种场景
Mlib:它封装了一些机器学习的算法库
Graphx:图计算
兼容性
spark程序就是一个计算逻辑程序,这个任务要运行就需要计算资源(内存、cpu、磁盘),
哪里可以给当前这个任务提供计算资源,就可以把spark程序提交到哪里去运行
standAlone(后期使用较多)
它是spark自带的独立运行模式,整个任务的资源分配由spark集群的老大Master负责
yarn(后期使用较多)
可以把spark程序提交到yarn中运行,整个任务的资源分配由yarn中的老大ResourceManager负责
mesos
它也是apache开源的一个类似于yarn的资源调度平台
3、简述spark与mapreduce的区别?
spark处理速度为什么比mapreduce要快
基于内存与磁盘
(1)mapreduce任务后期再计算的时候,每一个job的输出结果会落地到磁盘,后续有其他的job需要依赖于前面job的输出结果,这个时候就需要进行大量的磁盘io操作。性能就比较低。
(2)spark任务后期再计算的时候,job的输出结果可以保存在内存中,后续有其他的job需要依赖于前面job的输出结果,这个时候就直接从内存中获取得到,避免了磁盘io操作,性能比较高
对于spark程序和mapreduce程序都会产生shuffle阶段,在shuffle阶段中它们产生的数据都会落地到磁盘。
进程与线程
(1)mapreduce任务以进程的方式运行在yarn集群中,比如程序中有100个MapTask,一个task就需要一个进程,这些task要运行就需要开启100个进程。
(2)spark任务以线程的方式运行在进程中,比如程序中有100个MapTask,后期一个task就对应一个线程,这里就不在是进程,这些task需要运行,
这里可以极端一点:只需要开启1个进程,在这个进程中启动100个线程就可以了。进程中可以启动很多个线程,而开启一个进程与开启一个线程需要的时间和调度代价是不一样。 开启一个进程需要的时间远远大于开启一个线程。
二、
1、rdd的概念
RDD(Resilient Distributed Dataset)叫做弹性 分布式 数据集,是Spark中最基本的数据抽象,它代表一个不可变、可分区、里面的元素可并行计算的集合.
Dataset: 就是一个集合,存储很多数据.
Distributed:它内部的元素进行了分布式存储,方便于后期进行分布式计算.
Resilient: 表示弹性,rdd的数据是可以保存在内存或者是磁盘中.
2、rdd的五大属性
(1)A list of partitions
一个分区列表,数据集的基本组成单位。
(2)A function for computing each split
一个计算每个分区的函数
(3)A list of dependencies on other RDDs
一个rdd会依赖于其他多个rdd
通过lineage血统记录下rdd与rdd之间的依赖关系
好处
就在于后期某个rdd的部分分区数据丢失的时候,可以通过血统进行重新计算恢复得到
这也是spark任务自身的一个容错机制
(4)Optionally, a Partitioner for key-value RDDs (e.g. to say that the RDD is hash-partitioned)
(可选项)一个Partitioner,即RDD的分区函数
(5)Optionally, a list of preferred locations to compute each split on (e.g. block locations for an HDFS file)
一个列表,存储每个Partition的优先位置(可选项)
3、rdd的创建方式
1、通过已经存在的scala集合去构建
2、加载外部的数据源去构建
3、从已经存在的rdd进行转换生成一个新的rdd
4、rdd的算子操作分类
1、transformation(转换)
根据已经存在的rdd转换生成一个新的rdd, 它是延迟加载,它不会立即执行
map / flatMap / reduceByKey 等
2、action (动作)
它会真正触发任务的运行,将rdd的计算的结果数据返回给Driver端,或者是保存结果数据到外部存储介质中
collect / saveAsTextFile 等
5、RDD常见的算子操作说明
重点需要掌握
map / mapPartitions foreach / foreachPartition算子区别操作?
1) map / mapPartitions (transformation算子)
map:用于遍历RDD,将函数f应用于每一个元素,返回新的RDD
mapPartitions:用于遍历操作RDD中的每一个分区,返回生成一个新的RDD。
如果在映射的过程中,需要频繁创建额外的对象,使用mapPartitions要比map高效。
比如,将RDD中的所有数据通过JDBC连接写入数据库,如果使用map函数,可能要为每一个元素都创建一个connection,这样开销很大,如果使用mapPartitions,那么只需要针对每一个分区建立一个connection。
2) foreach / foreachPartition (action算子)
foreach: 用于遍历RDD, 将函数f应用于每一个元素,无返回值。
foreachPartition: 用于遍历操作RDD中的每一个分区,无返回值。:
一般使用mapPartitions或者foreachPartition算子比map和foreach更加高效,推荐使用。
3) coalesce/ repartition 算子
coalesce: 合并分区/减少分区 默认不shuffle
默认 coalesce 不能扩大分区数量。除非添加true的参数,或者使用repartition。
repartition: 重新分区, 有shuffle
repartition(numPartitions)其本质是调用了coalesce(numPartitions,true)方法, 第二个参数默认是true,表示会产生shuffle。
适用场景:
1、如果要shuffle,都用 repartition
2、不需要shuffle,仅仅是做分区的合并,coalesce
3、repartition常用于扩大分区。
三、
1、RDD的依赖关系
RDD和它依赖的父RDD的关系有两种不同的类型: 窄依赖(narrow dependency)和宽依赖(wide dependency)
窄依赖
窄依赖指的是每一个父RDD的Partition, 最多被子RDD的一个Partition使用,
所有的窄依赖不会产生shuffle: map/flatMap/filter/union等
总结:窄依赖我们形象的比喻为独生子女
宽依赖
宽依赖指的是多个子RDD的Partition, 会依赖同一个父RDD的Partition,
所有的宽依赖会产生shuffle: reduceByKey/sortByKey/groupBy/groupByKey/join等等
总结:宽依赖我们形象的比喻为超生
join分为宽依赖和窄依赖,如果RDD有相同的partitioner,那么将不会引起shuffle,这种join是窄依赖,反之就是宽依赖
Lineage(即血统)
RDD只支持粗粒度转换, 即只记录单个块上执行的单个操作。
将创建RDD的一系列Lineage(即血统)记录下来,以便恢复丢失的分区
RDD的Lineage会记录RDD的元数据信息和转换行为,lineage保存了RDD的依赖关系,当该RDD的部分分区数据丢失时,它可以根据这些信息来重新运算和恢复丢失的数据分区。
2、RDD的缓存机制
1、什么是rdd的缓存机制、好处是什么?
可以把一个rdd的数据缓存起来,后续有其他的job需要用到该rdd的结果数据,可以直接从缓存中获取得到,避免了重复计算。缓存是加快后续对该数据的访问操作。
2、如何对rdd设置缓存? cache和persist方法的区别是什么?
RDD通过persist方法或cache方法可以将前面的计算结果缓存。
但是并不是这两个方法被调用时立即缓存,而是触发后面的action时,该RDD将会被缓存在计算节点的内存中,并供后面重用。
3、什么时候设置缓存?
1、某个rdd的数据后期被使用了多次
公共rdd进行持久化,避免后续需要,再次重新计算,提升效率。
2、rdd的数据来之不易时
为了获取得到一个rdd的结果数据,经过了大量的算子操作或者是计算逻辑比较复杂
3、如何清除缓存?
1、自动清除 :一个application应用程序结束之后,对应的缓存数据也就自动清除
2、手动清除 :调用rdd的unpersist方法
四、
1、sparksql简介
Spark SQL is Apache Spark’s module for working with structured data.
SparkSQL是apache Spark用来处理结构化数据的一个模块
大数据技术宏观上进行分类:
(1)数据存储
HDFS HBASE
(2)数据计算
a. 离线计算
MR 、Hive 、RDD(spark-core)、sparksql
b. 实时计算
sparkStreaming 、Flink
2、sparksql特性
1、易整合
将SQL查询与Spark程序无缝混合
可以使用不同的语言进行代码开发(java、scala、python、R)
2、统一的数据源访问
以相同的方式连接到任何数据源
3、兼容hive
sparksql兼容hivesql
4、标准的数据库连接
支持标准的数据库连接JDBC或者ODBC
3、DataFrame简介
在Spark中,DataFrame是一种以RDD为基础的分布式数据集,类似于传统数据库的二维表格
DataFrame带有Schema元信息,即DataFrame所表示的二维表数据集的每一列都带有名称和类型,但底层做了更多的优化
4、DataFrame和RDD对比
RDD可以把它内部元素看成是一个java对象
DataFrame可以把内部是一个Row对象,它表示一行一行的数据
RDD
优点
1、编译时类型安全
开发会进行类型检查,在编译的时候及时发现错误
2、具有面向对象编程的风格
缺点
1、构建大量的java对象,占用了大量heap堆空间,导致频繁的GC
2、数据的序列化和反序列性能开销很大
DataFrame
DataFrame引入了schema元信息和off-heap(堆外内存)
优点
DataFrame引入了schema元信息,解决了rdd数据的序列化和反序列性能开销很大这个缺点。
DataFrame引入了off-heap,解决了rdd构建大量的java对象 占用了大量heap堆空间,导致频繁的GC这个缺点。
缺点
1、编译时类型不安全
2、不在具有面向对象编程的风格
5、DataFrame常用的操作
1、DSL风格语法 : spark自身提供了一套Api
/加载数据
val rdd1=sc.textFile("/person.txt").map(x=>x.split(" "))
//定义一个样例类
case class Person(id:String,name:String,age:Int)
//把rdd与样例类进行关联
val personRDD=rdd1.map(x=>Person(x(0),x(1),x(2).toInt))
//把rdd转换成DataFrame
val personDF=personRDD.toDF
//打印schema信息
personDF.printSchema
//展示数据
personDF.show
//查询指定的字段
personDF.select("name").show
personDF.select($"name").show
personDF.select(col("name").show
//实现age+1
personDF.select($"name",$"age",$"age"+1)).show
//实现age大于30过滤
personDF.filter($"age" > 30).show
//按照age分组统计次数
personDF.groupBy("age").count.show
//按照age分组统计次数降序
personDF.groupBy("age").count().sort($"count".desc)show
2、SQL风格语法
- 把dataFrame注册成一张表,通过sparkSession.sql(sql语句)操作该表数据
//DataFrame注册成表
personDF.createTempView("person")
//使用SparkSession调用sql方法统计查询
spark.sql("select * from person").show
spark.sql("select name from person").show
spark.sql("select name,age from person").show
spark.sql("select * from person where age >30").show
spark.sql("select count(*) from person where age >30").show
spark.sql("select age,count(*) from person group by age").show
spark.sql("select age,count(*) as count from person group by age").show
spark.sql("select * from person order by age desc").show
6、通过IDEA开发程序实现把RDD转换DataFrame
1、利用反射机制
事先可以确定DataFrame的schema信息
定义一个样例类,样例类中的属性,通过反射之后生成DataFrame的schema信息
2、通过StructType动态指定schema信息
事先不确定DataFrame的schema信息,在开发代码的过程中动态指定
其本质调用底层方法
//1、构建SparkSession对象
val spark: SparkSession = SparkSession.builder().appName("StructTypeSchema").master("local[2]").getOrCreate()
//2、获取sparkContext对象
val sc: SparkContext = spark.sparkContext
sc.setLogLevel("warn")
//3、读取文件数据
val data: RDD[Array[String]] = sc.textFile("E:\\person.txt").map(x=>x.split(" "))
//4、将rdd与Row对象进行关联
val rowRDD: RDD[Row] = data.map(x=>Row(x(0),x(1),x(2).toInt))
//5、指定dataFrame的schema信息
//这里指定的字段个数和类型必须要跟Row对象保持一致
val schema=StructType(
StructField("id",StringType)::
StructField("name",StringType)::
StructField("age",IntegerType)::Nil
)
val dataFrame: DataFrame = spark.createDataFrame(rowRDD,schema)
dataFrame.printSchema()
dataFrame.show()
dataFrame.createTempView("user")
spark.sql("select * from user").show()
spark.stop()
}
五、
1、sparksql操作hivesql
- 主要是理解sparksql的四大特性中的
- 第三点 sparksql兼容hivesql
- .enableHiveSupport() //-----开启对hive的支持
- 第三点 sparksql兼容hivesql
def main(args: Array[String]): Unit = {
//1、构建SparkSession对象
val spark: SparkSession = SparkSession.builder()
.appName("HiveSupport")
.master("local[2]")
.enableHiveSupport() //-----开启对hive的支持
.getOrCreate()
//2、直接使用sparkSession去操作hivesql语句
//2.1 创建一张hive表
spark.sql("create table people(id string,name string,age int) row format delimited fields terminated by ','")
//2.2 加载数据到hive表中
spark.sql("load data local inpath './data/kaikeba.txt' into table people ")
//2.3 查询
spark.sql("select * from people").show()
spark.stop()
}
}
-
2、sparksql操作jdbc数据源
- 1、sparksql通过 JDBC加载mysql表的数据
- 2、sparksql处理完成的数据,保存到mysql表中
//todo:通过sparksql把结果数据写入到mysql表中
object Data2Mysql {
def main(args: Array[String]): Unit = {
//1、创建SparkSession
val spark: SparkSession = SparkSession
.builder()
.appName("Data2Mysql")
.master("local[2]")
.getOrCreate()
//2、读取mysql表中数据
//2.1 定义url连接
val url="jdbc:mysql://node03:3306/spark"
//2.2 定义表名
val table="user"
//2.3 定义属性
val properties=new Properties()
properties.setProperty("user","root")
properties.setProperty("password","123456")
val mysqlDF: DataFrame = spark.read.jdbc(url,table,properties)
//把dataFrame注册成一张表
mysqlDF.createTempView("user")
//通过sparkSession调用sql方法
//需要统计经度和维度出现的人口总数大于1000的记录 保存到mysql表中
val result: DataFrame = spark.sql("select * from user where age > 30")
//保存结果数据到mysql表中
result.write.mode("append").jdbc(url,"kaikeba",properties)
//result.write.mode(args(0)).jdbc(url,args(1),properties
//mode:指定数据的插入模式
//overwrite: 表示覆盖,如果表不存在,事先帮我们创建
//append :表示追加, 如果表不存在,事先帮我们创建
//ignore :表示忽略,如果表事先存在,就不进行任何操作
//error :如果表事先存在就报错(默认选项)
//关闭
spark.stop()
}
}
打包—提交到集群
spark-submit \
--master spark://node01:7077 \
--class com.kaikeba.sql.Data2Mysql \
--executor-memory 1g \
--total-executor-cores 4 \
--driver-class-path /home/hadoop/mysql-connector-java-5.1.38.jar \
--jars /home/hadoop/mysql-connector-java-5.1.38.jar \
spark_class02-1.0-SNAPSHOT.jar \
append kaikeba
-
3、sparksql中自定义函数
- 自定义udf函数
- 核心代码
def main(args: Array[String]): Unit = {
//1、创建SparkSession
val sparkSession: SparkSession = SparkSession.builder().appName("SparkSQLFunction").master("local[2]").getOrCreate()
//2、构建数据源生成DataFrame
val dataFrame: DataFrame = sparkSession.read.text("E:\\data\\test_udf_data.txt")
//3、注册成表
dataFrame.createTempView("t_udf")
//4、实现自定义的UDF函数
//小写转大写
sparkSession.udf.register("low2Up",new UDF1[String,String]() {
override def call(t1: String): String = {
t1.toUpperCase
}
},StringType)
//大写转小写
sparkSession.udf.register("up2low",(x:String)=>x.toLowerCase)
//4、把数据文件中的单词统一转换成大小写
sparkSession.sql("select value from t_udf").show()
sparkSession.sql("select low2Up(value) from t_udf").show()
sparkSession.sql("select up2low(value) from t_udf").show()
sparkSession.stop()
}
4、sparksql整合hive
步骤
1、需要把hive安装目录下的配置文件hive-site.xml, 拷贝到每一个spark安装目录下对应的conf文件夹中
2、需要一个连接mysql驱动的jar包,拷贝到spark安装目录下对应的jars文件夹中
3、可以使用spark-sql脚本 后期执行sql相关的任务
使用方式
spark-sql \
--master spark://node01:7077 \
--executor-memory 1g \
--total-executor-cores 4 \
--conf spark.sql.warehouse.dir=hdfs://node01:8020/user/hive/warehouse
- 应用场景
#!/bin/sh
#定义sparksql提交脚本的头信息
SUBMITINFO="spark-sql --master spark://node01:7077 --executor-memory 1g --total-executor-cores 4 --conf spark.sql.warehouse.dir=hdfs://node01:8020/user/hive/warehouse"
#定义一个sql语句
SQL="select * from default.hive_source;"
#执行sql语句 类似于 hive -e sql语句
echo "$SUBMITINFO"
echo "$SQL"
$SUBMITINFO -e "$SQL"