该部分分为两篇,分别介绍RDD与Dataset/DataFrame:

一、RDD

二、DataSet/DataFrame

先来看下官网对RDD、DataSet、DataFrame的解释:

1.RDD

Resilient distributed dataset(RDD),which is a fault-tolerant collection of elements that can be operated on in parallel

RDD——弹性分布式数据集,分布在集群的各个结点上具有容错性的元素集,可以被并行处理。

参考链接:http://spark.apache.org/docs/latest/rdd-programming-guide.html#resilient-distributed-datasets-rdds

2. DataSet & DataFrame

A Dataset is a distributed collection of data.

DataSet——分布式数据集。

A DataFrame is a Dataset organized into named columns.

DataFrame——按列命名的分布式数据集。
 
API文档中可以看到,DataFrame其实就是指定了元素类型为Row的DataSet。而Row类型即具有确定元素个数的行结构。可以看出DataFrame其实是类似于数据库的表结构,后面可以看到对它的操作也和表的操作很类似。

type DataFrame = DataSet[Row]

 
官网更鼓励使用DataSet/DataFrame,见下文:

Note that, before Spark 2.0, the main programming interface of Spark was the Resilient Distributed Dataset (RDD). After Spark 2.0, RDDs are replaced by Dataset, which is strongly-typed like an RDD, but with richer optimizations under the hood. The RDD interface is still supported, and you can get a more complete reference at the RDD programming guide. However, we highly recommend you to switch to use Dataset, which has better performance than RDD. See the SQL programming guide to get more information about Dataset.

可以看到Spark2.0以后,DataSet取代了RDD,并具有更高的性能(其中一点便是DataSet支持sql(如select、join、union、groupBy等)操作,可以像操作数据库表/视图似的来进行数据处理)。

参考链接:http://spark.apache.org/docs/latest/quick-start.html

当然,有的场景RDD比DataSet/DataFrame更方便数据处理,比如有个数据集,每行包含很多字段,但是我们只需要获取其中的某几个字段,如果用DataSet/DataFrame,必须定义所有字段的结构,但是,如果使用RDD进行处理,直接获取每行的指定字段即可,不需要关心其他字段,后续对特定字段的操作再转换为DataSet/DataFrame处理即可,可见,RDD和DataSet结合使用有时候更方便数据数据。

下面分别对RDD、DataSet、DataFrame的使用方法进行介绍。

一.RDD

1.1 RDD操作类型

RDD操作主要分为两类:Transformations与Actions。官方将Transformations操作定义为从一个数据集中生成另一个数据集;将Actions操作定义为对数据集进行一系列计算以后返回给驱动程序一个值。可以看出数据转换(map)、合并(union)、过滤(filter)等操作均为Transformations类型,因为他们的结果仍然是一个数据集,而数据聚合(reduce)、统计(count)、保存(saveAsXXX)等操作均为Actions类型,原因是他们的最终都要将结果返回给驱动程序(即对结果进行汇总,而Transformations操作只需要在各个node/slave上执行)。

之所以要区分操作类型,是因为Transformations操作是滞后的,不会马上执行,只有当程序要返回结果给驱动程序时才会执行,所以定义了Transformations操作后立马执行println来输出某个值是得不到结果的,只有执行过Actions操作才能得到结算结果,且Actions操作会被立即执行。

官方列出的常用Transformations操作包括:map、filter、flatMap、mapPartitions、mapPartitionsWithIndex、sample、union、intersection、distinct、groupByKey、reduceBykey、aggregateByKey、sortByKey、join、cogroup、cartesian、pipe、coalsce、repartition、repartitionAndSortWithinPartitions;Actions操作包括:reduce、collect、count、first、take、takeSample、takeOrdered、saveAsTextFile、saveAsSequenceFile、saveAsObjectFile、countByKey、foreach。具体用法可以参考官方API

1.2 生成RDD

我们可以通过SparkContext来生成RDD,下面是两种获取SparkContext实例的方法。

