spark教程

目录

目录

一 spark设计与运行原理

1 Spark简介

2 Spark特点

 3 Spark生态

二 Spark的安装与运行

2 运行实例

3 Spark的交互式操作入门

4 更多的交互式操作

三 RDD的设计与原理

1 RDD编程基础

1.1 初始化Spark

1.2 使用shell

1.3 创建RDD

1.4 RDD操作

1.3 行动操作

1.4 持久化

1.5 分区

1.6 键值对RDD

1.7 常用的键值对转换操作


一 spark设计与运行原理


1 Spark简介


Spark是由美国加州大学伯克利分校AMP实验室于2009年开发,是基于内存计算的大数据并行框架,可用于构建大型的、低延时的数据分析应用程序。2013年成为Apache下的开源项目。Apache Spark是一种快速通用的集群计算系统。它提供Java,Scala,Python和R中的高级API,以及支持通用执行图的优化引擎。它还支持一组丰富的更高级别的工具,包括Spark SQL用于SQL和结构化数据的处理,MLlib机器学习,GraphX用于图形处理和Spark Streaming。


2 Spark特点

 

  • 运行速度快:Spark主要使用先进的有向无环图DAG执行引擎,以支持循环数据流和内存计算,基于内存的执行速度比Hadoop MapReduce快上百倍,基于磁盘的执行速度也能快十倍。
  • 容易使用:Spark支持Scala、Java、R和Python等语言,简洁的API设计有助于用户轻松构建并行程序,并且可以通过Spark Shell进行交互式编程。
  •  通用性:Spark提供了完整而强大的技术栈,包括SQL查询、流式计算、机器学习和图算法等组件,这些组件可以无缝整合在同一个应用中,足以应对复杂的计算。
  • 运行模式多样:Spark可以运行在独立的集群模式中,也可以运行于Hadoop上,并且可以访问HDFS、HBase、Cassandra和Hive等数据源。

 3 Spark生态


大数据处理类型

  •  复杂的批量数据处理:时间跨度通常在数十分钟到数小时之间(Hadoop MapReduce);
  • 基于历史数据的交互式查询:时间跨度通常在数十秒到数分钟之间(Impala,Hive);
  • 基于实时数据流的数据处理:时间跨度通常在数百毫秒到数秒之间(Storm,Spark stream)。

大数据框架存在的一些问题

  • 不同软件产生的数据格式不一样,因此无法做到数据无缝连接,需要进行格式转换;
  • 不同软件需要有不同的开发人员和维护人员,成本比较高;
  • 难以对同一个集群中各个系统进行统一资源调配。

Spark的设计遵循了“一个软件栈满足不同应用场景”的理念,逐渐形成了一套完整的既能提供内存计算框架,也可以支持SQL即席查询、实时流式计算、机器学习和图计算。Spark可以部署在Yarn之上,同时支持批处理、交互式查询和流数据处理。

Spark已经成为伯克利数据分析软件栈BDAS的重要组成部分。
                    

                                                                             图1 BDAS架构

Spark组件包含Spark Core、Spark SQL、Spark Streaming、MLlib和GraphX等。其功能具体如下:

  • Spark Core:它包含Spark最基础和最核心的功能,如内存计算、任务调度、部署模式和故障恢复、存储管理等,主要面向批数据处理。Spark Core建立在统一的抽象RDD之上,使其可以以一致的方式应对不同的大数据处理场景;需要注意的是,Spark Core通常被简称为Spark。
  •  Spark SQL:Spark SQL是用于结构化数据处理的组件,允许开发人员直接处理RDD,同时也可查询Hive、Hbase等外部数据源。Spark SQL的一个重要特点是其能够统一处理关系表和RDD,使得开发人员不需要自己编写Spark应用程序,开发人员可以轻松地使用SQL命令进行查询,并进行复杂的数据分析。
  • Spark Streaming:Spark Streaming是一种流计算框架,可以支持高吞吐量,可容错处理的实时流数据处理,其核心思路就是将流数据分解成一系列短小的批处理作业,每个短小的批处理作业可以使用Spark Core进行快速处理。Spark Streaming支持多种数据输入源,如Kafka、Flume和Tcp套接字等。
  • MLlib(机器学习):MLlib提供了常用机器学习算法的实现,包括聚类、分类、回归、协同过滤等,降低了机器学习的门槛,开发人员只要具备一定的理论知识就能进行机器学习方面的工作。
  • GraphX(图计算):GraphX是Spark中用于图计算的API,可以认为是Pregel在Spark上的重写和优化,GraphX性能良好,拥有丰富的功能和运算符,能在海量数据上自如的运行复杂的图算法。

