Spark RDD基础实战(弹性分布式数据集)

http://spark.apache.org/docs/latest/sql-data-sources-json.html

http://jsonlines.org/examples/   官网的准备的数据集合

启动Spark集群

bin/spark-shell  \

--master spark:centoshadoop1:7077

官方文档:

Spark RDD

Spark提供了一种对数据的核心抽象,称为弹性分布式数据集(Resilient Distributed Dataset,RDD)。每个RDD被分为多个分区,这些分区运行在集群中的不同节点上。也就是说,RDD是跨集群节点分区的元素集合,并且这些元素可以并行操作。

在编程时,可以把RDD看作是一个数据操作的基本单位。Spark中对数据的操作主要是对RDD的操作。

1:创建RDD

     RDD中的数据来源可以是程序中的对象集合,也可以来源于外部存储系统中的数据集,例如共享文件系统,HDFS,HBase或任何提供Hadoop InputFormat的数据源。

 

scala> val rdd = sc.parallelize(List(1,2,3,4,5,6))

rdd: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[7] at parallelize at :24

 

scala> val rdd = sc.makeRDD(List(1,2,3,4,5,6,7,8))

rdd: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[8] at makeRDD at :24

从返回的信息可以看出,上述创建的RDD中存储的是Int类型数据集合。实际上,RDD也是一个集合,与常用的List集合不同的是,RDD集合的数据分布于多台计算机上。

 

2:从外部存储系统创建RDD

Spark的textFile()方法可以读取本地文件系统或外部其他系统中的数据,并创建RDD。所不同的是,数据的来源路径不同.

 

(1)创建hadoop的工作目录

hdfs dfs -mkdir -p /library/SparkSQL/Data

上传文件到该工作目录

