一、准备工作
1.1 准备文件
准备本地系统文件
在\home目录里创建words.txt
把文件上传到
将words.txt上传到HDFS系统的/park目录里
查看文件内容
1.2 启动Spark Shell
启动HDFS服务
执行命令:start-dfs.sh
启动Spark服务
执行命令:start-all.sh
启动Spark Shell
执行名命令: spark-shell --master spark://master:7077
二、掌握转换算子
2.1 映射算子 - map()
任务1、将rdd1每个元素翻倍得到rdd2
对rdd1应用map()算子,将rdd1中的每个元素平方并返回一个名为rdd2的新RDD
查看结果
任务2、将rdd1每个元素平方得到rdd2
方法一、采用普通函数作为参数传给map()算子
方法二、采用下划线表达式作为参数传给map()算子
任务3、利用映射算子打印菱形
(1)Spark Shell里实现
右半菱形
加上前导空格,左半菱形
前导空格折半,显示菱形
(2)在IDEA里创建项目实现
创建Maven项目
将java目录改成scala目录
在pom.xml文件里添加相关依赖和设置源程序目录
4.0.0
cn.kox.rdd
SparkRDDDemo
1.0-SNAPSHOT
org.scala-lang
scala-library
2.12.15
org.apache.spark
spark-core_2.12
3.1.3
src/main/scala
8
8
UTF-8
添加日志属性文件
log4j.rootLogger=ERROR, stdout, logfile
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile.File=target/rdd.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n
创建hdfs-site.xml文件,允许客户端访问集群数据节点
only config in clients dfs.client.use.datanode.hostname true创建cn.kox.rdd.day01包
在cn.kox.rdd.day01包里创建Example01单例对象
package cn.kox.rdd.day01
import org.apache.spark.{SparkConf, SparkContext}
import scala.collection.mutable.ListBuffer
import scala.io.StdIn
/**
运行程序,查看结果
2.2 过滤算子 - filter()
方法二、用神奇占位符改写传入过滤算子的匿名函数
任务2、过滤出文件中包含spark的行
查看源文件/park/words.txt内容
执行命令: val lines= sc.textFile(“/park/words.txt”),读取文件 /park/words.txt生成RDD - lines
执行命令:val sparkLines = lines.filter(_.contains(“spark”)),过滤包含spark的行生成RDD - sparkLines
执行命令:sparkLines.collect,查看sparkLines内容,可以采用遍历算子,分行输出内容
输出长度超过20的行
任务3、利用过滤算子输出[2000, 2500]之间的全部闰年
传统做法,利用循环结构嵌套选择结构来实现
要求每行输出10个数
采用过滤算子来实现
要求每行输出10个数
任务4、利用过滤算子输出[10, 100]之间的全部素数
过滤算子:filter(n => !(n % 2 == 0 || n % 3 == 0 || n % 5 == 0 || n % 7 == 0))
2.3 扁平映射算子 - flatMap()
对于rdd1按空格拆分,做映射,生成新RDD - rdd2
对于rdd1按空格拆分,做扁平映射,生成新RDD - rdd3,有一个降维处理的效果
统计结果:文件里有25个单词
方法一、利用Scala来实现
利用列表的flatten函数
在cn.kox.rdd.day01包里创建Example02单例对象
package cn.kox.rdd.day01
import org.apache.spark.{SparkConf, SparkContext}
/**
运行程序,查看结果
方法二、利用Spark RDD来实现
利用flatMap算子
在cn.kox.rdd.day01包里创建Example03单例对象
package cn.kox.rdd.day01
import org.apache.spark.{SparkConf, SparkContext}
/**
运行程序,查看结果
2.4 按键归约算子 - reduceByKey()
创建成绩列表scores,基于成绩列表创建rdd1,对rdd1按键归约得到rdd2,然后查看rdd2内容
agg: aggregation 聚合值
cur: current 当前值
val scores = List((“张钦林”, 78), (“张钦林”, 90), (“张钦林”, 76),
(“陈燕文”, 95), (“陈燕文”, 88), (“陈燕文”, 98),
(“卢志刚”, 78), (“卢志刚”, 80), (“卢志刚”, 60))
val rdd1 = sc.makeRDD(scores)
val rdd2 = rdd1.reduceByKey((x, y) => x + y)
rdd2.collect.foreach(println)
可以采用神奇的占位符
任务2、在IDEA里计算学生总分
第一种方式:读取二元组成绩列表
在cn.kox.rdd.day02包里创建CalculateScoreSum01单例对象
package cn.kox.rdd.day02
import org.apache.spark.{SparkConf, SparkContext}
/**
运行程序,查看结果
第二种方式:读取四元组成绩列表
在cn.kox.rdd.day02包里创建CalculateScoreSum02单例对象
package cn.kox.rdd.day02
import org.apache.spark.{SparkConf, SparkContext}
import scala.collection.mutable.ListBuffer
/**
def main(args: Array[String]): Unit = {
// 创建Spark配置对象
val conf = new SparkConf()
.setAppName(“PrintDiamond”) // 设置应用名称
.setMaster(“local[*]”) // 设置主节点位置(本地调试)
// 基于Spark配置对象创建Spark容器
val sc = new SparkContext(conf)
// 创建四元组成绩列表
val scores = List(
(“张钦林”, 78, 90, 76),
(“陈燕文”, 95, 88, 98),
(“卢志刚”, 78, 80, 60)
)
// 将四元组成绩列表转化成二元组成绩列表
val newScores = new ListBuffer(String, Int)
// 通过遍历算子遍历四元组成绩列表
scores.foreach(score => {
newScores.append(Tuple2(score._1, score._2))
newScores.append(Tuple2(score._1, score._3))
newScores.append(Tuple2(score._1, score.4))
}
)
// 基于二元组成绩列表创建RDD
val rdd1 = sc.makeRDD(newScores)
// 对成绩RDD进行按键归约处理
val rdd2 = rdd1.reduceByKey( + _)
// 输出归约处理结果
rdd2.collect.foreach(println)
}
}
运行程序,查看结果
2.5 合并算子 - union()
课堂练习:将两个二元组成绩表合并
在集合运算里,并集符号:∪ \cup∪,并集运算:A ∪ B A \cup BA∪B
在集合运算里,交集符号:∩ \cap∩,交集运算:A ∩ B A \cap BA∩B
在集合运算里,补集运算:A ˉ \bar A
2.6 排序算子 - sortBy()
sortBy(x=>x._2,false)中的x代表rdd1中的每个元素。由于rdd1的每个元素是一个元组,因此使用x._2取得每个元素的第二个值。当然,sortBy(x=>x.2,false)也可以直接简化为sortBy(._2,false)。
2.7 按键排序算子 - sortByKey()
其实,用排序算子也是可以搞定的
2.8 连接算子
内连接算子 - join()
join()算子将两个(key, value)形式的RDD根据key进行连接操作,相当于数据库的内连接(Inner Join),只返回两个RDD都匹配的内容。
将rdd1与rdd2进行内连接
左外连接算子 - leftOuterJoin()
leftOuterJoin()算子与数据库的左外连接类似,以左边的RDD为基准(例如rdd1.leftOuterJoin(rdd2),以rdd1为基准),左边RDD的记录一定会存在。例如,rdd1的元素以(k,v)表示,rdd2的元素以(k, w)表示,进行左外连接时将以rdd1为基准,rdd2中的k与rdd1的k相同的元素将连接到一起,生成的结果形式为(k, (v, Some(w))。rdd1中其余的元素仍然是结果的一部分,元素形式为(k,(v, None)。Some和None都属于Option类型,Option类型用于表示一个值是可选的(有值或无值)。若确定有值,则使用Some(值)表示该值;若确定无值,则使用None表示该值。
rdd1与rdd2进行左外连接
右外连接算子 - rightOuterJoin()
rightOuterJoin()算子的使用方法与leftOuterJoin()算子相反,其与数据库的右外连接类似,以右边的RDD为基准(例如rdd1.rightOuterJoin(rdd2),以rdd2为基准),右边RDD的记录一定会存在。
rdd1与rdd2进行右外连接
全外连接算子 - fullOuterJoin()
fullOuterJoin()算子与数据库的全外连接类似,相当于对两个RDD取并集,两个RDD的记录都会存在。值不存在的取None。
rdd1与rdd2进行全外连接
2.9 交集算子 - intersection()
intersection()算子对两个RDD进行交集操作,返回一个新的RDD。要求两个算子类型要一致。
rdd1与rdd2进行交集操作,满足交换律
A ∩ B ≠ ϕ
2.10 去重算子 - distinct()
distinct()算子对RDD中的数据进行去重操作,返回一个新的RDD。有点类似与集合的不允许重复元素。
去重算子案例
去掉rdd中重复的元素
IP地址去重案例
在项目根目录创建ips.txt文件
192.168.234.21
192.168.234.22
192.168.234.21
192.168.234.21
192.168.234.23
192.168.234.21
192.168.234.21
192.168.234.21
192.168.234.25
192.168.234.21
192.168.234.21
192.168.234.26
192.168.234.21
192.168.234.27
192.168.234.21
192.168.234.27
192.168.234.21
192.168.234.29
192.168.234.21
192.168.234.26
192.168.234.21
192.168.234.25
192.168.234.25
192.168.234.21
192.168.234.22
192.168.234.21
在cn.kox.rdd.day03包里创建DistinctIPs单例对象
package cn.kox.rdd.day03
import org.apache.spark.{SparkConf, SparkContext}
/**
运行程序,查看结果
修改代码,保存去重结果到本地目录
(十一)组合分组算子 - cogroup()
cogroup()算子对两个(key, value)形式的RDD根据key进行组合,相当于根据key进行并集操作。例如,rdd1的元素以(k, v)表示,rdd2的元素以(k, w)表示,执行rdd1.cogroup(rdd2)生成的结果形式为(k, (Iterable, Iterable))。
rdd1与rdd2进行组合分组操作