二 Spark的安装与运行


2 运行实例


Spark附带了几个示例程序。Scala,Java,Python和R示例都在 examples/src/main目录中。要运行其中一个Java或Scala示例程序,请 bin/run-example <class> [params]在顶级Spark目录中使用。(在幕后,这将调用用于启动应用程序的更通用的 spark-submit脚本)。例如,

./bin/run-example SparkPi 10


您还可以通过Scala shell的修改版本以交互方式运行Spark。这是学习框架的好方法:

./bin/spark-shell --master local[2]


该--master选项指定分布式集群的URL,或者local使用一个线程local[N]在本地运行,或者使用N个线程在本地运行。您应该从使用local测试开始 。有关选项的完整列表,请使用该--help选项运行Spark shell 。

./bin/spark-shell --master spark://hostname:7077


--master参数指master-url,它表明的Spark的运行模式,值可以为如下所示:

  •  local 使用一个Worker线程本地化运行Spark\
  •  local[*] 使用与逻辑CPU个数相同数量的线程来本地化运行Spark(逻辑Cpu个数等于物理Cpu个数乘以每个物理Cpu包含的核心数)
  • local[K] 使用K个Worker线程来本地化运行Spark(理想情况下,K应该根据运行机器的Cpu核心数来确定)\ spark://host:port Spark采用独立集群模式,连接到指定的Spark集群,默认端口是7077
  •  yarn-client Spark采用Yarn集群模式,以客户端模式连接Yarn集群,集群的位置可以再HADOOP_CONF_DIR环境变量中找到;当用户提交了作业后,不能关掉Client,Driver Program驻留在Client
  • yarn-cluster Spark采用Yarn集群模式,以集群模式连接Yarn集群,集群的位置可以再HADOOP_CONF_DIR环境变量中找到;当用户提交了作业后,就可以关掉Client,作业会继续在Yarn上运行,该模式不适合运行交互类型的作业,常用于企业生产环境
  •  mesos://host:port Spark采用Mesos集群模式,连接到指定的Mesos集群,默认端口是5050

Spark的运行模式

  •  local 单机模式,常用于本地开发
  •  Standalone模式:Spark自带的资源调度管理服务
  • Mesos模式:Mesos是另一种资源调度管理框架,可以为运行在它上面的Spark提供服务
  • Yarn模式:yarn是Hadoop中用于资源调度管理的框架,它也可以为Spark提供资源调度服务。

3 Spark的交互式操作入门


基本操作


Scala版本
./bin/spark-shell 进入spark的交互界面

1 val textFile = spark.read.textFile("D:/hadoop/spark/hello.txt") //读取DataSet数据集
 2 textFile.count() //返回数据集的元素数
 3 textFile.first() //返回数据集中第一个元素
 4 val linesWithSpark = textFile.filter(line => line.contains("Spark")) //使用过滤器选取满足要求的元素



Python版本
./bin/pyspark 进入spark的交互界面

1 textFile = spark.read.text("D:/hadoop/spark/hello.txt") //读取数据集
2 textFile.count()  //返回数据集的元素数
3 textFile.first() //返回第一个元素
4 textFile.filter(textFile.value.contains("Spark")).count()  // How many lines contain "Spark"?

4 更多的交互式操作

 

textFile.map(x=>x.split(" ").size).reduce((a,b)=>Math.max(a,b))  //map计算每行出现的单词数,reduce统计行中单词的最大数



三 RDD的设计与原理

1 RDD编程基础

