目录

  • ​​spark outline​​
  • ​​RDD的创建和分区规则​​
  • ​​1.1 从集合中创建RDD​​
  • ​​1.2 从外部系统创建RDD​​
  • ​​2.1 内存分区规则和数据读取规则​​
  • ​​2.2 文件分区规则和数据读取规则​​

spark outline

​​大纲目录​​

RDD的创建和分区规则

1.1 从集合中创建RDD

从集合中创建RDD,主要提供了两种函数:​​parallelize和makeRDD​

需求:创建List集合(集合中元素1234),从集合中创建RDD,并打印输出

package com.xcu.bigdata.spark.core.pg02_rdd.pg021_rdd_create

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


object Spark01_CreateRDD_memory {
def main(args: Array[String]): Unit = {
//创建配置文件
/*
setMaster: 设置Spark环境的运行位置,如 local 或者 hdfs
[k]表示运行核数为k个,* 表示用本电脑所有核数
setAppName:设置当前应用程序的名字
*/
val conf: SparkConf = new SparkConf().setAppName("Spark01_CreateRDD_memory").setMaster("local[*]")
//创建SparkContext,该对象是提交的入口
val sc = new SparkContext(conf)
//创建一个集合对象
val list: List[Int] = List(1, 2, 3, 4)
//方式1:根据集合创建RDD
val rdd1: RDD[Int] = sc.parallelize(list)
//打印
rdd1.foreach(print)
//换行
println()
//方式2:根据集合创建RDD
val rdd2: RDD[Int] = sc.makeRDD(list)
rdd2.foreach(print)
//释放资源
sc.stop()
}
}

1.2 从外部系统创建RDD

外部存储系统:​​本地的文件系统,HDFS、HBase等​

目标:从本地文件系统,HDFS创建RDD

package com.xcu.bigdata.spark.core.pg02_rdd.pg021_rdd_create

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


object Spark02_CreateRDD_file {
def main(args: Array[String]): Unit = {
//创建配置文件
val conf: SparkConf = new SparkConf().setAppName("").setMaster("local[*]")
//创建SparkContext,该对象是提交的入口
val sc = new SparkContext(conf)
//从本地文件中读取数据,创建RDD
val rdd1: RDD[String] = sc.textFile("./input/word.txt")
//从hdfs上读取文件
val rdd2: RDD[String] = sc.textFile("hdfs://hadoop201:8020/input")
//打印输出
rdd1.foreach(println)
//释放资源
sc.stop()
}
}

2.1 内存分区规则和数据读取规则

​创建RDD的方式不一样​​,分区规则不一样

  • 切片规则
  1. ​默认的分区数​​取决于分配给应用的CPU的核数
  2. ​如果指定分区,​​那么最终分区数就为指定的数目
  • 数据读取规则
// 源码
def positions(length: Long, numSlices: Int): Iterator[(Int, Int)] = {
(0 until numSlices).iterator.map { i =>
val start = ((i * length) / numSlices).toInt
val end = (((i + 1) * length) / numSlices).toInt
(start, end)
}
}

数据读取举例:sc.makeRDD(List(1, 2, 3, 4, 5), numSlices = 3)

length:数据长度5,numSlices = 3,i为几号分区
根据以上代码
i ---> (start, end) ---> data
0 ---> (0, 1) ---> 1
1 ---> (1, 3) ---> 2 3
2 ---> (3, 5) ---> 4 5
package com.xcu.bigdata.spark.core.pg02_rdd.pg021_rdd_create

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

/**
* @Package : com.xcu.bigdata.spark.core.pg02_rdd.pg021_rdd_create
* @Author :
* @Desc : 从集合中创建RDD,并指定分区数
* -默认的分区数取决于分配给应用的CPU的核数
* -如果指定分区数,那么最终分区数就为指定的数目
*/
object Spark04_Partition_mem {
def main(args: Array[String]): Unit = {
//创建配置文件
val conf: SparkConf = new SparkConf().setAppName("").setMaster("local[*]")
//创建SparkContext,该对象是提交的入口
val sc = new SparkContext(conf)
//从集合中创建RDD
val rdd: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4, 5), numSlices = 3)
//保存成文件目的是为了查看分区数
rdd.saveAsTextFile("./output")
//释放资源
sc.stop()

}
}

2.2 文件分区规则和数据读取规则

从文件中创建RDD,​​采用的是Haoop的分区和数据读取规则:​

  • 文件切片规则:以字节方式来切片

切片举例:例如一个文件11个字节,在代码中设置minPartitions=2

minPartitions=2,即预计分为2个分区,但实际有可能不是2个分区

totalSize(文件总字节数) / numSlices(等于minPartitions)= 实际分区个数

11 / 2 = 5 … 1 (1 / 5 )与 0.1 做比较,如果(1 / 5 = 0.2)大0.1, 那么在开一个分区,如果(1 / 5 )小0.1,那么合并到最后一个分区,故实际分区个数为3个

​总结:​​如果指定分区数,那么最后分区数取决于总的字节数是否能够整除预计的最小分区数,并且剩余的字节数达到一个比率

​默认分区数取决于:​​math.min(分配给应用的CPU核数,2)

  • 数据读取规则:以行为单位来读取

数据读取举例:例如一个txt文件10个字节,minPartitions=4

  1. txt文件如下
1
2
3
4
  1. 显示回车和换行,并用符号@代替
# 数据以行的方式读取,但是读取过程中会考虑偏移量(数据的offset)
date=>offset
1@@ => 012
2@@ => 345
3@@ => 678
4 => 9
  1. 计算分区个数

10byte / 4 = 2byte … 2byte =>5个分区,意味着有5个分区,每个分区读取2个字节

每个分区的偏移量为
0 =>(0, 2)
1 =>(2, 4)
2 =>(4, 6)
3 =>(6, 8)
4 =>(8, 10)
  1. 读取的数据为
分区=>offset =>data
0 =>(0, 2)=> 1 # 实际读取到的offset 0 1 2
1 =>(2, 4)=> 2 # 实际读取到的offset 3 4,上一次读取数据时,把offset=2读走了
2 =>(4, 6)=> 3 # 实际读取到的offset 5 6
3 =>(6, 8)=> 空 # 实际读取到的offset 7 8
4 =>(8, 10)=> 4 # 实际读取到的offset 9
  1. 代码验证
package com.xcu.bigdata.spark.core.pg02_rdd.pg021_rdd_create

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

/**
* @Package : com.xcu.bigdata.spark.core.pg02_rdd.pg021_rdd_create
* @Author :
* @Desc : 从文件中创建RDD,分区规则和数据读取规则
*/
object Spark05_Partition_file {
def main(args: Array[String]): Unit = {
//创建配置文件
val conf: SparkConf = new SparkConf().setAppName("Spark05_Partition_file").setMaster("local[*]")
//创建SparkContext,该对象是提交的入口
val sc = new SparkContext(conf)
//从集合中创建RDD
val rdd: RDD[String] = sc.textFile("./input/w.txt", minPartitions = 4)
//保存成文件目的是为了查看分区数
rdd.saveAsTextFile("./output")
//释放资源
sc.stop()

}
}