//1.SparkContext
val sc = new SparkContext(new SparkConf().setAppName("Spark Context"))
val rdd1 = sc.textFile("data.txt") //2.SparkSession
val spark = SparkSession.builder().appName("Spark Session").getOrCreate()
val rdd2 = spark.sparkContext.textFile("data.txt")

上例中通过textFile读取本地文件来生成RDD,textFile参数可以是HDFS路径、本地文件(非单机模式下需要每个node上都有)或者任何hadoop文件系统支持的URI;除了textFile还可以使用hadoopFile、hadoopRDD、parallelize、makeRDD来生成RDD。这里提一下,textFile支持通配符形式的path,比如hdfs://xx.xx.xx.xx:9000/path1/ds=*/*.gz,特别适用于按分区存储的数据处理。

1.3 RDD处理

下面通过一个例子演示一下RDD常用操作的用法:

下面的代码对保存在HDFS上的日志文件进行解析、过滤、统计Title字段的字节数并计算Title的最大长度。

package com.personal.test

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.storage.StorageLevel object RDDTest {
def main(args: Array[String]): Unit = {
val MinFieldsLength = 53
val VTitleIndex = 11 val inputPath = "hdfs://192.168.1.44:9000/user/name/input/attempt_1530774346064"
val outputPath = "hdfs://192.68.1.44:9000/user/name/output/" val sparkConf = new SparkConf().setAppName("RDD Test")
val sc = new SparkContext(sparkConf)
val rdd = sc.textFile(inputPath) val lineCounter = sc.longAccumulator("LineCounter") val resultRdd = rdd.map(_.split("\t"))
.filter(
fields =>{
lineCounter.add(1)
if(fields.length < MinFieldsLength) false else true
}
)
.map(fields => fields(VTitleIndex).length)
.persist(StorageLevel.MEMORY_ONLY) resultRdd.saveAsTextFile(outputPath)
val maxTitleLength = resultRdd.reduce((a, b) => if (a>b) a else b) println(s"Line count: ${lineCounter.value}")
println(s"Max title length: ${maxTitleLength}") sc.stop()
}
}

例中先初始化一个SparkContext对象,然后通过textFile读取hdfs中的文件,生成一个RDD,接着调用map逐行分割字符串,再调用filter对字段数不合法的行进行过滤,接着再计算每行的Title字段长度并写入hdfs,同时使用reduce计算Title的最大长度,最后输出统计信息。根据RDD操作类型定义,文中调用map->filter->map的过程是不会马上被执行的,直到调用saveAsTextFile和reduce时才会被执行。

上例中用到了一个特殊的变量——累加器(Accumulator),通过SparkContext.longAccumulator(name: String)定义,顾名思义,只能进行加法操作,用于计数器或求总和。这类变量在Spark中称为共享变量(Shared Variables),即在集群的各个node中的值是相同的),与共享变量相反,程序中定义的其他变量在集群的各个node之间是互相独立的

除了计数器,Spark还支持另一种共享变量——广播变量(Broadcast Variables),它是只读的,被cache到每台机器中,常用于各个node之间的大规模数据分发。Spark任务是分阶段执行的,各个阶段需要的数据便是通过broadcast方式分发的,cache时进行序列化,任务执行时再反序列化。因此,只有在各个阶段需要同一份数据或需要cache反序列化后的值时才需要显式定义broadcast变量,通过调用SparkContext.broadcast(value: T)来定义。

org.apache.spark.rdd.RDD.map原型为:

def map[U](f: (T) ⇒ U)(implicit arg0: ClassTag[U]): RDD[U]
      Return a new RDD by applying a function to all elements of this RDD.

可以看到,map是一个高阶函数,即参数也是一个函数;第二个为隐式参数,不需要显示赋值(需要初始化spark后"import spark.implicits._"),程序会根据上下文自动赋值。map常用于对数据逐行处理,返回值是个新的RDD,处理后的结果数不变。如上例中:

val resultRdd = rdd.map(_.split("\t"))