RDD(弹性分布式数据集)是Spark的核心概念,它是一个只读的、可分区的分布式数据集,这个数据集的全部或部分可以缓存在内存中,可在多次计算重用。

Spark的另一个抽象就是可以用在并行操作的共享变量,默认情况下,当Spark并行运行一个函数作为不同节点上的一组任务时,它会将函数中使用的每个变量的副本发送给每个任务。有时,变量需要跨任务共享,或者在任务和驱动程序之间共享。Spark支持两种类型的共享变量:广播变量,可用于缓存所有节点的内存中的值; 累加器,它们是仅“添加”到的变量,例如计数器和总和。

1.1 初始化Spark

Spark程序必须做的第一件事是创建一个SparkContext对象,它告诉Spark如何访问集群。要创建SparkContext首先需要构建一个包含有关应用程序信息的SparkConf对象。每个JVM只能激活一个SparkContext。stop()在创建新的SparkContext之前,您必须使用它。

scala版本

val conf = new SparkConf().setAppName(appName).setMaster(master)
val sc = new SparkContext(conf)

python版本

conf = SparkConf().setAppName(appName).setMaster(master)
sc = SparkContext(conf=conf)

appName指定了应用程序的名称,master指定了程序运行的url(local,mesos,yarn上)。

1.2 使用shell

./bin/spark-shell --master local

./bin/spark-shell --master local --jar code.jar

./bin/spark-shell --master local --packages "com.example.demo"

--master指定运行模式,--jar添加指定jar包,--packages添加依赖库。

1.3 创建RDD

scala版本


textFile函数
spark通过textFile函数从文件系统加载数据到内存并创建RDD,该方法把文件的URI作为参数,这个参数可以是本地文件系统或分布式文件系统,从文件中读取到的每一行内容都成为RDD中的一个元素。

val lines = sc.textFile("D:/out.txt")
输出 lines: org.apache.spark.rdd.RDD[String] = D:/out.txt MapPartitionsRDD[1] at textFile at <console>:24

parallelize函数
通过并行集合数组创建RDD

var array = Array(1,2,3,4,5)
输出array: Array[Int] = Array(1, 2, 3, 4, 5)
var rdd = sc.parallelize(array)
输出rdd: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[8] at parallelize at <console>:26

python版本

textFile()函数用过文件系统生成RDD

distFile = sc.textFile("data.txt")

parallelize()函数并行化生成RDD

data = [1, 2, 3, 4, 5]
distData = sc.parallelize(data)

spark支持从文件系统中生成RDD,还可以从分布式文件系统中生成RDD,例如:hdfs、Cassandra,HBase,Amazon S3等。

spark生成RDD的注意事项:

  • 如果是在本地文件系统中,且运行模式为集群时,需把文件拷贝到Worker节点的相同路径中;
  • textFile函数的参数支持使用通配符;
  • textFile(url,partitionNum):partitionNum可以指定分区,默认情况下spark为每个块创建分区(hdfs中的块大小默认128M),不能创建比块小的分区。

除了文本文件,Spark还支持其他数据格式:

  • SparkContext.wholeTextFiles允许您读取包含多个小文本文件的目录,并将它们的全路径名和内容返回。
  • sc.hadoopRDD()方式生成RDD
  • sc.sequenceFile()
  • sc.saveAsObjectFile和SparkContext.objectFile支持以包含序列化Java对象的简单格式保存RDD。

 

1.4 RDD操作

基础示例

//加载文件生成RDD
val lines = sc.textFile("D:/hadoop/spark/hello.txt")
//通过转换统计每个RDD元素的长度
val line =lines.map(x=>x.length)
//统计RDD中的字符个数
line.reduce((a,b)=>a+b)


RDD的操作包括转换和行动操作
(1)转换操作
每一次转换都会生成不同的RDD,供给下一个操作使用,RDD的转换是惰性的,在转换过程中不会发生计算,只有当行动开始时,才会发生真正的计算。
常用的RDD转换API