hdfs dfs -put ./tools/jsonfile/* /library/SparkSQL/Data/

查询上传的文件

hdfs dfs -ls -R /library/SparkSQL/Data

-rw-r--r--   3 hadoop supergroup        115 2020-03-26 12:00 /library/SparkSQL/Data/department.json

-rw-r--r--   3 hadoop supergroup        255 2020-03-26 12:00 /library/SparkSQL/Data/newPeople.json

-rw-r--r--   3 hadoop supergroup        516 2020-03-26 12:00 /library/SparkSQL/Data/people.json

 

bin/spark-shell \

--master spark://centoshadoop1:7077

 

val sqlContext=new org.apache.spark.sql.SQLContext(sc)

val people=

sqlContext.jsonFile("hdfs://centoshadoop1:8020/library/SparkSQL/Data/people.json")

scala> people.collect    

res0: Array[org.apache.spark.sql.Row] =

 Array([33,1,male,001,Mechael,3000], [30,2,female,002,Andy,4000], [19,3,male,003,Justin,5000], [32,1,male,004,John,6000], [20,2,female,005,Herry,7000], [26,3,male,006,Jack,3000])

注意下面的/library/SparkSQL/Data/目录必须是通过hdfs或者hadoop命令创建的逻辑目录,否则报错该目录找不到

(2)读取本地系统文件,在系统目录/library/SparkSQL/Data/下有一个Sparksc.txt文件.

Sparksc.txt 文档内容为

hello hadoop

hello java

scala

上传文档到逻辑目录:

hdfs dfs -put /home/hadoop/tools/jsonfile/Sparksc.txt  /library/SparkSQL/Data/

使用textFile()方法将上述文件内容转化为一个RDD,并使用collect()方法(该方法是RDD的一个行动算子)

scala> val rdd = sc.textFile("/library/SparkSQL/Data/Sparksc.txt")

rdd: org.apache.spark.rdd.RDD[String] =

 /library/SparkSQL/Data/Sparksc.txt MapPartitionsRDD[14] at textFile at :24

读取该rdd中的多个元素

scala> rdd.collect

res4: Array[String] = Array(hello       hadoop, hello   java, scala)

scala> var data = rdd.collect

data: Array[String] = Array(hello hadoop, hello java, scala)

scala> for(element<-data)println(element)

hello hadoop

hello java

Scala

(3)读取HDFS系统文件

 

scala> val rdd = sc.textFile("hdfs://mycluster:8082/input/README.txt")

rdd: org.apache.spark.rdd.RDD[String] =

 hdfs://mycluster:8082/input/README.txt MapPartitionsRDD[16] at textFile at :24

执行下面的命令时报错,该信息可知搭建的数hadoop是高可用集群的,不支持端口

scala> rdd.collect

java.io.IOException: Port 8082 specified in URI hdfs://mycluster:8082/input/README.txt but host 'mycluster' is a logical (HA) namenode and does not use port information.

解决方案:val rdd = sc.textFile("hdfs://mycluster/input/README.txt")

scala> rdd.collect

再次运行上面的命令,无错误信息。

scala> val dl=spark.read.textFile("hdfs://mycluster/input/README.txt")

dl: org.apache.spark.sql.Dataset[String] = [value: string]

 

scala> dl.show()

+--------------------+

|               value|

+--------------------+

|For the latest in...|

|                    |

|   http://hadoop....|

|                    |

|   and our wiki, at:|

|                    |

|   http://wiki.ap...|

|                    |

|This distribution...|

|which you current...|

|possession, use, ...|

|encryption softwa...|

|check your countr...|

|import, possessio...|

|see if this is pe...|

|        information.|

|                    |

|The U.S. Governme...|

|Security (BIS), h...|

|Control Number (E...|

+--------------------+

only showing top 20 rows

3任务RDD算子

     RDD被创建后是只读的,不允许修改,Spark提供了丰富的用于操作RDD的方法,这些方法被称为算子。一个创建完的RDD只支持两种算子:转化(Transformation)算子和行动(Action)算子.

(1)转化算子

转化算子负责对RDD中的数据进行计算并转化为新的RDD。Spark中的所有转化算子都是惰性的,因为它们不会立即计算结果,而只是记住对某个RDD的具体操作过程,直到遇到行动算子才会与行动算子一起执行。

例如,map()是一种转化算子,它接收一个函数作为参数,并把这个函数应用于RDD的每个元素,最后将函数的返回值作为结果RDD中的对应元素的值。

如下代码所示,对rdd1应用map()算子,将rdd1中的每个元素加1并返回一个名为rdd2的新RDD:

scala> val rdd1 = sc.parallelize(List(1,2,3,4,5,6))

rdd1: org.apache.spark.rdd.RDD[Int] =

 ParallelCollectionRDD[9] at parallelize at :24

scala> val rdd2 = rdd1.map(x => x+1)

rdd2: org.apache.spark.rdd.RDD[Int] = MapPartitionsRDD[10] at map at :25

scala> rdd2.collect

res5: Array[Int] = Array(2, 3, 4, 5, 6, 7)

上述代码中,向算子map()传入了一个函数x=>x+1。其中x为函数的参数名称,也可以使用其他字符,例如a=>a+1。Spark会将RDD中的每一个元素传入该函数的参数中。当然,也可以将参数使用下划线”_”代替。例如以下代码:

scala> val rdd1= sc.parallelize(List(1,2,3,4,5,6))

rdd1: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[11] at parallelize at :24

 

scala> val rdd2 = rdd1.map(_+1)

rdd2: org.apache.spark.rdd.RDD[Int] = MapPartitionsRDD[12] at map at :25

 

上述代码中的下划线代表rdd1中的每个元素。rdd1和rdd2中实际上没有任何数据,因为parallelize()和map()都为转化算子,调用转化算子不会立即计算结果。

     若需要查看计算结果可使用行动算子collect()。例如以下代码中的rdd2.collect表示执行计算,并将结果以数组的形式收集到当前Driver。因为RDD的元素为分布式的,可能分布在不同的节点上。

scala> rdd2.collect()

res6: Array[Int] = Array(2, 3, 4, 5, 6, 7)

4其他常用的转化算子介绍如下:

filter(func)算子

通过函数func对源RDD的每个元素进行过滤,并返回一个新的RDD。

例如以下代码,过滤出rdd1中大于3的所有元素,并输出结果。

scala> val rdd1 = sc.parallelize(List(1,2,3,4,5,6,7))

rdd1: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[13] at parallelize at :24

 

scala> val rdd2 = rdd1.filter(_>3)

rdd2: org.apache.spark.rdd.RDD[Int] = MapPartitionsRDD[14] at filter at :25

 

scala> rdd2.collect()

res8: Array[Int] = Array(4, 5, 6, 7)

备注: 下划线”_”代表rdd1中的每个元素

flatMap(func)算子

与map()算子类似,但是每个传入函数func的RDD元素返回0元素会返回0到多个元素,最终会将返回的所有元素合并到一个RDD。

例如以下代码,将集合List转化为rdd1,然后调用rdd1的flatMap()算子将rdd1的每一个元素按照空格分割成多个元素,最终合并所有元素到一个新的RDD。

scala> val rdd1 = sc.parallelize(List("hadoop hello scala","spark hello"))

rdd1: org.apache.spark.rdd.RDD[String] = ParallelCollectionRDD[15] at parallelize at :24

scala> val rdd2 = rdd1.flatMap(_.split(" "))

rdd2: org.apache.spark.rdd.RDD[String] = MapPartitionsRDD[16] at flatMap at :25

 

scala> rdd2.collect()

res9: Array[String] = Array(hadoop, hello, scala, spark, hello)

 

reduceByKey(func)算子

reduceByKey()算子的作用对象是元素为(key,value)形式(Scala元组)的RDD,可以将相同key的元素聚集到一起,最终把所有相同key的元素合并成为一个元素。该元素的Key不变,value可以聚合成一个列表或者进行求和等操作。最终返回的RDD的元素类型和原有类型保持一致。

例如,有两个同学zhangsan和lisi,zhangsan的语文和数学成绩分别为98,78,lisi的语文和数学成绩分别为88,79,现需要分别求zhangsan和lisi的总成绩。代码如下:

scala> val list = List(("zhangsan",98),("zhangsan",78),("lisi",88),("lisi",79))

list: List[(String, Int)] = List((zhangsan,98), (zhangsan,78), (lisi,88), (lisi,79))

 

scala> val rdd1 = sc.parallelize(list)

rdd1: org.apache.spark.rdd.RDD[(String, Int)] = ParallelCollectionRDD[17] at parallelize at :26

 

scala> val rdd2 = rdd1.reduceByKey((x,y)=>x+y)

rdd2: org.apache.spark.rdd.RDD[(String, Int)] = ShuffledRDD[18] at reduceByKey at :25

 

scala> rdd2.collect()

res10: Array[(String, Int)] = Array((zhangsan,176), (lisi,167))       

上述代码使用了reduceByKey()算子,并传入了函数(x,y)=>x+y,x和y代表key相同的两个value值。该算子会寻找相同key的元素,当找到这样的元素时会对其value执行(x,y)=>x+y处理,即只保留求和后的数据作为value。

此外,上述代码中的rdd1.reduceByKey((x,y)=>x+y)也可以简化以下代码:

rdd1.reduceByKey(_+_)

 

union()算子

该算子将两个RDD合并为一个新的RDD,主要用于对不同的数据来源进行合并,两个RDD中的数据类型要保持一致。

例如以下代码,通过集合创建了两个RDD,然后将两个RDD合并成一个RDD:

 

scala> val rdd1 = sc.parallelize(Array(1,2,3,4,5))

rdd1: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[19] at parallelize at :24

 

scala> val rdd2 = sc.parallelize(Array(4,5,6,7,8,8,9))

rdd2: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[20] at parallelize at :24

 

scala> val rdd3 = rdd1.union(rdd2)

rdd3: org.apache.spark.rdd.RDD[Int] = UnionRDD[21] at union at :27

 

scala> rdd3.collect()

res11: Array[Int] = Array(1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 8, 9)

 

sortBy(func)算子

该算子将RDD中的元素按照某个规则进行排序。该算子的第一个参数为排序函数,第二个参数是一个布尔值,指定升序(默认)或降序。若需要降序排列,则将第二个参数值为false。

例如,一个数组中存放了三个元组,将该数组转化为RDD集合,然后对该RDD按照每个元素中的第二个值进行降序排列。代码如下:

scala> val rdd1 = sc.parallelize(Array(("hadoop",12),("java",32),("spark",22)))

rdd1: org.apache.spark.rdd.RDD[(String, Int)] = ParallelCollectionRDD[23] at parallelize at :24

 

scala> val rdd2 = rdd1.sortBy(x=>x._2,false)

rdd2: org.apache.spark.rdd.RDD[(String, Int)] = MapPartitionsRDD[28] at sortBy at :25

 

scala> rdd2.collect

res12: Array[(String, Int)] = Array((java,32), (spark,22), (hadoop,12))

上述代码sortBy(x=>x._2,false)中的x代表rdd1中的每个元素。由于rdd1的每个元素是一个元组,因此使用x._2取得每个元素的第二个值。当然,sortBy(x=>x._2,false)也可以直接简化为sortBy(_._2,false)。

5行动算子

Spark中的转化算子并不会立即进行运算,而是在遇到行动算子时才会执行相应的语句,触发Spark的任务调度。

Spark常用的行动算子及其介绍如下表

行动算子

介绍

reduce(func)

将RDD中的元素进行聚合运算

collect()

向Driver以数组形式返回数据集的所有元素。通过对于过滤操作或其他返回足够小的数据子集的操作非常有用

count()

返回数据集中元素的数量

first()

返回集合中第一个元素

take(n)

返回包含数据集的前n个元素的数组

takeOrdered(n,[ordering])

返回RDD中的前n个元素,并以自然顺序或自定义的比较器顺序进行排序

saveAsSequenceFile(path)

将数据集中的元素持久化为一个Hadoop SequenceFile文件,并将文件存储在本地文件系统,HDFS或其他Hadoop支持的文件系统的指定目录中。实现了Hadoop Writable接口的键值对形式的RDD可以使用该操作

SaveAsTextFile(path)

将数据集中的元素持久化为一个或一组文本文件,并将文件存储在本地文件系统,HDFS或其他Hadoop支持的文件系统的指定目录中。Spark会对每个元素调用toString()方法,将每个元素转化为文本文件中的一行。

saveAsObjectFile(path)

将数据集中的元素序列化成对象,存储到文件找中。然后可以使用SparkContext.objectFile()对该文件进行加载

 

countByKey()

统计RDD中key相同的元素的数量,仅元素类型为键值对(key,value)的RDD可用,返回的结果类型为map

foreach(func)

对RDD中的每一个元素运行给定的函数func

下面对其中的几个行动算子进行实例讲解:

(1)reduce(func)算子

将数字1到100所组成的集合转化为RDD,然后对该RDD进行reduce()算子计算,统计RDD中所有元素值的总和。代码如下:

scala> rdd2.collect

res12: Array[(String, Int)] = Array((java,32), (spark,22), (hadoop,12))

 

scala> val rdd1 = sc.parallelize(1 to 200)

rdd1: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[29] at parallelize at :24

 

scala> rdd1.reduce(_+_)

res13: Int = 20100

上述代码中的下划线”_”代表RDD中的元素

(2)count()算子

统计RDD集合中元素的数量,代码如下:

scala> val rdd1 = sc.parallelize(1 to 300)

rdd1: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[30] at parallelize at :24

 

scala> rdd1.count

res14: Long = 300

(3)countByKey()算子

List集合中存储的键值对形式的元组,使用该List集合创建一个RDD,然后对其进行countByKey()的计算,代码如下:

scala> val rdd1 = sc.parallelize(List(("zhang",87),("zhang",79),("li",90)))

rdd1: org.apache.spark.rdd.RDD[(String, Int)] = ParallelCollectionRDD[31] at parallelize at :24

 

scala> rdd1.countByKey()

res15: scala.collection.Map[String,Long] = Map(zhang -> 2, li -> 1)

(4)take(n)算子

返回集合中前5个元素组成的数组,代码如下:

scala> val rdd1 = sc.parallelize(1 to 100)

rdd1: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[34] at parallelize at :24

 

scala> rdd1.take(5)

res16: Array[Int] = Array(1, 2, 3, 4, 5)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(Spark,RDD编程实战指南,大数据)