Spark初学笔记(二)

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

  1. 使用sc.parallelize方法,复制一个已创建的scala集合上的对象,实现并行操作。

     val rdd1 = sc.parallelize(Array(1,2,3,4,5,6,7,8),4)
    
     sc.parallelize(data, num))
    

num指slices数目,指将数据集分为几份。如果不设定,Spark根据集群状况进行自动设定。

  1. 通过使用外部的数据源创建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越长,出错的概率就越大。出错之后,从最近一次的检查点开始运行。

  1. 本地目录创建的RDD,需要将spark-shell运行在本地模式上

     sc.setCheckpointDir("/root/tem/spark")//设置检查点目录
    
  2. 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的依赖关系

  1. 窄依赖(Narrow Dependencies):每一个父RDD的分区最多被子RDD的一个分区使用。
    子RDD分区:父RDD分区=1:1,或1:n (独生子女)

  2. 宽依赖(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的特点:

  1. 支持多种数据源:Hive、RDD、Parquet、JSON、JDBC等。
  2. 拥有多种性能优化技术。
  3. 组件扩展性。

DataFrame

DataFrame(表):是Spark SQL对结构化数据的抽象集合、其表现形式是RDD。

  1. 通过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
    
  2. 通过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)
    
  3. 直接读取一个具有格式的数据文件(例如:json文件)

    val stuDF = spark.read.json("  ")
    

DSL语句

  1. 查看表内所有信息

     StuDataFrame.show
    
  2. 根据指定姓名查找

     StuDataFrame.select("name").show
    
  3. 查询学生信息: 学号 年龄+1

     StuDataFrame.select($"id",$"age",$"age"+1).show
    
  4. 查询年龄大于15的学生

     StuDataFrame.filter($"age" > 15).show
    
  5. 根据性别分组

     StuDataFrame.groupBy($"gender").count.show
    

视图

  1. 只在当前会话中有效:StuDataFrame.createOrReplaceTempView(“emp”)

     spark.sql("select * from emp").show
    
  2. 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

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值