操作含义
filter(func)筛选出满足函数func的元素,并返回一个新的数据集
map(func)将每个元素传递到func中,并将结果返回成一个新的数据集
flatMap(func)与map()相似,但每个输入元素都可以映射到0到多个输出结果
groupByKey()应用于(K,V)键值对的数据集时,返回一个新的(K,Iterable)形式的数据集
reduceByKey(func)应用于(K,V)键值对的数据集时,返回一个新的(K,V)形式的数据集,其中每个值是将每个key传递到函数func中进行聚合后的结果

mapPartitions(func)

 

与map类似,但在RDD的每个分区(块)上单独运行,因此当在类型T的RDD上运行时,func必须是Iterator <T> => Iterator <U>类型
mapPartitionsWithIndex(func)与mapPartitions类似,但也为func提供了表示分区索引的整数值,因此当在类型T的RDD上运行时,func必须是类型(Int,Iterator <T>)=> Iterator <U>。
sample(withReplacementfractionseed)使用给定的随机数生成器种子,在有或没有替换的情况下对数据的一小部分进行采样。
union(otherDataset)返回一个新数据集,其中包含源数据集和参数中元素的并集。
intersection(otherDataset)返回包含源数据集和参数中元素交集的新RDD。
distinct([numPartitions]))返回包含源数据集的不同元素的新数据集。
aggregateByKey(zeroValue)(seqOpcombOp,[ numPartitions ])当调用(K,V)对的数据集时,返回(K,U)对的数据集,其中使用给定的组合函数和中性“零”值聚合每个键的值。允许与输入值类型不同的聚合值类型,同时避免不必要的分配。同样groupByKey,reduce任务的数量可通过可选的第二个参数进行配置。
sortByKey([ascending], [numPartitions])当在K实现Ordered的(K,V)对的数据集上调用时,返回按键按升序或降序排序的(K,V)对的数据集
join(otherDataset, [numPartitions])当调用类型(K,V)和(K,W)的数据集时,返回(K,(V,W))对的数据集以及每个键的所有元素对。外连接通过支持leftOuterJoinrightOuterJoinfullOuterJoin
cogroup(otherDataset, [numPartitions])当调用类型(K,V)和(K,W)的数据集时,返回(K,(Iterable <V>,Iterable <W>))元组的数据集。此操作也称为groupWith
cartesian(otherDataset)当调用类型为T和U的数据集时,返回(T,U)对的数据集(所有元素对)。
pipe(command[envVars])通过shell命令管道RDD的每个分区,例如Perl或bash脚本。RDD元素被写入进程的stdin,并且输出到其stdout的行将作为字符串的RDD返回。
coalesce(numPartitions)将RDD中的分区数减少为numPartitions。过滤大型数据集后,可以更有效地运行操作。
repartition(numPartitions)随机重新调整RDD中的数据以创建更多或更少的分区并在它们之间进行平衡
repartitionAndSortWithinPartitions(partitioner)根据给定的分区重新分区RDD,并在每个生成的分区中按键对记录进行排序。这比repartition在每个分区中调用然后排序更有效,因为它可以将排序推送到shuffle机器中。

 

filter函数

var file = sc.textFile("D:/hadoop/spark/hello.txt")
输出file: org.apache.spark.rdd.RDD[String] = D:/hadoop/spark/hello.txt MapPartitionsRDD[13] at textFile at <console>:24
var rdd = file.filter(line=>line.contains("spark"))
输出rdd: org.apache.spark.rdd.RDD[String] = MapPartitionsRDD[14] at filter at <console>:25
rdd.foreach(println)
输出
spark
spark1
jhelewrwespark

map函数
 

var file = sc.textFile("D:/hadoop/spark/hello.txt")
输出file: org.apache.spark.rdd.RDD[String] = D:/hadoop/spark/hello.txt MapPartitionsRDD[13] at textFile at <console>:24
 var rdd2 = file.map(line=>line+"123")
输出rdd2: org.apache.spark.rdd.RDD[String] = MapPartitionsRDD[15] at map at <console>:25
rdd2.foreach(println)
输出
sfuhfd        sf123
sdfsiows123
spark1123
soar123
.........

 

