1.弹性分布式数据集RDD
1.1.RDD概述
1.1.1.什么是RDD
RDD(Resilient Distributed DataSet)弹性分布式数据集。是spark中最基本的数据抽象,是一个不可变的、可分区的、可并行计算的集合。
Resilient:数据可以选择性的存储在内存中,或者磁盘中
Distributed:分布式存储、分布式计算
DataSet:用于存放数据的数据集合
1.1.2.RDD的属性
-
A list of partitions:分区列表,数据集基本组成单位
说明:每个分区对应一个计算任务,决定并行计算的粒度。比如:读取hdfs上数据文件产生的RDD分区数,与数据存储block的个数相等
-
A function for computing each split:计算每个分区的函数
比如:在单词计数任务中的textFile、flatMap、map等函数
-
A list of dependencies on other RDDs:一个RDD会依赖于其他的RDD
说明:依赖关系
-
Optionally, a Partitioner for key-value RDDs:可选的,一个Partitioner
说明:只有key/value的RDD才会有Partitioner。当前在spark中提供了两种类型的Partitioner:一个是基于哈希的HashPartitioner;一个是基于范围的RangePartitioner
-
Optionally, a list of preferred locations to compute each split on:可选的,一个列表,存储每个Partition的优先位置
说明:数据本地性(移动数据不如移动计算划算)
1.1.3.为什么会产生RDD
- 传统的MapReduce虽然具有自动容错、平衡负载和可拓展性的优点,但是其最大缺点是采用非循环式的数据流模型,使得在迭代计算中要进行大量的磁盘IO操作,RDD正是解决这一缺点的抽象方法。
- RDD是spark提供的最重要的抽象概念,它是一种具有容错机制的特殊集合,分布在集群的节点上,以函数式编程来操作集合,进行各种并行操作。可以把RDD的结果数据进行缓存,方便进行多次重用,避免重复计算。
1.1.4.RDD在spark中的地位和作用
-
为什么会有spark:
因为传统的并行计算模型无法有效的解决迭代计算(iterative)和交互式计算(interactive)。spark的使命便是解决这两个问题(迭代计算和交互式计算),这也是它存在的价值和理由。
-
spark如何解决迭代计算:
迭代计算的主要实现思想,就是通过RDD,把所有计算的数据保存在分布式的内存中。迭代计算通常情况下都是对同一个数据集做反复的迭代计算。数据在内存中将大大提升IO操作效率。这也是spark的核心:内存计算
-
spark如何实现交互式计算:
spark是用scala语言实现的,spark和scala能够紧密的集成,spark可以完美的运用scala的解释器,使得其中的scala可以像操作本地集合对象一样轻松操作分布式数据集
-
spark和RDD的关系:
RDD是一种具有容错性、基于内存计算的抽象方法,RDD是spark Core的底层核心,spark则是这个抽象方法的实现
1.2.创建RDD
#进入spark shell(node03)
cd /export/servers/spark-2.0.2-bin-hadoop2.7/bin
spark-shell --master local[2]
-
根据scala集合创建
sc.parallelize(Array(1,2,3,4,5,6))
-
读取外部文件创建
sc.textFile("/export/servers/testdata/words.txt")
-
根据已经存在的RDD,经过算子转换生成新的RDD
val rdd1=sc.textFile("/export/servers/testdata/words.txt") rdd1.flatMap(_.split(" "))
1.3.RDD编程API
1.3.1.RDD算子分类
-
Transformation:转换
根据数据集创建一个新的数据集,计算后返回一个新RDD
-
Action:动作
对rdd结果计算后,返回一个数值value给驱动程序
1.3.2.Transformation
RDD中的所有转换都是延迟加载的,它们并不会直接计算结果。相反的,它们只是记住这些应用到基础数据集(例如一个文件)上的转换操作。只有当发生一个要求返回结果给Driver的动作时,这些转换才会真正运行。这种设计让spark更加有效率地运行。
常用的Transformation:
序号 | 转换 | 含义 |
---|---|---|
1 | map(func) | 返回一个新的RDD,该RDD由每一个输入元素经过func函数转换后组成 |
2 | filter(func) | 返回一个新的RDD,该RDD由经过func函数计算,后返回值为true的输入元素组成 |
3 | flatMap(func) | 类似于map,每一个输入元素可以被映射为0或多个输出元素(func返回一个序列,而不是单一元素) |
4 | mapPartitions(func) | 类似于map,独立地在RDD的每一个分片上运行,在类型为T的RDD上运行时,func的函数类型必须是Iterator[T] => Iterator[U] |
5 | mapPartitionsWithIndex(func) | 类似于mapPartitions,func带有一个整数参数表示分片的索引值,因此在类型为T的RDD上运行时,func的函数类型必须是 (Int, Interator[T]) => Iterator[U] |
6 | union(othersDataSet) | 对源RDD和参数RDD求并集后返回一个新的RDD |
7 | intersection(otherDataSet) | 对源RDD和参数RDD求交集后返回一个新的RDD |
8 | distinct([numTasks]) | 对源RDD进行去重后返回一个新的RDD |
9 | groupByKey([numTasks]) | 在一个(K,V)的RDD上调用,返回一个(K, Iterator[V])的RDD |
10 | reduceByKey(func,[numTasks]) | 在一个(K,V)的RDD上调用,返回一个(K,V)的RDD,使用指定的reduce函数,将相同key的值聚合到一起,与groupByKey类似,reduce任务的个数可以通过第二个可选的参数来设置 |
11 | sortByKey([ascending],[numTasks]) | 在一个(K,V)的RDD上调用,K必须实现Ordered接口,返回一个按照key进行排序的(K,V)的RDD |
12 | sortBy(func,[ascending],[numTasks]) | 与sortByKey类似,但是更灵活 |
13 | join(otherDataSet,[numTasks]) | 在类型为(K,V)和(K,W)的RDD上调用,返回一个相同key对应的所有元素对在一起的(K,(V,W))的RDD |
14 | cogroup(otherDataSet,[numTasks]) | 在类型为(K,V)和(K,W)的RDD上调用,返回一个(K,(Iterable,Iterable))类型的RDD |
15 | coalesce(numPartitions) | 减少 RDD 的分区数到指定值 |
16 | repartition(numPartitions) | 重新给 RDD 分区 |
17 | repartitionAndSort\ | 重新给 RDD 分区,并且每个分区内以记录的 key 排序 |
18 | WithinPartitions(partitioner) |
1.3.3.Action
序号 | 动作 | 含义 |
---|---|---|
1 | reduce(func) | reduce将RDD中元素前两个传给输入函数,产生一个新的return值,新产生的return值与RDD中下一个元素(第三个元素)组成两个元素,再被传给输入函数,直到最后只有一个值为止 |
2 | collect() | 在驱动程序中,以数组的形式返回数据集的所有元素生 |
3 | count() | 返回RDD的元素个数 |
4 | first() | 返回RDD的第一个元素(类似于take(1)) |
5 | take(n) | 返回一个由数据集的前n个元素组成的数组 |
6 | takeOrdered(n,[ordering]) | 返回自然顺序或者自定义顺序的前 n 个元素 |
7 | saveAsTextFile(path) | 将数据集的元素以textfile的形式保存到hdfs文件系统或者其他支持的文件系统,对于每个元素,spark将会调用toString方法,将它装换为文件中的文本 |
8 | saveAsSequenceFile(path) | 将数据集中的元素以hadoop sequencefile的格式保存到指定的目录下,可以使hdfs或者其他hadoop支持的文件系统 |
9 | saveAsObjectFile(path) | 将数据集的元素,以 Java 序列化的方式保存到指定的目录下 |
10 | countByKey() | 针对(K,V)类型的RDD,返回一个(K,Int)的map,表示每一个key对应的元素个数 |
11 | foreach(func) | 在数据集的每一个元素上,运行函数func |
12 | foreachPartition(func) | 在数据集的每一个分区上,运行函数func |
1.4.RDD常用算子操作
启动spark-shell:
#进入spark shell(node03)
cd /export/servers/spark-2.0.2-bin-hadoop2.7/bin
spark-shell --master spark://node01:7077
1.4.1.map、filter
# 通过并行化生成rdd
val rdd1=sc.parallelize(List(5, 6, 4, 7, 3, 8, 2, 9, 1, 10))
#对rdd1里的每一个元素乘2然后排序
val rdd2=rdd1.map(_*2).sortBy(x=>x,true)
# 过滤出大于等于5的元素
val rdd3=rdd2.filter(_>=5)
# 将元素以数组的方式在客户端显示
rdd3.collect
1.4.2.flatMap
# 通过并行化生成rdd
val rdd1 = sc.parallelize(Array("a b c", "d e f", "h i j"))
# 将rdd1里面的每一个元素先切分再压平
val rdd2=rdd1.flatMap(_.split(" "))
#将元素以数组的方式在客户端显示
rdd2.collect
1.4.3.union、intersection
#并行化生成两个rdd
val rdd1 = sc.parallelize(List(5, 6, 4, 3))
val rdd2 = sc.parallelize(List(1, 2, 3, 4))
# 求并集
val unionRdd=rdd1.union(rdd2)
# 求交集
val intersectRdd=rdd1.intersection(rdd2)
# 并集去重
val distinctRdd=unionRdd.distinct
# 在客户端显示结果集
unionRdd.collect
intersectRdd.collect
distinctRdd.collect
1.4.4.join、groupByKey
# 并行化生成两个rdd
val rdd1 = sc.parallelize(List(("tom", 1), ("jerry", 3), ("kitty", 2)))
val rdd2 = sc.parallelize(List(("jerry", 2), ("tom", 1), ("shuke", 2)))
# 联合操作求join
val rdd3=rdd1.join(rdd2)
rdd3.collect
# 求并集
val rdd4=rdd1.union(rdd2)
rdd4.collect
# 按key进行分组
val rdd5=rdd4.groupByKey
rdd5.collect
1.4.5.cogroup
# 并行化生成两个rdd
val rdd1 = sc.parallelize(List(("tom", 1), ("tom", 2), ("jerry", 3), ("kitty", 2)))
val rdd2 = sc.parallelize(List(("jerry", 2), ("tom", 1), ("jim", 2)))
# cogroup操作
val rdd3=rdd1.cogroup(rdd2)
# 在客户端显示结果集
rdd3.collect
1.4.6.reduce
# 并行化生成一个rdd
val rdd1 = sc.parallelize(List(1, 2, 3, 4, 5))
# reduce操作
val rdd2=rdd1.reduce(_+_)
1.4.7.reduceByKey、sortByKey
# 并行化生成两个rdd
val rdd1 = sc.parallelize(List(("tom", 1), ("jerry", 3), ("kitty", 2), ("shuke", 1)))
val rdd2 = sc.parallelize(List(("jerry", 2), ("tom", 3), ("shuke", 2), ("kitty", 5)))
# 求并集union
val rdd3=rdd1.union(rdd2)
rdd3.collect
# 按key进行聚合
val rdd4=rdd3.reduceByKey(_+_)
rdd4.collect
#按value的降序排序
val rdd5=rdd4.map(t=>(t._2,t._1)).sortByKey(false).map(t=>(t._2,t._1))
rdd5.collect
1.4.8.repartition、coalesce
# 并行化生成一个rdd(指定三个分区)
val rdd1 = sc.parallelize(1 to 10,3)
rdd1.getNumPartitions
# 利用repartition改变rdd1分区数
## 减少分区
rdd1.repartition(2).partitions.size
## 增加分区
rdd1.repartition(4).partitions.size
#利用coalesce改变rdd1分区数(只能减少分区)
##减少分区
rdd1.coalesce(2).partitions.size
1.5.RDD编程实战
1.5.1.通过spark实现点击流日志分析案例
1.5.1.1.点击流介绍
点击流数据是由网站日志中整理得到的,它可以比网站日志本身包含更多的信息 。更注重用户浏览网站的整个流程,网站日志中记录的用户点击就像是图上的“点”,而点击流更像是将这些“点”串起来形成的“线” 。可以把“点”认为是网站的Page(单个操作),而“线”则是访问网站的Session (一次会话完整操作)。
点(Page)的信息:URL、点击时间(Hit Time)、页面停留时间(Time on Page)、位于Session的第几步(Step),Sessionid(在关系数据库中可以用于跟Session表的外键关联)……
**线(Session)的信息:**Sessionid(唯一标识符)、访问来源(Referrers)、进入页面(Entrance)、离开页面(Exit)、开始时间(Begin Time)、结束时间(End Time)、访问时长(Time on Site)、访问页面数(Depth of Visit)、访问用户(Cookie)……
1.5.1.2.案例日志格式介绍
日志组成:
ip地址 用户Id cookieId 访问时间 请求方式 请求协议 响应状态码 流量 请求的url 客户端信息
1.5.1.3.创建项目
1.5.1.3.导入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>project_djl</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<scala.version>2.11.8</scala.version>
<hadoop.version>2.7.4</hadoop.version>
<spark.version>2.0.2</spark.version>
<mysql.version>5.1.38</mysql.version>
</properties>
<dependencies>
<!--scala依赖-->
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>${scala.version}</version>
</dependency>
<!--spark依赖-->
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.11</artifactId>
<version>${spark.version}</version>
</dependency>
<!--hadoop依赖-->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>${hadoop.version}</version>
</dependency>
<!--依赖mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
</dependencies>
<!--配置插件-->
<build>
<plugins>
<!--scala编译插件-->
<plugin>
<groupId>org.scala-tools</groupId>
<artifactId>maven-scala-plugin</artifactId>
<version>2.15.2</version>
<executions>
<execution>