Spark 基础知识

一、Spark 集群安装

  1. 修改 spark-env.sh 文件,在该配置文件中添加如下配置
export JAVA_HOME=/usr/java/jdk1.7.0_45
export SPARK_MASTER_IP=node1.itcast.cn
export SPARK_MASTER_PORT=7077
  1. 修改 slaves 文件,加入节点。

  2. 发送到其他机器,然后启动。

二、启动 spark shell

spark-shell --master spark://node1.itcast.cn:7077 --executor-memory 2g --total-executor-cores 2

参数说明

  • --master spark://node1.itcast.cn:7077 指定Master的地址
  • --executor-memory 2g 指定每个worker可用内存为2G
  • --total-executor-cores 2 指定整个集群使用的cup核数为2个

注意:

如果启动spark shell时没有指定master地址,但是也可以正常启动spark shell和执行spark shell中的程序,其实是启动了spark的local模式,该模式仅在本机启动一个进程,没有与集群建立联系。

二、 Spark RDD 编程

RDD 是弹性式分布式数据集,是 spark 对数据的核心抽象。 对 RDD 的操作包括两部分,转换操作(Transfermation)和行动操作(Action)

转换操作会由一个 RDD 生成一个新的 RDD。行动操作会对 RDD 计算出一个结果。 spark 对 RDD 是惰性求值的。转换操作并不会触发实际的 RDD 的计算,只有当第一次执行行动操作时才会计算。