但是file.map(line=>line.split(" "))中,line.split(" ")每行筛选出的元素会形成Array对象

flatMap函数
flatMap函数经过----->map过程形成Array对象-------->flat过程将每个Array中的元素转换成RDD中的元素

val rdd = sc.parallelize(1 to 5)
rdd.flatMap(x=>(1 to x)).foreach(x=>print(x+"  "))

groupByKey函数
groupByKey函数应用于(K,v)键值对的数据集

val text = sc.parallelize(Array(("hadoop",1),("spark",1),("spark",2),("hadoop",4),("flink",1)))
text.groupByKey().foreach(println)


 

reduceByKey函数
它也是应用于(K,V)形式的数据集,并返回(K,V)形式的数据集。
reduceByKey((a,b)=>a+b)
原始数据("hadoop",1),("is",1),("hadoop",1),("is",1)--->归并为("hadoop",(1,1)),("is",(1,1))--->通过(a,b)=>a+b筛选形成新数据集("hadoop",2),("is",2)

val text = sc.parallelize(Array(("hadoop",1),("spark",1),("spark",2),("hadoop",4),("flink",1)))
text.reduceByKey((a,b)=>a+b).foreach(println)

mapPartitions(func)函数

func为Iterator[T]=>Iterator[U]类型

val conf = new SparkConf().setMaster("local").setAppName("control").set("spark.testing.memory", "2147480000")
    val sc = new SparkContext(conf)
    val rdd1 = sc.parallelize(1 to 9, 3)
    def doubleFunc(iter: Iterator[Int]) : Iterator[(Int,Int)] = {
      var res = List[(Int,Int)]()
      while(iter.hasNext) {
        val cur = iter.next
        res .::= (cur,cur*2)
      }
      res.iterator
    }
    val rdd2 = rdd1.mapPartitions(doubleFunc)
    println(rdd2.collect().mkString)

输出

map和mapPartitions对比:

  • map是对rdd中的每一个元素进行操作;mapPartitions则是对rdd中的每个分区的迭代器进行操作;

  • 如果是普通的map,比如一个partition中有1万条数据。那么你的function要执行和计算1万次。使用MapPartitions操作之后,一个task仅仅会执行一次function,function一次接收所有的partition数据。只要执行一次就可以了,性能比较高。如果在map过程中需要频繁创建额外的对象(例如将rdd中的数据通过jdbc写入数据库,map需要为每个元素创建一个链接而mapPartition为每个partition创建一个链接),则mapPartitions效率比map高的多。SparkSql或DataFrame默认会对程序进行mapPartition的优化;

  • 如果是普通的map操作,一次function的执行就处理一条数据;那么如果内存不够用的情况下, 比如处理了1千条数据了,那么这个时候内存不够了,那么就可以将已经处理完的1千条数据从内存里面垃圾回收掉,或者用其他方法,腾出空间来吧。
    所以说普通的map操作通常不会导致内存的OOM异常。 但是MapPartitions操作,对于大量数据来说,比如甚至一个partition,100万数据,一次传入一个function以后,那么可能一下子内存不够,但是又没有办法去腾出内存空间来,可能就OOM,内存溢出。

 

mapPartitionsWithIndex(func)函数

func为Iterator[(Int,T)]=>Iterator[U]类型,Int指定了下标

import org.apache.spark.{SparkConf, SparkContext}

object RDDmyCon {
  def doubleFunc(index:Int,iter:Iterator[Int]):Iterator[(Int,Int)]={
    var res = List[(Int,Int)]()
    while(iter.hasNext){
      val cur = iter.next
      res .::=(index,cur)
    }
    res.iterator
  }
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setMaster("local").setAppName("control").set("spark.testing.memory", "2147480000")
    val sc = new SparkContext(conf)
    val rdd1 = sc.parallelize(1 to 9, 3)
    val data2 = rdd1.mapPartitionsWithIndex(doubleFunc)
    data2.collect().foreach(x=>print(x+" "))
  }
}

sample(withReplace,fraction,seed)函数

