Spark期末复习(二)RDD

spark核心编程RDD

RDD概述

弹性分布式数据集,代表一个弹性的、不可变的、可分区、里面的元素可并行计算的集合

  • 弹性
    • 存储的弹性:内存与磁盘相互切换
    • 容错的弹性:数据丢失可以自动恢复
    • 计算的弹性:计算出错重试机制
    • 分片的弹性:可根据需要重新分片
  • 分布式:数据存储在大数据集群的不同节点上
  • 数据集:RDD封装了计算逻辑,并不保存数据
  • 数据抽象:RDD是一个抽象类,需要子类具体实现
  • 不可变:RDD封装了计算逻辑,是不可以改变的,想要改变,只能产生新的RDD,在新的RDD里面封装计算逻辑
  • 可分区、并行计算

五大属性:

  • Partition:一组分片,数据集的基本组成单位。可以在创建RDD时指定RDD的分片个数。每个RDD会被一个计算任务处理,相互之间没有影响
  • 分区函数:
  • RDD之间的依赖关系:依赖关系分为宽依赖和窄依赖。部分数据丢失后,可以通过依赖关系重新计算
  • Partitioner:分片函数/分区器:两种类型的分区函数:
    • 基于哈希的Hash Partition,
    • 基于范围的Range Partition。
    • 只有对于key-value的RDD才会有partitioner。
  • Preferred location:每个partition的优先位置

执行原理
spark执行时,先申请资源,然后将应用程序的数据处理逻辑分解为一个一个的计算任务,将计算任务分发到已经分配了资源的计算节点上,按照指定的计算模型进行数据计算,最后得出计算结果

RDD工作原理(在yarn上)

  • 启动yarn集群
  • spark通过申请资源申请调度节点和计算节点
  • spark框架根据需求将计算逻辑根据分区划分成不同的任务
  • 调度节点将任务根据计算节点状态发送到队形的计算节点进行计算

RDD的创建

  1. 从文件中创建::
    • sc.textfile():以行为单位读取数据,读取的数据都是字符串
    • sc.wholeTextFiles():以文件为单位
  2. 从内存中读取数据
  3. 通过并行集合(数组)创建:
    1. sc.parallelize()
    2. sc.markRDD()
  4. 分区:一行一行读取并分区,与子结束没关系,与偏移量有关系

RDD的操作

transformation

只发生转换

  • filter:
    val lines: RDD[String] = sc.textFile("data/01.txt")
    val result: RDD[String] = lines.filter(lines => lines.contains("spark"))
    result.collect().foreach(println)
    
  • map:一个数据一个数据执行,分区内顺序执行,分区之间并行
	val list: List[Int] = List(1, 2, 3)
    val rdd: RDD[Int] = sc.makeRDD(list)
    val result2: RDD[Int] = rdd.map(_ * 2)
    result2.collect().foreach(println)
  • mappartition:以分区为单位转换操作,将整个分区加载到内存中进行引用

    • 传递一个迭代器,返回一个迭代器
    • 内存占用较高
  • mapPartitionWithIndex

  • flatmap:
    与map的区别:

  • groupByKey:返回一个新的(K,Iterable)形式的数据集(无参数传入)

	val lines: RDD[String] = sc.textFile("data/01.txt")
    val words: RDD[(String, Iterable[Int])] = lines.flatMap(_.split(" "))
          .map(x => (x, 1))
          .groupByKey()
	words.collect().foreach(println)

运行结果:
在这里插入图片描述

  • reduceByKey(func)
    val lines: RDD[String] = sc.textFile("data/01.txt")
    val words: RDD[(String, Int)] = lines.flatMap(_.split(" "))
      .map(x => (x, 1))
      .reduceByKey((a, b) => a + b)
    words.collect().foreach(println)

运行结果:
在这里插入图片描述

Action

  • count():统计RDD个数
  • collect():执行
  • take(int):
  • foreach():对每一个RDD执行一样的操作
	val array: Array[Int] = Array(1, 2, 3, 4, 5)
    val rdd: RDD[Int] = sc.parallelize(array)
    val count: Long = rdd.count()
    val first: Int = rdd.first()
    val take: Array[Int] = rdd.take(1)
    val reduce: Int = rdd.reduce((a, b) => a + b)
    println(count)
    println(first)
    println(reduce)

运行结果:
在这里插入图片描述

惰性机制:调用action中的函数才执行计算

RDD分区

分区可以增加程序的并行度,实现分布式计算

  • 分区原则:分区个数 = 集群中CPU核心数目
  • 如何分区:
    • local模式:默认本地机器CPU数目
    • Standlone模式/yarn模式:默认值大于2
    • Apache Mesos模式:默认分区数为8
    • 手动分区:
      1. 创建RDD时指定分区数量:sc.textFile(path,partitionNum)
      2. 使用reparitition方法重新设置分区:val rdd = data.repartition(2)mo

RDD的持久化和checkpoint

持久化

