Spark初学笔记(二)
RDD(Resilient Distributed Datasets)
Spark围绕弹性分布式数据集(RDD)的概念展开,RDD是一个可以并行操作的容错的容错集合。创建RDD有两种方法:并行化 驱动程序中的现有集合,或引用外部存储系统中的数据集,例如共享文件系统,HDFS,HBase或提供Hadoop InputFormat的任何数据源。
RDD有两种类型算子:Transformation(转换)、Action(执行)。
Transformation
Transformation(转换)属于延迟计算,并不会触发计算,而是将一个RDD通过一种规则映射为另外一个RDD。
下表列出了Spark支持的部分Transformation。有关详细信息,请参阅RDD API文档并配对RDD函数。
Transformation | 描述 |
---|---|
map(func) | 返回通过函数func传递源的每个元素形成的新分布式数据集。 |
filter(func) | 返回通过选择func返回true 的源元素形成的新数据集。 |
intersection | 返回包含源数据集的相同元素的数据集。(交集) |
union | 对源RDD和参数RDD求并集后返回一个新的RDD(并集) |
distinct([numPartitions])) | 返回包含源数据集的不同元素的新数据集。(去重) |
flatMap(func) | 与map类似,但每个输入项可以映射到0个或更多输出项(因此func应该返回Seq而不是单个项)。 |
groupByKey([numPartitions]) | 在(K,V)对的数据集上调用时,返回(K,Iterable )对的数据集。(按照Key进行分组) |
reduceByKey(func, [numPartitions]) | 当调用(K,V)对的数据集时,返回(K,V)对的数据集,其中使用给定的reduce函数func聚合每个键的值,该函数必须是类型(V,V)=> V.同样groupByKey,reduce任务的数量可通过可选的第二个参数进行配置。 |
Action
Action(执行):触发Spark作业的运行,真正触发转换算子的计算。
下表列出了Spark支持的部分Action。请参阅RDD API文档
Action | 描述 |
---|---|
reduce(func) | 使用函数func(它接受两个参数并返回一个)来聚合数据集的元素。该函数应该是可交换的和关联的,以便可以并行正确计算。 |
collect() | 在驱动程序中将数据集的所有元素作为数组返回。在过滤器或其他返回足够小的数据子集的操作之后,这通常很有用。 |
count() | 返回数据集中的元素数。 |
first() | 返回数据集的第一个元素(类似于take(1))。 |
take(n) | 返回包含数据集的前n个元素的数组。 |
takeSample | 返回一个数组,其中包含数据集的num个元素的随机样本,有或没有替换,可选地预先指定随机数生成器种子。 |
takeOrdered(n, [ordering]) | 使用自然顺序或自定义比较器返回RDD 的前n个元素。 |
saveAsTextFile | 将数据集的元素作为文本文件(或文本文件集)写入本地文件系统,HDFS或任何其他Hadoop支持的文件系统的给定目录中。Spark将在每个元素上调用toString,将其转换为文件中的一行文本。 |
countByKey() | 仅适用于类型(K,V)的RDD。返回(K,Int)对的散列映射,其中包含每个键的计数。 |
foreach(func) | 在数据集的每个元素上运行函数func。这通常用于副作用,例如更新累加器或与外部存储系统交互。 |
如何创建RDD
-
使用sc.parallelize方法,复制一个已创建的scala集合上的对象,实现并行操作。
val rdd1 = sc.parallelize(Array(1,2,3,4,5,6,7,8),4) sc.parallelize(data, num))
num指slices数目,指将数据集分为几份。如果不设定,Spark根据集群状况进行自动设定。
-
通过使用外部的数据源创建RDD。如:HDFS,本地文件等。
val rdd2=sc.textFile("hdfs://192.168.1.156:9000/input/data.txt") //HDFS文件 val rdd3 = sc.textFile("data.txt") //当前目录下文件
RDD缓存机制
默认情况下,RDD执行多次算子时,每执行一次算子操作都会从源头处计算,所以为了提高效率,我们将对多次使用的RDD进行持久化。(Spark根据设定的持久化策略,将RDD中的数据保存在内存或者磁盘上,当进行算子操作时,再从内存或者磁盘中取出进行操作,不必从源做起)。
RDD默认缓存在内存中。(内存运行速度更快,提高RDD效率)
可通过调用cache()和 persist() 对RDD进行持久化操作。示例如下:
def persist(): this.type = persist(StorageLevel.MEMORY_ONLY)
def cache(): this.type = persist()
cache和persist的区别:
cache其实是调用了persist方法,缓存策略为MEMORY_ONLY。而persist可以通过设置参数有多种缓存策略(缓存策略见下)。
存储级别 | 描述 |
---|---|
MEMORY_ONLY | 将RDD存储为JVM中的反序列化Java对象。如果内存大小不足,则某些分区将不会被缓存,并且这些没有被缓存的RDD每次需要时都会重新计算。这是默认级别。 |
MEMORY_AND_DISK | 将RDD存储为JVM中的反序列化Java对象。如果内存大小不足,将会存储到磁盘中,并在需要时从那里读取它们。 |
MEMORY_ONLY_SER (Java和Scala) | 将RDD存储为序列化 Java对象(每个分区一个字节数组)。这通常比反序列化对象更节省空间,特别是在使用 快速序列化器时,但读取CPU密集程度更高。 |
MEMORY_AND_DISK_SER (Java和Scala) | 与MEMORY_ONLY_SER类似,但将超出内存的数据溢出到磁盘,而不是每次需要时动态重新计算它们。 |
DISK_ONLY | 仅将RDD分区存储在磁盘上。 |
MEMORY_ONLY_2,MEMORY_AND_DISK_2等 | 与上面的级别相同,但复制两个群集节点上的每个分区。 |
OFF_HEAP | 与MEMORY_ONLY_SER类似,但将数据存储在 堆外内存中。这需要启用堆外内存。 |
StorageLevel 类的源码:
val NONE = new StorageLevel(false, false, false, false)
val DISK_ONLY = new StorageLevel(true, false, false, false)
val DISK_ONLY_2 = new StorageLevel(true, false, false, false, 2)
val MEMORY_ONLY = new StorageLevel(false, true, false, true)
val MEMORY_ONLY_2 = new StorageLevel(false, true, false, true, 2)
val MEMORY_ONLY_SER = new StorageLevel(false, true, false, false)
val MEMORY_ONLY_SER_2 = new StorageLevel(false, true, false, false, 2)
val MEMORY_AND_DISK = new StorageLevel(true, true, false, true)
val MEMORY_AND_DISK_2 = new StorageLevel(true, true, false, true, 2)
val MEMORY_AND_DISK_SER = new StorageLevel(true, true, false, false)
val MEMORY_AND_DISK_SER_2 = new StorageLevel(true, true, false, false, 2)
val OFF_HEAP = new StorageLevel(true, true, true, false, 1)
StorageLevel( [ user_Disk ], [ userMemory ], [ useOffHeap ] , [ deserialized ], [ replication ] )
//[user_Disk]:Boolean 是否使用用户磁盘
//[user_Memory]:Boolean 是否使用用户内存
//[userOffHeap]:Boolean 是否使用堆外内存
//[deserialized]:Boolean 反序列化
//[replication]:Int 备份数
RDD容错机制、怎么设置
RDD的容错机制是通过检查点(Checkpoint)来实现。
(1)复习检查点:HDFS中,合并元信息
Oracle中,会以最高优先级唤醒数据库写进程(DBWn),将内存中的脏数据写入 数据文件
(2)RDD的检查点:容错机制,辅助Lineage(血统)—> 整个计算的过程
如果lineage越长,出错的概率就越大。出错之后,从最近一次的检查点开始运行。
-
本地目录创建的RDD,需要将spark-shell运行在本地模式上
sc.setCheckpointDir("/root/tem/spark")//设置检查点目录
-
HDFS目录创建的RDD:需要将spark-shell或者任务运行在集群模式上
scala> sc.setCheckpointDir("hdfs://bigdata11:9000/spark/checkpoint")//设置hdfs的checkpoint的目录、执行后,hdfs里面会创建一个目录 scala> val rdd1 = sc.textFile("hdfs://bigdata11:9000/input/sales")//创建RDD scala> rdd1.checkpoint //checkpoint是一个transformation的算子、不会有数据流出 scala> rdd1.count //当你checkpoint执行成功了,那么前面所有的RDD依赖都会被销毁
RDD的依赖关系
-
窄依赖(Narrow Dependencies):每一个父RDD的分区最多被子RDD的一个分区使用。
子RDD分区:父RDD分区=1:1,或1:n (独生子女) -
宽依赖(Wide Dependencies):多个子RDD的分区会依赖同一个父RDD的分区。
子RDD分区:父RDD分区=n:1,或n:m (超生)
Spark SQL
Spark SQL is Apache Spark’s module for working with structured data.
(spark-sql是ApacheSark用于处理结构化数据的模块)
Spark SQL的特点:
- 支持多种数据源:Hive、RDD、Parquet、JSON、JDBC等。
- 拥有多种性能优化技术。
- 组件扩展性。
DataFrame
DataFrame(表):是Spark SQL对结构化数据的抽象集合、其表现形式是RDD。
-
通过case class定义表
(1)定义一个case class来代表Stu表的schema结构case class Stu(id:Int,name:String,gender:String,age:Int)
(2)导入emp.csv文件
val lines = sc.textFile("/root/temp/stu.csv").map(_.split(",")) //从指定的地址创建RDD
(3)生成一个表:DataFrame
//将RDD和case class关联 val stuRDD = stuRDD.map(x => Stu(x(0).toInt, x(1), x(2),x(3).toInt)) val StuDataFrame = stuRDD.toDF
-
通过SparkSession.createDataFrame()
val schema = StructType(List(StructField("id", DataTypes.IntegerType), StructField("name", DataTypes.StringType), StructField("gender",DataTypes.StringType), StructField("age", DataTypes.IntegerType))) val stuRDD = stuRDD.map(x => Stu(x(0).toInt, x(1), x(2),x(3).toInt)) val StuDataFrame = sqlContext.createDataFrame(rowRDD, schema)
-
直接读取一个具有格式的数据文件(例如:json文件)
val stuDF = spark.read.json(" ")
DSL语句
-
查看表内所有信息
StuDataFrame.show
-
根据指定姓名查找
StuDataFrame.select("name").show
-
查询学生信息: 学号 年龄+1
StuDataFrame.select($"id",$"age",$"age"+1).show
-
查询年龄大于15的学生
StuDataFrame.filter($"age" > 15).show
-
根据性别分组
StuDataFrame.groupBy($"gender").count.show
视图
-
只在当前会话中有效:StuDataFrame.createOrReplaceTempView(“emp”)
spark.sql("select * from emp").show
-
Global Temporay View 在全局范围都有效(不同的会话中)
df.createGlobalTempView("G_emp") spark.sql("select * from global_temp.G_emp").show
使用数据源
load和save 默认的数据源都是Parquet文件
(Parquet文件:是Spark SQL的Load函数默认的数据源,能把其他文件格式转成Parquet文件如:jason)
使用load函数加载数据,会自动生成DataFrame
load函数默认的数据源是Parquet文件
val StuDataFrame = spark.read.load("/root/training/spark-2.1.0-bin-hadoop2.6/examples/src/main/resources/stu.parquet")
StuDataFrame.show
scala> StuDataFrame.select($"name",$"telphone").write.save("/root/temp/result")
//查询用户的名字和电话号码,并保存
Streaming
Spark Streaming是核心Spark API的扩展,可实现实时数据流的可扩展,高吞吐量,容错流处理。数据可以从许多来源(如Kafka,Flume,Kinesis或TCP套接字)中提取,并且可以使用以高级函数表示的复杂算法进行处理map。
常见的流式处理框架
(1)Apache Storm
(2)Spark Streaming
(3)JStorm:阿里巴巴
(4)Flink