对RDD中集合内的元素进行采样,第一个参数withReplacement是true表示有放回取样,false表示无放回取样;第二个参数表示取样比例,第三个参数是随机种子。

val rdd = sc.parallelize(1 to 100)
rdd.sample(false,0.4,5).collect().foreach(x=>print(x+" "))

 

union(otherDataset)函数

otherDataset指其他rdd

import org.apache.spark.{SparkConf, SparkContext}

object UnionRdd {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().set("spark.testing.memory", "2147480000").setAppName("union").setMaster("local")
    val sc = new SparkContext(conf)
    val data = sc.parallelize(1 to 10)
    val data2 = sc.parallelize(11 to 20)
    data.union(data2).collect().foreach(x =>print(x+" "))
  }
}

intersection(otherDataset)函数

返回两个数据集的交集

def main(args: Array[String]): Unit = {
    val conf = new SparkConf().set("spark.testing.memory", "2147480000").setAppName("union").setMaster("local")
    val sc = new SparkContext(conf)
    val data = sc.parallelize(1 to 10)
    val data2 = sc.parallelize(8 to 20)
    data.intersection(data2).collect().foreach(x=>print(x+" "))
  }

distinct([numPartitions])函数

去除重复的元素

val conf = new SparkConf().set("spark.testing.memory", "2147480000").setAppName("union").setMaster("local")
    val sc = new SparkContext(conf)
    val data = sc.parallelize(1 to 10)
    val data2 = sc.parallelize(8 to 20)
    val test = data.union(data2)
    test.distinct().collect().foreach(x=>print(x+" "))
    println()

 

aggregate[U](zeroValue: U)(seqOp: Function2[U, T, U], combOp: Function2[U, U, U]): U函数

zeroValue表示初始值,先将zeroValue与各个分区中的元素进行seqOp操作,然后combOp组合不同分区seqOp计算的结果,返回和zeroValue的类型一样的值。

 def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setMaster("local").setAppName("control").set("spark.testing.memory", "2147480000")
    val sc = new SparkContext(conf)
    val rdd1 = sc.parallelize(1 to 6, 2)
    val (mul,sum,count) = rdd1.aggregate((1,0,0))((acc,number)=>(acc._1*number,acc._2+number,acc._3+1),(x,y)=>(x._1*y._1,x._2+y._2,x._3+y._3))
    print(mul+" "+ sum +" "+count)
  }

输出

aggregateByKey(zeroValue)(seqOpcombOp, [numPartitions])函数

aggregateByKey是根据key对元素操作,设定初始值zeroValue,然后将zeroValue与各个分区的元素进行seqOp操作,然后combOp再把seqOp的结果聚合。

def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setMaster("local").setAppName("control").set("spark.testing.memory", "2147480000")
    val sc = new SparkContext(conf)
    val array = Array((1,2),(1,3),(2,2),(2,3),(3,1))
    val rdd = sc.parallelize(array,2)
    def seqOp(a:Int,b:Int):Int={
      a+b
    }
    def combineOp(a:Int,b:Int):Int={
      a+b
    }
    val dd = rdd.aggregateByKey(0)(seqOp,combineOp).collect()
    dd.foreach(x=>print(x+" "))
  }

 

sortByKey([ascending], [numPartitions])函数

根据key排序

def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setMaster("local").setAppName("control").set("spark.testing.memory", "2147480000")
    val sc = new SparkContext(conf)
    val array = Array((5,6),(1,3),(2,6),(2,3),(3,1),(1,5),(2,4),(1,8))
    val rdd = sc.parallelize(array)
    rdd.sortByKey().collect().foreach(x=>print(x+" "))
  }

 

join(otherDataset, [numPartitions])函数

连接数据集,键相同的连接

 def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setMaster("local").setAppName("control").set("spark.testing.memory", "2147480000")
    val sc = new SparkContext(conf)
    val array = Array((5,6),(1,3),(2,6),(2,3),(3,1),(1,5),(2,4),(1,8))
    val array2 = Array((4,5),(1,2),(2,5),(1,8))
    val rdd = sc.parallelize(array)
    val rdd2 = sc.parallelize(array2)
    rdd.join(rdd2).collect().foreach(x=>print(x+" "))
  }

 