2.1 RDD 的特性

  1. 一组分片(Partition. ,即数据集的基本组成单位。对于RDD来说,每个分片都会被一个计算任务处理,并决定并行计算的粒度。用户可以在创建RDD时指定RDD的分片个数,如果没有指定,那么就会采用默认值。默认值就是程序所分配到的CPU Core的数目。

  2. 一个计算每个分区的函数。Spark中RDD的计算是以分片为单位的,每个RDD都会实现compute函数以达到这个目的。compute函数会对迭代器进行复合,不需要保存每次计算的结果。

  3. RDD之间的依赖关系。RDD的每次转换都会生成一个新的RDD,所以RDD之间就会形成类似于流水线一样的前后依赖关系。在部分分区数据丢失时,Spark可以通过这个依赖关系重新计算丢失的分区数据,而不是对RDD的所有分区进行重新计算。

  4. 一个Partitioner,即RDD的分片函数。当前Spark中实现了两种类型的分片函数,一个是基于哈希的HashPartitioner,另外一个是基于范围的RangePartitioner。只有对于于key-value的RDD,才会有Partitioner,非key-value的RDD的Parititioner的值是None。Partitioner函数不但决定了RDD本身的分片数量,也决定了parent RDD Shuffle输出时的分片数量。

  5. 一个列表,存储存取每个Partition的优先位置(preferred location. 。对于一个HDFS文件来说,这个列表保存的就是每个Partition所在的块的位置。按照“移动数据不如移动计算”的理念,Spark在进行任务调度的时候,会尽可能地将计算任务分配到其所要处理数据块的存储位置。

2.2 创建 RDD 的两种方式

  1. 通过读取外部数据源加载
val rdd2 = sc.textFile("hdfs://node1.itcast.cn:9000/words.txt")
  1. 创建集合的方式加载。
val rdd1 = sc.parallelize(Array(1,2,3,4,5,6,7,8))

RDD 的两种算子

Transformation

RDD 不会触发真正的计算,只是记录下这些转换的动作,等到触发 Action 时才会提交任务进行计算。

常用的 Transformation 算子

函数名 目的 示例 结果
map() 将函数应用于 RDD 的每个元素,将返回值构成新的 RDD rdd.map(x => x + 1) {2, 3, 4, 4}
flatMap() 应用于 RDD 的每个元素,返回的迭代器的所有内容构成新的 RDD rdd.flatMap(x => x.to(3)) {1, 2, 3, 2, 3, 3, 3}
filter() 过滤 rdd.filter(x => x!= 1) {2, 3, 3}
distinct() 去重 rdd.distinct() {1, 2, 3}

对 {1, 2, 3, 3} 的 RDD 进行转化操作

函数名 目的 示例 结果
map() 将函数应用于 RDD 的每个元素,将返回值构成新的 RDD rdd.map(x => x + 1) {2, 3, 4, 4}
flatMap() 应用于 RDD 的每个元素,返回的迭代器的所有内容构成新的 RDD rdd.flatMap(x => x.to(3)) {1, 2, 3, 2, 3, 3, 3}
filter() 过滤 rdd.filter(x => x!= 1) {2, 3, 3}
distinct() 去重 rdd.distinct() {1, 2, 3}

对 {1, 2, 3} 和 {3, 4, 5} 进行转化操作

函数名 目的 示例 结果
union() 将两个 RDD 合并成一个 RDD rdd.union(other) {1, 2, 3, 3, 4, 5}
intersection() 求两个 RDD 中相同的元素 rdd.intersection(other) {3}
subtract() 移除一个 RDD 中的内容 rdd.subtract(other) {1, 2}

Action

真正触发计算的算子。

常用的 Action 算子

函数名 目的
count 统计rdd中的元素个数
collect 将集群上的数据拉取到本地进行遍历(不推荐使用)
saveAsTextFile 直接将rdd中的数据保存在hdfs中
take 将远程rdd的前n个数据拉取到本地

三、RDD 的依赖关系

RDD和它依赖的父RDD(s)的关系有两种不同的类型,即窄依赖(narrow dependency)和宽依赖(wide dependency)。

Spark 基础知识_第1张图片
image1

3.1 宽依赖

宽依赖指的是多个子 RDD 的 Partition 会依赖同一个父 RDD 的 Partition。一般情况下需要将宽依赖的算子进行缓存。

会形成宽依赖的算子:
reduceByKey, combineByKey, groupByKey, join, aggregateByKey

3.2 窄依赖

窄依赖指的是每一个父 RDD 的 Partition 最多被子 RDD 的一个 Partition 使用

会形成窄依赖的算子:
map, flatMap, filter, union

注意: 关于 join 的算子,如果每个Partition仅仅和已知的、特定的Partition进行join,那么这个依赖关系也是窄依赖。对于需要parent RDD的所有Partition进行join的转换,也是需要Shuffle,这类join的依赖就是宽依赖而不是前面提到的窄依赖了。

3.3 如何判断是否为宽依赖

  1. 一般情况下 value 型的算子产生的 RDD 是窄依赖,key-value 型的算子产生的 RDD 是宽依赖。
  2. 通过调用dependencies来判断是那种分区依赖关系
Spark 基础知识_第2张图片
image

3.3 将 RDD 划分成宽依赖和窄依赖的原因

  1. 首先,narrow dependencies可以支持在同一个cluster node上,以pipeline形式执行多条命令,例如在执行了map后,紧接着执行filter。相反,shuffle / wide dependencies 需要所有的父分区都是可用的,可能还需要调用类似MapReduce之类的操作进行跨节点传递。

  2. 其次,则是从失败恢复的角度考虑。 narrow dependencies的失败恢复更有效,因为它只需要重新计算丢失的parent partition即可,而且可以并行地在不同节点进行重计算。相反,shuffle / wide dependencies 牵涉RDD各级的多个parent partition。

3.3 Lineage 血统

RDD只支持粗粒度转换,即在大量记录上执行的单个操作。将创建RDD的一系列Lineage(即血统)记录下来,以便恢复丢失的分区。RDD的Lineage会记录RDD的元数据信息和转换行为,当该RDD的部分分区数据丢失时,它可以根据这些信息来重新运算和恢复丢失的数据分区。

四、RDD 的缓存

默认情况下 RDD 会在每次行动操作时重新计算。如果想对这个 RDD 进行重用, 可以使用 RDD.persist() 或者 cache() 方法将 RDD 缓存下来,这样就可以重用。但是并不是这两个方法被调用时立即缓存,而是触发后面的action时,该RDD将会被缓存在计算节点的内存中,并供后面重用。

持久化缓存的级别

级别 使用的空间 CPU 时间 是否在内存中 是否在磁盘上 备注
MEMORY_ONLY
MEMORY_ONLY_SER
MEMORY_AND_DISK 中等 部分 部分 如果内存放不下,溢写到磁盘
MEMORY_AND_DISK_SER 部分 部分 如果内存放不下,溢写到磁盘,内存中存放序列化后的数据
DISK_ONLY

checkpoint

为了防止 RDD 的缓存中的数据丢失,从而导致 RDD 的重算带来的巨大开销。可以将认为比较重要的 RDD 计算的结果进行 checkpoint,这样会将计算得到的结果写入到分布式文件系统中,如 HDFS。==建议在调用 checkpoint 之前先将 RDD 的结果 cache 一下。==

注意:checkpoint 之后 RDD 的所有依赖关系将会被删除,恢复数据将会从 HDFS 中读取文件进行恢复,RDD 的依赖关系将会重新开始计算。

你可能感兴趣的:(Spark 基础知识)