为了重用,或数据计算时间较长或数据较重要时使用持久化
使用cache() 或 persist()
cache默认保存到内存中
存储到磁盘:persist(storage Level.DISK_ONLT)
级别有不同:

  • 仅在内存中缓存,且溢出不写入磁盘,丢弃:MEMORY_NOLY
  • 仅写入磁盘:DISK_ONLY
  • 仅写入磁盘,副本为2:MEMORY_ONLY_2
  • 写入内存,内存不够溢写磁盘:MEMORY_AND_DISK
    val lines: RDD[String] = sc.textFile("data/01.txt",2)
    val wordcount: RDD[(String, Int)] = lines.flatMap(_.split(" ")).map(x => (x, 1))
      .reduceByKey((a, b) => a + b)
    wordcount.cache()
//    wordcount.persist()
    wordcount.collect().foreach(println)

check point

检查点路径中保存的文件,执行完毕后不会被删除
设置保存路径:
sc.setCheckpointDir(“”)

check point与 cache对区别:

  • cache将数据存储在内存中进行数据重用,会在血缘关系中添加新的依赖,出现问题从头读取
  • persist将数据存储在磁盘文件中进行重用,涉及到io,性能较低,但数据安全。如果job执行完毕,临时保存的数据文件就会丢失
  • check point将数据长久地保存在磁盘文件中进行数据重用,为了数据安全,一般会独立进行。为了提高效率,一般需要与cache联合使用:
rdd.cache()    
rdd.checkpoint()
  • checkpoint在执行过程中会切断血缘关系,重新建立新的血缘关系,等同于改变数据源

RDD的分区

**分区的作用:**增加程序的并行度,实现分布式计算

分区个数 = 集群中CPU核心数目
但Apache Mesos模式中,分区个数默认为8

设置分区数:创建RDD时可手动指定:
val rdd = parallelize(array,2)
或通过repartition方法修改分区个数

    val size: Int = wordcount.partitions.size
    val repartitionRDD: RDD[(String, Int)] = wordcount.repartition(4)
    val size2: Int = repartitionRDD.partitions.size

    println(size)
    println(size2)

运行结果:在这里插入图片描述

分区类型

  • HashPartitioner(哈希分区)
  • RangerPartitioner(区域分区)
  • 自定义分区

自定义分区器:

  • 继承org.apache.spark.Partitioner
  • numPartitions:Int 返回创建出来的分区数
  • getPartiton(key: Any):Int 返回分区索引,从0开始
  • 定义分区的函数
  • 示例:根据key值的最后一位数字,写到不同的文件中:
package com.dw.rdd

import org.apache.spark.rdd.RDD
import org.apache.spark.{Partitioner, SparkConf, SparkContext}

object testPartitioner {
  def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("MyPartitioner")
    val sc: SparkContext = new SparkContext(conf)

    val data: RDD[Int] = sc.parallelize(1 to 20)
    data.map((_, 1)).partitionBy(new Mypartitioner(10)).map(_._1).saveAsTextFile("data/partitioner")
  }

}

class Mypartitioner(numPartition: Int) extends Partitioner{
  override def numPartitions: Int = numPartition

  override def getPartition(key: Any): Int = {
    key.toString.toInt % 10
  }
}

键值对RDD

键值对RDD的创建

  1. 读取文件 => flatmap分词 => map构成键值对(_,1)
  2. 读列表 => map构成键值对(_,1)

##键值对RDD的操作
3. reduceByKey:相同key的值进行汇总求和``
4. groupByKey:相同key的值进行分组,但不汇总求和
5. 分别实现wordcount:
通过reduceByKey实现:

    rdd = sc.textfile(“”)
    word = rdd.flatmap(line => line.split(‘ ‘)).map((_,1))
    WordCount = word.reduceByKey(_+_)

通过groupByKey实现:

    ```
    rdd = sc.textfile(“”)
    word = rdd.flatmap(line => line.split(‘ ‘)).map((_,1))
    WordCount = word.groupByKey.map(x => (_.1,X.2.sum))
    ```
  1. 将所有的key返回生成一个新的RDD:rdd.keys
  2. 将所有的value返回生成一个新的RDD:rdd.values
  3. sortByKey():返回一个根据键排序的RDD
  4. sortBy():自定义根据什么东西排序
  5. map Values(func):对每一个value都应用func函数
  6. join:把RDD中元素key相同的进行连接

RDD的数据读取

本地文件系统数据读写

读:sc.textFile(“”)
写:rdd.saveAsTextFile(“”)

分布式文件系统HDFS的数据读取

读:sc.textFile(“”)
写:rdd.saveAsTextFile(“”)

json文件的数据读写

读:sc.textFile(“”)
解析:JSON.parseFull(JsonString:String)
解析成功返回:Some(map: Map[String,Any])
解析失败返回:None

综合案例

综合案例

package com.dw.rdd

import org.apache.spark.rdd.RDD
import org.apache.spark.{HashPartitioner, SparkConf, SparkContext}

object sort {
  def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("sort")
    val sc: SparkContext = new SparkContext(conf)
    sc.setCheckpointDir("checkpoint")

    var index: Int = 0
    val lines: RDD[(String, String)] = sc.wholeTextFiles("data/sort", 3)
    val result: RDD[(Int, Int)] = lines.filter(_.trim().length > 0.)
      .map(x => (x.trim().toInt, " "))
      .partitionBy(new HashPartitioner(1))
      .sortByKey()
      .map(x => {
        index = index + 11
        (index, x._1)
      })
    result.saveAsTextFile("data/sortoutput")
//    result.collect().foreach(println)
    sc.stop()
  }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值