cogroup(otherDataset, [numPartitions])函数

对两个RDD中的KV元素,每个RDD中相同key中的元素分别聚合成一个集合。与reduceByKey不同的是针对两个RDD中相同的key的元素进行合并。

def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setMaster("local").setAppName("control").set("spark.testing.memory", "2147480000")
    val sc = new SparkContext(conf)
    val array = Array((5,6),(1,3),(2,6),(2,3),(3,1),(1,5),(2,4),(1,8))
    val array2 = Array((4,5),(1,2),(2,5),(1,8))
    val rdd = sc.parallelize(array)
    val rdd2 = sc.parallelize(array2)
    rdd.cogroup(rdd2).collect().foreach(x=>print(x+" "))
  }

 

 

cartesian(otherDataset)函数

对两个RDD中的所有元素进行笛卡尔积操作

def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setMaster("local").setAppName("control").set("spark.testing.memory", "2147480000")
    val sc = new SparkContext(conf)
    val rdd1 = sc.parallelize(1 to 3)
    val rdd2 = sc.parallelize(2 to 4)
    rdd1.cartesian(rdd2).foreach(println)

  }

pipe(command[envVars])函数

调用命令

 

coalesce(numPartitions,shuffle)函数

对RDD进行重新分区,shuffle默认值为false,当shuffle=false时,不能增加分区数目。

 

repartition(numPartitions)

等于coalesce(numPartitions,true),重新分区

 

glom():将RDD的每个分区中类型为T的元素转换为Array[T]。

 


1.3行动操作


行动操作是真正发生计算的地方。
常用的行动操作API