org.apache.spark.rdd.RDD.filter原型为:

def filter(f: (T) ⇒ Boolean): RDD[T]
      Return a new RDD containing only the elements that satisfy a predicate.

同map一样,filter也是一个高阶函数,函数返回值为true时保留该数据,为false时过滤掉该数据。如上例中:

val resultRdd = rdd.map(_.split("\t"))
.filter(
fields =>{
lineCounter.add(1)
if(fields.length < MinFieldsLength) false else true
}
)

org.apache.spark.rdd.RDD.saveAsTextFile原型为:

def saveAsTextFile(path: String, codec: Class[_ <: CompressionCodec]): Unit
      Save this RDD as a compressed text file, using string representations of elements.

def saveAsTextFile(path: String): Unit
      Save this RDD as a text file, using string representations of elements.

saveAsTextFile为Actions类型的方法,用于将rdd结果以text格式持久化到指定path下,写入的时候会检查是否path已经存在,存在则抛出异常。第二个参数用于指定压缩类型,如org.apache.hadoop.io.compress.GzipCodec、com.hadoop.compression.lzo.LzoCodec,默认不压缩。如上例中:

resultRdd.saveAsTextFile(outputPath)

org.apache.spark.rdd.RDD.reduce原型为:

def reduce(f: (T, T) ⇒ T): T
      Reduces the elements of this RDD using the specified commutative and associative binary operator.

reduce的参数为同一类型的二元操作函数,即“T <operator> T”,可用于求最值,求和等聚合需求。如上例中:

val maxTitleLength = resultRdd.reduce((a, b) => if (a>b) a else b)

上例中还用到了一个Spark中很重要的功能——持久化(Persistence),它将RDD持久化/缓存到各个node的内存中以加速后续的计算。可以通过调用persist() 或 cache()来使RDD持久化,cache的存储方式是反序列化后写入内存,persist的存储方式(StorageLevel)可以通过参数指定,不指定参数等同于cache,可选的存储方式包括:

类型 说明
MEMORY_ONLY 将RDD反序列化为java objects写入JVM。若无法完全写入内存,则部分partiton内的数据将在需要的时候重新计算。
MEMORY_AND_DISK 将RDD反序列化为java objects写入JVM。若无法完全写入内存,则无法写入内存的写入磁盘,需要的时候从磁盘读取。
MEMORY_ONLY_SER 将RDD序列化为java objects写入JVM。
MEMORY_AND_DISK_SER 将RDD序列化为java objects写入JVM。若无法完全写入,则无法写入内存的部分写入磁盘。
DISK_ONLY 将RDD写入磁盘
MEMORY_ONLY_2
MEMORY_AND_DISK_2
与MEMORY_ONLY、MEMORY_AND_DISK类似,但每个partition会备份到两个nodes中。
OFF_HEAP 与MEMORY_ONLY_SER类似,但是会写入堆外内存(off-heap memory),前提是启用了堆外内存。

其他操作可以查阅API文档或其他资料,这里不再举例。

