RDD的作用。
从以上流程可以看出 RDD 在整个流程中主要用于将逻辑进行封装,并生成 Task 发送给
Executor 节点执行计算,接下来我们就一起看看 Spark 框架中 RDD 是具体是如何进行数据
处理的。
---31---
用的比较多的是在内存中创建和文件中创建。
在集合中创建RDD
seq和set的区别:https://blog.csdn.net/qq_36932624/article/details/83060446
package com.atguigu.bigdata.spark.core.rdd.builder
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Spark01_RDD_Memory {
def main(args: Array[String]): Unit = {
// TODO 准备环境
// *表示当前系统的最大可用核数 不写就是单线程
val sparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
val sc = new SparkContext(sparkConf)
// TODO 创建RDD
// 从内存中创建RDD,将内存中集合的数据作为处理的数据源
val seq = Seq[Int](1,2,3,4)
// parallelize : 并行
//val rdd: RDD[Int] = sc.parallelize(seq)
// makeRDD方法在底层实现时其实就是调用了rdd对象的parallelize方法。
val rdd: RDD[Int] = sc.makeRDD(seq)
rdd.collect().foreach(println)
// TODO 关闭环境
sc.stop()
}
}
local的*表示的什么呢,表示的是当前本机的核心数。
---32---
默认是以当前目录的根路径为基准的。
package com.atguigu.bigdata.spark.core.rdd.builder
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Spark02_RDD_File {
def main(args: Array[String]): Unit = {
// TODO 准备环境
val sparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
val sc = new SparkContext(sparkConf)
// TODO 创建RDD
// 从文件中创建RDD,将文件中的数据作为处理的数据源
// path路径默认以当前环境的根路径为基准。可以写绝对路径,也可以写相对路径
//sc.textFile("D:\\mineworkspace\\idea\\classes\\atguigu-classes\\datas\\1.txt")
// 相对路径是当前的项目的根
//val rdd: RDD[String] = sc.textFile("datas/1.txt")
// path路径可以是文件的具体路径,也可以目录名称
//val rdd = sc.textFile("datas")
// path路径还可以使用通配符 *
//val rdd = sc.textFile("datas/1*.txt")
// path还可以是分布式存储系统路径:HDFS
val rdd = sc.textFile("hdfs://linux1:8020/test.txt")
rdd.collect().foreach(println)
// TODO 关闭环境
sc.stop()
}
}
---33---
以文件为单位读取数据:
package com.atguigu.bigdata.spark.core.rdd.builder
import org.apache.spark.{SparkConf, SparkContext}
object Spark02_RDD_File1 {
def main(args: Array[String]): Unit = {
// TODO 准备环境
val sparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
val sc = new SparkContext(sparkConf)
// TODO 创建RDD
// 从文件中创建RDD,将文件中的数据作为处理的数据源
// textFile : 以行为单位来读取数据,读取的数据都是字符串
// wholeTextFiles : 以文件为单位读取数据
// 读取的结果表示为元组,第一个元素表示文件路径,第二个元素表示文件内容
val rdd = sc.wholeTextFiles("datas")
rdd.collect().foreach(println)
// TODO 关闭环境
sc.stop()
}
}
读取内容的样子:
(file:/G:/CODE_MY/sggBigData/atguigu-classes/datas/1.txt,hello word
hello spark)
(file:/G:/CODE_MY/sggBigData/atguigu-classes/datas/2.txt,hello word1
hello spark1)
---34---
关于并行度:
几个分区几个task,发给executor。
z
不传的话就是按照核心数分区的。
还是可以配置的:
代码:
package com.atguigu.bigdata.spark.core.rdd.builder
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Spark01_RDD_Memory_Par {
def main(args: Array[String]): Unit = {
// TODO 准备环境
val sparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
sparkConf.set("spark.default.parallelism", "5")
val sc = new SparkContext(sparkConf)
// TODO 创建RDD
// RDD的并行度 & 分区
// makeRDD方法可以传递第二个参数,这个参数表示分区的数量
// 第二个参数可以不传递的,那么makeRDD方法会使用默认值 : defaultParallelism(默认并行度)
// scheduler.conf.getInt("spark.default.parallelism", totalCores)
// spark在默认情况下,从配置对象中获取配置参数:spark.default.parallelism
// 如果获取不到,那么使用totalCores属性,这个属性取值为当前运行环境的最大可用核数
//val rdd = sc.makeRDD(List(1,2,3,4),2)
val rdd = sc.makeRDD(List(1,2,3,4))
// 将处理的数据保存成分区文件
rdd.saveAsTextFile("output")
// TODO 关闭环境
sc.stop()
}
}
--------------------------------
并行就是分区。
并行就是分区。
分区之间是独立的,有独立的task。task发给executor。
一个executor三个task发过去只能有一个执行。
默认的并行度,不传的话就是cpu的核心数量。
package com.atguigu.bigdata.spark.core.rdd.builder
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Spark01_RDD_Memory_Par {
def main(args: Array[String]): Unit = {
// TODO 准备环境
val sparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
sparkConf.set("spark.default.parallelism", "2")
val sc = new SparkContext(sparkConf)
// TODO 创建RDD
// RDD的并行度 & 分区
// makeRDD方法可以传递第二个参数,这个参数表示分区的数量
// 第二个参数可以不传递的,那么makeRDD方法会使用默认值 : defaultParallelism(默认并行度)
// scheduler.conf.getInt("spark.default.parallelism", totalCores)
// spark在默认情况下,从配置对象中获取配置参数:spark.default.parallelism
// 如果获取不到,那么使用totalCores属性,这个属性取值为当前运行环境的最大可用核数
//val rdd = sc.makeRDD(List(1,2,3,4),2)
val rdd = sc.makeRDD(List(1,2,3,4))
// 将处理的数据保存成分区文件
rdd.saveAsTextFile("output")
// TODO 关闭环境
sc.stop()
}
}
---35---
数据不多但是分区比较多该怎么办呢,数据是如何放在分区里面的呢?
package com.atguigu.bigdata.spark.core.rdd.builder
import org.apache.spark.{SparkConf, SparkContext}
object Spark01_RDD_Memory_Par1 {
def main(args: Array[String]): Unit = {
// TODO 准备环境
val sparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
val sc = new SparkContext(sparkConf)
// TODO 创建RDD
// 【1,2】,【3,4】
//val rdd = sc.makeRDD(List(1,2,3,4), 2)
// 【1】,【2】,【3,4】 不均匀的话是怎么存放的呢?
//val rdd = sc.makeRDD(List(1,2,3,4), 3)
// 【1】,【2,3】,【4,5】
val rdd = sc.makeRDD(List(1,2,3,4,5), 3)
// 将处理的数据保存成分区文件
rdd.saveAsTextFile("output")
// TODO 关闭环境
sc.stop()
}
}
分区的规律是什么呢?
看下slice切分的思想,数据是12345,分区是3:
接下来的功能:
这个是源码 就是这个规律。
1 2 3 4 5 3
0=>(0,1) 1
1=>(1,3) 2 3
2=>(3,5) 4 5
注意一点,这个效果是一样的: sparkConf.set("spark.default.parallelism", "2")
---36---
在文件中读取数据是如何分区的呢?
我们点进去源码:
点进去:
package com.atguigu.bigdata.spark.core.rdd.builder
import org.apache.spark.{SparkConf, SparkContext}
object Spark02_RDD_File_Par {
def main(args: Array[String]): Unit = {
// TODO 准备环境
val sparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
val sc = new SparkContext(sparkConf)
// TODO 创建RDD
// textFile可以将文件作为数据处理的数据源,默认也可以设定分区。
// minPartitions : 最小分区数量
// math.min(defaultParallelism, 2)
//val rdd = sc.textFile("datas/1.txt")
// 如果不想使用默认的分区数量,可以通过第二个参数指定分区数
// Spark读取文件,底层其实使用的就是Hadoop的读取方式
// 分区数量的计算方式:
// totalSize = 7
// goalSize = 7 / 2 = 3(byte)
// 7 / 3 = 2...1 (1.1) + 1 = 3(分区)
//
val rdd = sc.textFile("datas/1.txt", 2)
rdd.saveAsTextFile("output")
// TODO 关闭环境
sc.stop()
}
}
真正的分区可能比最小分区的值要大。
分区数量是怎么算出来的呢?
所有的读取的文件统计字节数:
字节数是7.
7/2=3,表示每个分区存放三个字节。
7/3=2...1:2个分区+1,1/3>0.1则产生新的分区,剩余的数占分区的字节数的多少。
3个分区。
---37---
文件读取的话,数据是如何存储的呢?
package com.atguigu.bigdata.spark.core.rdd.builder
import org.apache.spark.{SparkConf, SparkContext}
object Spark02_RDD_File_Par1 {
def main(args: Array[String]): Unit = {
// TODO 准备环境
val sparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
val sc = new SparkContext(sparkConf)
// TODO 创建RDD
// TODO 数据分区的分配
// 1. 数据以行为单位进行读取
// spark读取文件,采用的是hadoop的方式读取,所以一行一行读取,和字节数没有关系
// 2. 数据读取时以偏移量为单位,偏移量不会被重复读取
/*
1@@ => 012
2@@ => 345
3 => 6
*/
// 3. 数据分区的偏移量范围的计算
// 0 => [0, 3] => 12
// 1 => [3, 6] => 3
// 2 => [6, 7] =>
// 【1,2】,【3】,【】
val rdd = sc.textFile("datas/1.txt", 2)
rdd.saveAsTextFile("output")
// TODO 关闭环境
sc.stop()
}
}
读取是一行一行读取的。
这个文件里面是123
---38---
这个文件里面是
1234567
89
0
代码:
package com.atguigu.bigdata.spark.core.rdd.builder
import org.apache.spark.{SparkConf, SparkContext}
object Spark03_RDD_File_Par2 {
def main(args: Array[String]): Unit = {
// TODO 准备环境
val sparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
val sc = new SparkContext(sparkConf)
// TODO 创建RDD
// 14byte / 2 = 7byte
// 14 / 7 = 2(分区)
/*
1234567@@ => 012345678
89@@ => 9101112
0 => 13
[0, 7] => 1234567
[7, 14] => 890
*/
// 如果数据源为多个文件,那么计算分区时以文件为单位进行分区
val rdd = sc.textFile("datas/word.txt", 2)
rdd.saveAsTextFile("output")
// TODO 关闭环境
sc.stop()
}
}
---39---
RDD的方法。
1.转换,就是旧的RDD转换为新的RDD,就是flatmap和map等。
2.行动:触发作业的执行,collect。
RDD的算子分为两种:转换和行动算子
---40---
map算子:
都可以的:
代码:
package com.atguigu.bigdata.spark.core.rdd.operator.transform
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Spark01_RDD_Operator_Transform {
def main(args: Array[String]): Unit = {
val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Operator")
val sc = new SparkContext(sparkConf)
// TODO 算子 - map
val rdd = sc.makeRDD(List(1,2,3,4))
// 1,2,3,4
// 2,4,6,8
// 转换函数 在函数里面得方法就是函数
def mapFunction(num:Int): Int = {
num * 2
}
var fun = (i:Int)=>i*2;
val mapRDD: RDD[Int] = rdd.map(fun)
//val mapRDD: RDD[Int] = rdd.map((num:Int)=>{num*2})
//val mapRDD: RDD[Int] = rdd.map((num:Int)=>num*2)
//val mapRDD: RDD[Int] = rdd.map((num)=>num*2)
//val mapRDD: RDD[Int] = rdd.map(num=>num*2)
// val mapRDD: RDD[Int] = rdd.map(_*2)
mapRDD.collect().foreach(println)
sc.stop()
}
}
---41---
我们解析服务器的日志信息:
package com.atguigu.bigdata.spark.core.rdd.operator.transform
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Spark01_RDD_Operator_Transform_Test {
def main(args: Array[String]): Unit = {
val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Operator")
val sc = new SparkContext(sparkConf)
// TODO 算子 - map
val rdd = sc.textFile("datas1/apache.log")
// 长的字符串
// 短的字符串
val mapRDD: RDD[String] = rdd.map(
line => {
val datas = line.split(" ")
datas(6)
}
)
mapRDD.collect().foreach(println)
sc.stop()
}
}
---42---
如何体现rdd方法的并行计算:
代码:
package com.atguigu.bigdata.spark.core.rdd.operator.transform
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Spark01_RDD_Operator_Transform_Par {
def main(args: Array[String]): Unit = {
val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Operator")
val sc = new SparkContext(sparkConf)
// TODO 算子 - map
// 1. rdd的计算一个分区内的数据是一个一个执行逻辑
// 只有前面一个数据全部的逻辑执行完毕后,才会执行下一个数据。
// 分区内数据的执行是有序的。
// 2. 不同分区数据计算是无序的。
val rdd = sc.makeRDD(List(1,2,3,4),2)
val mapRDD = rdd.map(
num => {
println(">>>>>>>> " + num)
num
}
)
val mapRDD1 = mapRDD.map(
num => {
println("######" + num)
num
}
)
mapRDD1.collect()
sc.stop()
}
}
按照老师的演示:
我们看下这个其实是乱的。
我们改为一个分区:
RDD一个分区内的数据是一个一个执行的逻辑的,前面逻辑执行完才会执行下一个,分区内的数据是有序的,不同分区的数据是无序的。
两个分区的数据一定是这样的。一个分区是先走1 再走2的,1和3谁先执行是不一定的。
---43---
单个分区是串行的,效率很低的,我们用缓冲区一个分区的数据全部拿到了之后再做操作可以。
spark算子的缓冲区操作:
这个内存小数据量大的话可能会有问题,会将分区加载到内存,存在引用的话是不会释放的额。
package com.atguigu.bigdata.spark.core.rdd.operator.transform
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Spark02_RDD_Operator_Transform {
def main(args: Array[String]): Unit = {
val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Operator")
val sc = new SparkContext(sparkConf)
// TODO 算子 - mapPartitions
val rdd = sc.makeRDD(List(1,2,3,4), 2)
// mapPartitions : 可以以分区为单位进行数据转换操作
// 但是会将整个分区的数据加载到内存进行引用
// 如果处理完的数据是不会被释放掉,存在对象的引用。
// 在内存较小,数据量较大的场合下,容易出现内存溢出。
val mpRDD: RDD[Int] = rdd.mapPartitions(
iter => {
// 有多少个分区这个箭头执行都多少次
println(">>>>>>>>>>")
iter.map(_ * 2)
}
)
mpRDD.collect().foreach(println)
sc.stop()
}
}
分析结果:有多少个分区箭头执行多少次的
---44---
小功能,获取每个分区的最大值:
package com.atguigu.bigdata.spark.core.rdd.operator.transform
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Spark02_RDD_Operator_Transform_Test {
def main(args: Array[String]): Unit = {
val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Operator")
val sc = new SparkContext(sparkConf)
// TODO 算子 - mapPartitions-
val rdd = sc.makeRDD(List(1,2,3,4), 2)
// 【1,2】,【3,4】
// 【2】,【4】
val mpRDD = rdd.mapPartitions(
iter => {
List(iter.max).iterator
}
)
mpRDD.collect().foreach(println)
sc.stop()
}
}
---45---
map和MapPartition
---46---
分区之间是无序的。
分区索引。
如何看下数据在哪个分区里面?
---47---
扁平映射:
---48---
再次做一个小功能,还是扁平化操作,3不是一个集合,如何把3拆分开呢:
---49---
作用是什么?
需求:分区取最大值,两个分区最大值求和
---50---
理解分区的不变的含义:
package com.atguigu.bigdata.spark.core.rdd.operator.transform
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Spark01_RDD_Operator_Transform_Part {
def main(args: Array[String]): Unit = {
val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Operator")
val sc = new SparkContext(sparkConf)
// TODO 算子 - map
val rdd = sc.makeRDD(List(1,2,3,4),2)
// 【1,2】,【3,4】
rdd.saveAsTextFile("output")
val mapRDD = rdd.map(_*2)
// 【2,4】,【6,8】
mapRDD.saveAsTextFile("output1")
sc.stop()
}
}
---51---
groupby:
package com.atguigu.bigdata.spark.core.rdd.operator.transform
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Spark06_RDD_Operator_Transform {
def main(args: Array[String]): Unit = {
val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Operator")
val sc = new SparkContext(sparkConf)
// TODO 算子 - groupBy
val rdd : RDD[Int] = sc.makeRDD(List(1,2,3,4), 2)
// groupBy会将数据源中的每一个数据进行分组判断,根据返回的分组key进行分组
// 相同的key值的数据会放置在一个组中
def groupFunction(num:Int) = {
num % 2
}
val groupRDD: RDD[(Int, Iterable[Int])] = rdd.groupBy(groupFunction)
groupRDD.collect().foreach(println)
sc.stop()
}
}
package com.atguigu.bigdata.spark.core.rdd.operator.transform
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Spark06_RDD_Operator_Transform1 {
def main(args: Array[String]): Unit = {
val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Operator")
val sc = new SparkContext(sparkConf)
// TODO 算子 - groupBy
val rdd = sc.makeRDD(List("Hello", "Spark", "Scala", "Hadoop"), 2)
// 分组和分区没有必然的关系
val groupRDD = rdd.groupBy(_.charAt(0))
groupRDD.collect().foreach(println)
sc.stop()
}
}
---52---
分组和分区是没有必然的联系的。
shuffle来袭,数据被打乱了。
---53---
获取每个时间段的访问量:
时间段的访问量:
package com.atguigu.bigdata.spark.core.rdd.operator.transform
import java.text.SimpleDateFormat
import java.util.Date
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Spark06_RDD_Operator_Transform_Test {
def main(args: Array[String]): Unit = {
val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Operator")
val sc = new SparkContext(sparkConf)
// TODO 算子 - groupBy
val rdd = sc.textFile("datas1/apache.log")
// groupBy返回的是一个迭代器
val timeRDD: RDD[(String, Iterable[(String, Int)])] = rdd.map(
line => {
val datas = line.split(" ")
// 数组取第几位
val time = datas(3)
//time.substring(0, )
val sdf = new SimpleDateFormat("dd/MM/yyyy:HH:mm:ss")
val date: Date = sdf.parse(time)
val sdf1 = new SimpleDateFormat("HH")
val hour: String = sdf1.format(date)
(hour, 1)
}
).groupBy(_._1)
timeRDD.map{
case ( hour, iter ) => {
(hour, iter.size)
}
}.collect.foreach(println)
sc.stop()
}
}
---54---
filter:
package com.atguigu.bigdata.spark.core.rdd.operator.transform
import java.text.SimpleDateFormat
import java.util.Date
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Spark07_RDD_Operator_Transform {
def main(args: Array[String]): Unit = {
val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Operator")
val sc = new SparkContext(sparkConf)
// TODO 算子 - filter
val rdd = sc.makeRDD(List(1,2,3,4))
val filterRDD: RDD[Int] = rdd.filter(num=>num%2!=0)
filterRDD.collect().foreach(println)
sc.stop()
}
}
注意一点:
将数据根据指定的规则进行筛选过滤,符合规则的数据保留,不符合规则的数据丢弃。
当数据进行筛选过滤后,分区不变,但是分区内的数据可能不均衡,生产环境下,可能会出
现数据倾斜。
小功能,过滤出来特定日期的数据
cpackage com.atguigu.bigdata.spark.core.rdd.operator.transform
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Spark07_RDD_Operator_Transform_Test {
def main(args: Array[String]): Unit = {
val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Operator")
val sc = new SparkContext(sparkConf)
// TODO 算子 - filter
val rdd = sc.textFile("datas/apache.log")
rdd.filter(
line => {
val datas = line.split(" ")
val time = datas(3)
time.startsWith("17/05/2015")
}
).collect().foreach(println)
sc.stop()
}
}
---55---
sample:抽取样本的数据
代码:
package com.atguigu.bigdata.spark.core.rdd.operator.transform
import org.apache.spark.{SparkConf, SparkContext}
object Spark08_RDD_Operator_Transform {
def main(args: Array[String]): Unit = {
val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Operator")
val sc = new SparkContext(sparkConf)
// TODO 算子 - filter
val rdd = sc.makeRDD(List(1,2,3,4,5,6,7,8,9,10))
// sample算子需要传递三个参数
// 1. 第一个参数表示,抽取数据后是否将数据返回 true(放回),false(丢弃)
// 2. 第二个参数表示,
// 如果抽取不放回的场合:数据源中每条数据被抽取的概率,基准值的概念
// 如果抽取放回的场合:表示数据源中的每条数据被抽取的可能次数
// 3. 第三个参数表示,抽取数据时随机算法的种子
// 如果不传递第三个参数,那么使用的是当前系统时间
println(rdd.sample(
false,
0.4
// 1
).collect().mkString(","))
// println(rdd.sample(
// true,
// 2
// //1
// ).collect().mkString(","))
sc.stop()
}
}
第一种情况:
println(rdd.sample(
false,
0.4,
1
返回值:种子确定好每个数据的额概率就被确定好了,每次的值是相同的。
第二种情况:数据每次是不一样的
println(rdd.sample(
false,
0.4
// 1
).collect().mkString(","))
第三种情况:抽取完的数据是要放回去的
2指的是抽取的次数可能是2次。
println(rdd.sample(
true,
2
//1
).collect().mkString(","))
我们在数据的倾斜可能会使用的。
---56---
package com.atguigu.bigdata.spark.core.rdd.operator.transform
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Spark09_RDD_Operator_Transform {
def main(args: Array[String]): Unit = {
val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Operator")
val sc = new SparkContext(sparkConf)
// TODO 算子 - filter
val rdd = sc.makeRDD(List(1,2,3,4,1,2,3,4))
// map(x => (x, null)).reduceByKey((x, _) => x, numPartitions).map(_._1)
// (1, null),(2, null),(3, null),(4, null),(1, null),(2, null),(3, null),(4, null)
// (1, null)(1, null)(1, null)
// (null, null) => null
// (1, null) => 1
val rdd1: RDD[Int] = rdd.distinct()
rdd1.collect().foreach(println)
sc.stop()
}
}
这个讲了原理,暂时不看了。
---57---
缩减分区:
分区是并行计算的,两个分区就要有两个Exccutor取计算,浪费计算资源的。
package com.atguigu.bigdata.spark.core.rdd.operator.transform
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Spark10_RDD_Operator_Transform {
def main(args: Array[String]): Unit = {
val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Operator")
val sc = new SparkContext(sparkConf)
// TODO 算子 - filter
val rdd = sc.makeRDD(List(1,2,3,4,5,6), 3)
// coalesce方法默认情况下不会将分区的数据打乱重新组合
// 这种情况下的缩减分区可能会导致数据不均衡,出现数据倾斜
// 如果想要让数据均衡,可以进行shuffle处理
//val newRDD: RDD[Int] = rdd.coalesce(2)
val newRDD: RDD[Int] = rdd.coalesce(2,true)
newRDD.saveAsTextFile("output")
sc.stop()
}
}
默认是不会打乱重新组合的,会缩减分区,就会造成数据的倾斜。
想让数据均衡就要shuffle:
---58---
如何扩大分区呢?
package com.atguigu.bigdata.spark.core.rdd.operator.transform
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Spark11_RDD_Operator_Transform {
def main(args: Array[String]): Unit = {
val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Operator")
val sc = new SparkContext(sparkConf)
// TODO 算子 - filter
val rdd = sc.makeRDD(List(1,2,3,4,5,6), 2)
// coalesce算子可以扩大分区的,但是如果不进行shuffle操作,是没有意义,不起作用。
// 所以如果想要实现扩大分区的效果,需要使用shuffle操作
// spark提供了一个简化的操作
// 缩减分区:coalesce,如果想要数据均衡,可以采用shuffle
// 扩大分区:repartition, 底层代码调用的就是coalesce,而且肯定采用shuffle
//val newRDD: RDD[Int] = rdd.coalesce(3, true)
val newRDD: RDD[Int] = rdd.repartition(3)
newRDD.saveAsTextFile("output")
sc.stop()
}
}
也会分为shuffle和shffle。没有事没有意义的,扩大分区一定要shuffle。
---59---
package com.atguigu.bigdata.spark.core.rdd.operator.transform
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Spark12_RDD_Operator_Transform {
def main(args: Array[String]): Unit = {
val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Operator")
val sc = new SparkContext(sparkConf)
// TODO 算子 - sortBy
val rdd = sc.makeRDD(List(6,2,4,5,3,1), 2)
val newRDD: RDD[Int] = rdd.sortBy(num=>num)
newRDD.saveAsTextFile("output")
sc.stop()
}
}
按照分区排序的。
package com.atguigu.bigdata.spark.core.rdd.operator.transform
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Spark12_RDD_Operator_Transform1 {
def main(args: Array[String]): Unit = {
val sparkConf = new SparkConf().setMaster("local[*]").setAppName("Operator")
val sc = new SparkContext(sparkConf)
// TODO 算子 - sortBy
val rdd = sc.makeRDD(List(("1", 1), ("11", 2), ("2", 3)), 2)
// sortBy方法可以根据指定的规则对数据源中的数据进行排序,默认为升序,第二个参数可以改变排序的方式
// sortBy默认情况下,不会改变分区。但是中间存在shuffle操作
val newRDD = rdd.sortBy(t=>t._1.toInt, false)
newRDD.collect().foreach(println)
sc.stop()
}
}
---60---