操作含义
count()返回数据集中的元素个数
collect()以数组的形式返回数据集中的所有元素
first()返回数据集中的第一个元素
take(n)以数组的方式返回数据集中的前n个元素
reduce(func)通过函数func(输入两个参数)
foreach(func)将数据集中的每个元素传递给func
takeSample(withReplacementnum, [seed])返回一个数组,其中包含数据集的num个元素的随机样本,有或没有替换,可选地预先指定随机数生成器种子。
takeOrdered(n[ordering])使用自然顺序或自定义比较器返回RDD 的前n个元素。
saveAsTextFile(path)将数据集的元素作为文本文件(或文本文件集)写入本地文件系统,HDFS或任何其他Hadoop支持的文件系统的给定目录中。Spark将在每个元素上调用toString,将其转换为文件中的一行文本。
saveAsSequenceFile(path
(Java and Scala)
将数据集的元素作为Hadoop SequenceFile写入本地文件系统,HDFS或任何其他Hadoop支持的文件系统中的给定路径中。这可以在实现Hadoop的Writable接口的键值对的RDD上使用。在Scala中,它也可以在可隐式转换为Writable的类型上使用(Spark包括基本类型的转换,如Int,Double,String等)。
saveAsObjectFile(path
(Java and Scala)
使用Java序列化以简单格式编写数据集的元素,然后可以使用它进行加载SparkContext.objectFile()
countByKey()仅适用于类型(K,V)的RDD。返回(K,Int)对的散列映射,其中包含每个键的计数。

1.4持久化


在spark中,RDD采用惰性求值的机制,每次遇到行动操作都会从头开始执行计算。每次调用行动操作,都会触发一次从头开始的计算,这对于迭代计算代价是很大的,因为迭代计算经常会多次重复使用同一组数据。解决方法就是通过持久化来避免重复计算的开销。


具体方法就是使用persist()方法,对RDD标记为持久化,出现persist()的地方,并不会马上计算生成RDD并把它持久化,而是要等到第一个行动操作触发真正计算以后,才会把计算结果持久化,持久化后的RDD会被保留在计算节点的内存中,被后续的行动操作重复使用。


persist持久化级别

  •  persist(MEMORY_ONLY):表示将RDD作为反序列化的对象存储于JVM中,如果内存不足,就要按照LRU原则替换缓存中的内容。
  • persist(MEMORY_AND_DISK):表示将RDD作为反序列化的对象存储在JVM中,如果内存不足,超出的分区将会被存放在磁盘上
  •  一般使用cache()方法时,会调用persist(MEMORY_ONLY)方法。

1.5 分区


分区的作用:RDD是弹性分布式数据集,通常RDD很大,会被分成很多个分区,分别保存在不同的节点上。对RDD进行分区,目的是增加并行度,让计算向数据靠拢,减少了网络开销。


分区原则:RDD分区的一个原则就是使分区的个数尽量等于集群中的CPU核心数目。对于不同的Spark部署模式,都可以设置分区数目spark.default.parallelism这个参数的值,来配置默认的分区数。


Spark部署模式

  •  Local模式:默认为本地机器的CPU数目,若设置了local[N],则默认为N;
  •  Standalone或YARN模式:在集群中所有CPU核心数目总和和2取较大者为默认值;
  •  Mesos模式:默认分区数为8


手动设置分区数目

  • sc.textFile(url,partitionNum)
  • sc.parallelize(url,partitionNum) 这个函数若没有指定分区数时,会采用min(default,2)的算法设置分区\
  • 使用repartition()方法重新设置分区个数
  • data.repartition(partitionNum)

自定义分区方法:Spark提供了自带的哈希分区(HashPartitioner)与区域分区(RangePartitioner),能够满足大多说应用场景的需求。与此同时,Spark也支持自定义分区方式,即提供自定义的Partitioner对象来控制RDD的分区方式,从而利用领域知识进一步减少通信开销。其次,Spark的分区函数针对的是(key,value)类型的RDD,也就是说,RDD的每个元素都是(key,value)类型,分区函数根据key进行分区。因此对于非(key,value)类型的RDD进行分区,需要先把RDD转换为(key,value)类型,再进行分区。


自定义分区类需要继承org.apache.spark.Partitioner类,实现下面三个方法

  •  numPartitions:Int  返回创建出来的分区数
  • getPartition(key:Any):Int 返回给定键的分区编号(0~numPartitions-1)
  •  equals():java判断相等性的标准方法

WordCount实例图
 

val file = "hdfs://mynode01:9000/opt/hello.txt"
var conf = new SparkConf().setAppName("WordCount").setMaster("local[4]")
conf.set("spark.testing.memory", "2147480000")
var sc = new SparkContext(conf)
var text = sc.textFile(file)
var wordcount = text.flatMap(line=>line.split(" ")).map(word=>(word,1)).reduceByKey((a,b)=>a+b)
    `wordcount.foreach(println)

1.6键值对RDD


 1 键值对RDD的创建

键值对RDD的创建方式主要有两种:

  • 从文件中加载生成RDD
  • 通过并行集合(数组)创建RDD

从文件中加载生成RDD: textFile().map()或textFile().flatMap()

通过并行集合生成RDD:parallelize().map或parallelize().flatMap()

1.7常用的键值对转换操作


reduceByKey(func)
使用func函数合并具有相同键的值。
reduceByKey与groupByKey不同,reduceByKey对每个key对应的值的聚合,而groupByKey生成的是(key,value-list)

 groupByKey()
对相同的键进行分组
例("hadoop",1),("spark",2),("hadoop",3),("spark",1)分组后为("hadoop",(1,3)),("spark",(1,2))

 keys      返回RDD中(K,v)格式数据集中的键

values    返回RDD中(K,v)格式数据集中的值


sortByKey()   返回提个根据key排序的RDD


sortBy(func)   返回按func排序后的RDD

mapValues(func)    对RDD中的(K,v)中的v进行func操作
join()   表示内连接,对于给定的两个输入数据集(K,V1)和(K,V2),只有两个数据集都存在的key才会被输出,得到(K,(V1,V2))的结果


combineByKey

combineByKey(createCombiner,mergeValue,mergeCombiners,partitioner,mapSideCombine)中的各个参数的含义如下:\
createCombiner:在第一次遇到key时创建组合函数,将RDD数据集中的V类型值转换C类型值(V=>C);
 


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值