Spark提高篇——RDD/DataSet/DataFrame(一)的更多相关文章

  1. Spark提高篇——RDD/DataSet/DataFrame(二)

    该部分分为两篇,分别介绍RDD与Dataset/DataFrame: 一.RDD 二.DataSet/DataFrame 该篇主要介绍DataSet与DataFrame. 一.生成DataFrame ...

  2. RDD&Dataset&DataFrame

    Dataset创建 object DatasetCreation { def main(args: Array[String]): Unit = { val spark = SparkSession ...

  3. Spark SQL 之 RDD、DataFrame 和 Dataset 如何选择

    引言 Apache Spark 2.2 以及以上版本提供的三种 API - RDD.DataFrame 和 Dataset,它们都可以实现很多相同的数据处理,它们之间的性能差异如何,在什么情况下该选用 ...

  4. RDD/Dataset/DataFrame互转

    1.RDD -> Dataset val ds = rdd.toDS() 2.RDD -> DataFrame val df = spark.read.json(rdd) 3.Datase ...

  5. Spark中的RDD和DataFrame

    什么是DataFrame 在Spark中,DataFrame是一种以RDD为基础的分布式数据集,类似于传统数据库中的二维表格. RDD和DataFrame的区别 DataFrame与RDD的主要区别在 ...

  6. Spark 论文篇-RDD:一种为内存化集群计算设计的容错抽象(中英双语)

    论文内容: 待整理 参考文献: Resilient Distributed Datasets: A Fault-Tolerant Abstraction for In-Memory Cluster C ...

  7. spark SQL、RDD、Dataframe总结

  8. 谈谈RDD、DataFrame、Dataset的区别和各自的优势

    在spark中,RDD.DataFrame.Dataset是最常用的数据类型,本博文给出笔者在使用的过程中体会到的区别和各自的优势 共性: 1.RDD.DataFrame.Dataset全都是spar ...

  9. Spark RDD、DataFrame原理及操作详解

    RDD是什么? RDD (resilientdistributed dataset),指的是一个只读的,可分区的分布式数据集,这个数据集的全部或部分可以缓存在内存中,在多次计算间重用. RDD内部可以 ...

随机推荐

  1. explicit_defaults_for_timestamp引发的狗血剧情

    今天就碰到了一个较初级的问题,居然为找这个参数花了好半天时间,深以为不齿.需求是这样的,有个表的某个字段需要从datetime改成timestamp类型.原结构如下:create table tmp1 ...

  2. UML model refactoring: a systematic literature review

    一.基本信息 标题:UML model refactoring: a systematic literature review 时间:2015 出版源:Empirical Software Engin ...

  3. 【repost】 JS变量重复声明以及忽略var 声明的问题及其背后的原理

    JS的容错率很高,一些其他语言常见的小错误JS都能大度得包容,比如给一个方法传入超出预计的参数.在声明变量之前使用该变量(变量的声明提升解决了这个问题)等等,这里我们就要解剖一下JS变量重复声明以及当 ...

  4. 在ASP.NET MVC里对Web Page网页进行权限控制

    我们在ASP.NET MVC开发时,有时候还是得设计ASP.NET的Web Page网页(.aspx和.aspx.cs),来实现一些ASP.NET MVC无法实现的功能,如此篇<Visual S ...

  5. 【npm】伙计,给我来一杯package.json!不加糖

    前言:夜深了,我熬了一锅热气腾腾的package.json,给大家端上来,希望大家喜欢 json和JS对象的区别 package.json,顾名思义,它是一个json文件,而不能写入JS对象. 所以我 ...

  6. 初探系列 — Pharbers用于单点登录的权限架构

    一. 前言 就职公司 法伯科技是一家以数据科技为驱动, 专注于医药健康领域的循证咨询公司. 以数据科学家身份, 赋能医药行业. 让每位客户都能享受数据带来的价值, 洞察业务, 不止于数据, 让决策更精 ...

  7. Javascript高级编程学习笔记(73)—— 表单(1)表单基础

    表单 JS最初的一个用途就是帮助服务器分担处理表单的责任 时至今日,虽然web应用以及JS都有了长足的发展,但是表单依然是现在web应用中比较重要的部分. 因为默认的表单控件很丑,所以有时候我们会使用 ...

  8. python写的计算器

    import tkinter #导入tkinter模块 root = tkinter.Tk()root.minsize(280,500)root.title('xx的计算器') #1.界面布局#显示面 ...

  9. 微信小程序合法域名配置-不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书

    微信小程序合法域名配置-不校验合法域名.web-view(业务域名).TLS 版本以及 HTTPS 证书 很多教程说按照以上方式调用即可.但是当我们在程序中实际调用以上程序时,就会报错, http:/ ...

  10. hbase之createTable完整的netty实现执行流程

    hbase的客户端代码并不想hive一样用java编写,shell调用,而是使用ruby编写. 在admin.rb文件中方法create,其中接受两个参数,其中第二个参数类型为变长参数. 而在crea ...