解压到本地磁盘,配置环境变量 bin目录和sbin目录
spark-core
8
8
2.12.0
2.7.7
2.4.7
org.apache.spark
spark-core_2.12
2.4.7
org.scala-lang
scala-library
${scala.version}
org.apache.spark
spark-core_2.12
${spark.version}
org.apache.spark
spark-sql_2.12
${spark.version}
mysql
mysql-connector-java
5.1.47
org.apache.hadoop
hadoop-client
${hadoop.version}
org.apache.spark
spark-mllib_2.12
${spark.version}
src/main/scala
src/test/scala
net.alchim31.maven
scala-maven-plugin
3.2.2
compile
testCompile
-dependencyfile
${project.build.directory}/.scala_dependencies
org.apache.maven.plugins
maven-shade-plugin
2.4.3
package
shade
*:*
META-INF/*.SF
META-INF/*.DSA
META-INF/*.RSA
object wc {
def main(args: Array[String]): Unit = {
val sparConf = new SparkConf().setMaster("local").setAppName("wordcount")
val context = new SparkContext(sparConf)
context.stop()
}
}
object wc {
def main(args: Array[String]): Unit = {
//建立连接
val sparConf = new SparkConf().setMaster("local").setAppName("wordcount")
val context = new SparkContext(sparConf)
//读取数据
val value: RDD[String] = context.textFile("data")
//扁平映射,将每一行数据铲粪成一个一个单词
val word: RDD[String] = value.flatMap(_.split(" "))
//根据单词分组
val wordGroup: RDD[(String, Iterable[String])] = word.groupBy(word => word)
//结构转换
val wordToCount: RDD[(String, Int)] = wordGroup.map {
case (word, list) =>(word, list.size)
}
//采集转化结果
val tuples: Array[(String, Int)] = wordToCount.collect()
tuples.foreach(println)
context.stop()
}
}
第二种
object wc2 {
def main(args: Array[String]): Unit = {
//建立连接
val sparConf = new SparkConf().setMaster("local").setAppName("wordcount")
val context = new SparkContext(sparConf)
//读取数据
val value: RDD[String] = context.textFile("data")
//扁平映射,将每一行数据铲粪成一个一个单词
val word: RDD[String] = value.flatMap(_.split(" "))
//结构转化
val wordM: RDD[(String, Int)] = word.map(i => (i, 1))
val wordGroup: RDD[(String, Iterable[(String, Int)])] = wordM.groupBy(wordM => wordM._1)
val wordCount: RDD[(String, Int)] = wordGroup.map {
case (k, v) => {
(k, v.map(i => i._2).sum)
}
}
// val wordCount: RDD[(String, Int)] = wordGroup.map { //或者
// case (k, v) => {
// v.reduce(
// (k2,v2)=>(k2._1,k2._2+v2._2)
// )
// }
// }
//采集转化结果打印
wordCount.collect().foreach(println)
context.stop()
}
}
object wc4 {
def main(args: Array[String]): Unit = {
//建立连接
val sparConf = new SparkConf().setMaster("local").setAppName("wordcount")
val context = new SparkContext(sparConf)
//读取数据
val value: RDD[String] = context.textFile("data")
//扁平映射,将每一行数据铲粪成一个一个单词
val word: RDD[String] = value.flatMap(_.split(" "))
//结构转化
val wordM: RDD[(String, Int)] = word.map(i => (i, 1))
//spark reduceByKey:相同key的数据,可以对value进行reduce聚合
val wordCount: RDD[(String, Int)] = wordM.reduceByKey(_ + _)
//采集转化结果打印
wordCount.collect().foreach(println)
context.stop()
}
}
spark部署文章已发布
进入spark目录执行
bin/spark-submit --class org.apache.spark.examples.SparkPi --master local[2] ./examples/jars/spark-examples_2.11-2.4.7.jar 10
出现以上结果及成功!
进入已经配置好的spark,进入spark下的sbin目录执行start-all.sh,就会启动,我部署的是三台,一个master,三个worker,执行jps查看存在即正常启动
然后我们在spark目录下提交
bin/spark-submit --class org.apache.spark.examples.SparkPi --master spark://master:7077 ./examples/jars/spark-examples_2.11-2.4.7.jar 10
出现以上结果及成功!
- --class Spark程序中包含主函数的类,比如说自己写的wordcount类的类
- --master Spark程序运行模式:local[*], sprak://master:7077, Yarn 我们上面用到了本地local和第二个spark的standalone模式的提交
- --executor-memory 1G 指定每个executor可用内存为1G
- --total-executor-cores 2 指定所有executor使用的cpu核数为2个
- --executor-cores 指定每个executor使用的cpu核数
- application-jar 也就是打包好的jar包
- application-arguments 传给main方法的参数,我们上面传的是10,表示执行10个任务
配置高可用spark在我其他文章有
配置高可用spark使用Standalone模式的情况下独立性极强,但是他不是专门资源调度框架,而是一个计算框架,所以用其他的专业资源调度框架更好,国内Yarn使用的情况非常多
首先我们得先配置hadoop的yarn配置文件配置关闭内存检测,其他文章hadoop集群yarn配置里有
然后配置spark的spark-env.sh
export JAVA_HOME=/bigdata/jdk
YARN_CONF_DIR=/bigdata/hadoop/etc/hadoop
SPARK_MASTER_WEBUI_PORT=8989 #指定spark master的端口号
然后启动hdfs和yarn
进入spark目录执行
# --deploy-mode 指定的是运行模式 cluster 集群模式
bin/spark-submit --class org.apache.spark.examples.SparkPi --master yarn --deploy-mode cluster ./examples/jars/spark-examples_2.11-2.4.7.jar 10
然后我们进入yarn的webui界面查看
http://192.168.9.140:8088/cluster
这里的端口是hadoop的yarn配置下的ui端口地址
配置spark历史服务器
进入到conf然后复制spark-defaults.conf.template去掉template
然后输入
spark.eventLog.enabled true
spark.eventLog.dir hdfs://master:8020/directory
# 注意端口不要冲突,自己设置一个历史服务器端口,下面是用来和yarn绑定的,和env.sh配置的端口需要一致
spark.yarn.historyServer.address=master:18088
spark.history.ui.port=18088
hdfs上的directory需要存在
然后在spark-env.sh追加
# ui.port自己指定一个历史服务器的访问
export SPARK_HISTORY_OPTS="
-Dspark.history.ui.port=18088
-Dspark.history.fs.logDirectory=hdfs://master:8020/directory
-Dspark.history.retainedApplications=30
"
然后启动历史服务
sbin/start-history-server.sh
然后再重新提交任务
# 集群模式换成客户端模式client
bin/spark-submit --class org.apache.spark.examples.SparkPi --master yarn --deploy-mode client ./examples/jars/spark-examples_2.11-2.4.7.jar 10
注意端口
hdfs getconf -confKey fs.default.name获取hdfs正确端口
然后注意要启动spark sbin目录下的历史服务器
再集群上执行程序太麻烦了,在本地window下就可以使用spark来进行直接使用
首先我们先解压压缩包,然后进入bin目录下
双击运行spark-shell.cmd
进入这样界面就ok!
然后我们测试一下是否能正常使用交互式界面
在bin目录下新建一个word.txt 来测试wordcount
spark scala
spark yarn
#控制台执行
sc.textFile("./word.txt").flatMap(_.split(" ")).map(i=>(i,1)).reduceByKey(_ + _).collect
如上图则成功
也可以执行jar包,进入bin目录
在当前目录打开cmd
spark-submit --class org.apache.spark.examples.SparkPi --master local[*] --deploy-mode client ../examples/jars/spark-examples_2.11-2.4.7.jar 10
成功,并且运行速度也快
local模式用来测试
standalone模式单独部署
yarn模式混合部署,需要借助hadoop集群的yarn和hdfs
通常spark我们是结合yarn来一起使用的
spark应用程序提交到yarn环境执行的时候会有两种部署执行的方式: client和cluster,一个客户端模式一个集群模式,两种区别在于:Dirver程序运行节点位置.
spark会把处理数据拆分成最小单元,单元就叫rdd,也叫做弹性分布式数据集,进行的操作都会被包装成一个个rdd不断叠加扩展,rdd的数据只有早调用collect方法的时候才会真正执行业务逻辑操作,之前的包装都是功能上的扩展,并且在rdd中是有分区的概念的,分成多个执行并行操作
具有:
弹性
- 存储弹性:内存磁盘自动切换
- 容错弹性:数据丢失可以自动恢复
- 计算弹性:计算出错重试
- 分片弹性:分区可以根据需要重新分片
分布式:
数据存储在大数据集群不同节点上
数据集:
封装了计算逻辑,不保存数据
数据抽象:
RDD是一个抽象类,基本上是靠子类实现,子类功能更加丰富
不可变:
RDD封装了计算逻辑,是不可以改变的,想要改变只能重新产生新的rdd
可分区,并行计算
分区列表:
rdd数据结构存在分区列表,用于执行任务时并行计算,实现分布式计算的重要属性
分区计算函数:
在计算的时候,使用的是分区函数对每一个分区进行计算
rdd之间依赖:
rdd是计算模型的封装,一层套一层叠加,进行组合的时候就需要进行多个rdd建立依赖
分区器:
如何分区靠分区器来分区,可能有可能没有
首选位置:
判断计算发送到哪个节点,效率最优
rdd在整个流程中主要用于逻辑封装,生成task发送给executor节点执行计算
def main(args: Array[String]): Unit = {
// 准备环境
val rdd: SparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
val sc = new SparkContext(rdd)
//创建rdd,从内存中创建rdd,将内存中集合的数据作为处理源
val ints = Seq(1, 2, 3, 4)
//parallelize: 并行 makeRDD:和parallelize一样
// val value: RDD[Int] = sc.parallelize(ints)
val value: RDD[Int] = sc.makeRDD(ints)
value.collect().foreach(println)
sc.stop()
}
def main(args: Array[String]): Unit = {
// 准备环境
val rdd: SparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
val sc = new SparkContext(rdd)
//将文件中的数据作为处理的数据源
//默认路径是根路径,当前项目的根路径,可以指定目录或者具体文件,也可以hdfs路径
val value: RDD[String] = sc.textFile("data/1.txt")
value.collect().foreach(println)
sc.stop()
}
def main(args: Array[String]): Unit = {
// 准备环境
val rdd: SparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
val sc = new SparkContext(rdd)
//将文件中的数据作为处理的数据源
//textfile:是以行为单位来读取,wholeTextFiles:是以文件为单位来读取
val value: RDD[(String, String)] = sc.wholeTextFiles("data")
value.collect().foreach(println)
sc.stop()
}
def main(args: Array[String]): Unit = {
// 准备环境
val rdd: SparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
rdd.set("spark.defalult.parallelism",6) //设置分区数
val sc = new SparkContext(rdd)
// makeRDD可以指定分区数,默认是当前环境最大可用核数
val value: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4), 2)
//saveAsTextFile:可以吧数据保存成分区文件
value.saveAsTextFile("output")
sc.stop()
}
def main(args: Array[String]): Unit = {
// 准备环境
val rdd: SparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
val sc = new SparkContext(rdd)
// textFile默认最小分区数是2,有可能会比这个高
val value: RDD[String] = sc.textFile("data", 2)
sc.stop()
}
spark读取文件,采用的是hadoop的方式读取,所以一行一行读取,和字节数没有关系
读取数据的时候以偏移量为单位
rdd方法:
转换:旧的rdd包装成新的rdd就是转换,不进行操作只是包装,进行flatMap,map等只是封装不触发
行动:触发任务的调度和作业的执行 collect
rdd方法也叫rdd算子算子:
rdd的方法可以将计算逻辑发送到Executor端(分布式节点)执行
方法的外部操作都是在Driver端执行,方法内部逻辑实在Executor端执行
rdd根据数据的处理方式不同在于分为value类型,双value类型和key-value类型
逐条映射到每一个元素上执行操作
def main(args: Array[String]): Unit = {
val rdd: SparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
val sc = new SparkContext(rdd)
val value: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4))
value.map(_*2).collect().foreach(println)
sc.stop()
}
一个分区内是,前一个执行完再执行下一个,执行的顺序是有序的
多个分区,是无序的,也可能同时并行
获取一整个分区再执行里面的转换,数据较大不适合用
返回的结果是需要一个迭代器,比如执行任意的处理哪怕是过滤数据filter
List(x.max).iterator返回这样的迭代器也可以
def main(args: Array[String]): Unit = {
val rdd: SparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
val sc = new SparkContext(rdd)
//分区两个
val value: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4),2)
value.mapPartitions(//获取一整个分区的数据才进行执行里面的迭代操作
_.map(_*2)
)
.collect().foreach(println)
sc.stop()
}
默认传入迭代器,但是就多了一个index
def main(args: Array[String]): Unit = {
val rdd: SparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
val sc = new SparkContext(rdd)
//分区两个
val value: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4),2)
value.mapPartitionsWithIndex(//获取一整个分区的数据才进行执行里面的迭代操作
(index,item)=>{//一个是当前分区index一个是迭代器
if(index == 1){
item //返回当前迭代器
}else{
Nil.iterator//返回空迭代器
}
}
)
.collect().foreach(println)
sc.stop()
}
再map对每个元素的基础上把多维度的打散为一个一个的,也叫扁平映射
def main(args: Array[String]): Unit = {
val rdd: SparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
val sc = new SparkContext(rdd)
//分区两个
val value: RDD[List[Int]] = sc.makeRDD(List(List(1,2),List(3,4)),2)
val value1: RDD[String] = sc.makeRDD(List("hello world", "hello spark"))
// 有不相同的时候我们用模式匹配
val value2: RDD[Any] = sc.makeRDD(List(List(1, 2), 3, List(4, 5)), 2)
value.flatMap(item=>item)
.collect().foreach(println)
// 切割字符串打散
value1.flatMap(_.split(" ")).collect().foreach(println)
// 模式匹配
value2.flatMap(
data=>{//这里可以直接用case匹配
data match { //到最后需要返回的是可打散的相同的
case x:List[_]=>x //匹配List类型的任意数组
case dat=>List(dat)//匹配单个数组包装成相同类型
}
}
)
sc.stop()
}
将一个分区的数据变成相同类型的数组,转换之后分区数量不变,数据也不变化位置
def main(args: Array[String]): Unit = {
val rdd: SparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
val sc = new SparkContext(rdd)
//分区两个
val value: RDD[Int] = sc.makeRDD(List(1,2,3,4),2)
//使用glom求出每个分区的最大值求和
val value2: RDD[Array[Int]] = value.glom()
println(value2.map(
i => i.max
)
.collect().sum)
sc.stop()
}
默认情况下分区并行的时候执行算子之后原本的数据是什么分区就是什么分区,并且数据对应的位置也不会改变
groupby会将数据源中的每一个数据进行分组判断,根据返回的分组key进行分组,相同的key就会放置在一个组里
分组和分区没有必然的关系
分组之后数据就会被打乱(也就是打散),重新组合,这叫shuffle
分组之后相同组的会被分配到同一个分区中,但是并不是说一个分区峙中只能有一个组
def main(args: Array[String]): Unit = {
val rdd: SparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
val sc = new SparkContext(rdd)
//分区两个
val value: RDD[Int] = sc.makeRDD(List(1,2,3,4),2)
//根据余数进行分组
value.groupBy(
item=>item%2
)
.collect().foreach(println)
sc.stop()
}
def main(args: Array[String]): Unit = {
val rdd: SparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
val sc = new SparkContext(rdd)
//分区两个
val value: RDD[String] = sc.makeRDD(List("hello","spark","scala","hadoop"),2)
//根据首字母进行分组
value.groupBy(
item=>item.charAt(0)
)
.collect().foreach(println)
sc.stop()
}
def main(args: Array[String]): Unit = {
val rdd: SparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
val sc = new SparkContext(rdd)
val value: RDD[String] = sc.textFile("data/apache.log")
val value1 = value.map(
line => {
val datas: String = line.split(" ")(3)
val format = new SimpleDateFormat("dd/MM/yyyy:HH:mm:ss")
val date: Date = format.parse(datas)//转换成时间
// date.getHours()
val format1 = new SimpleDateFormat("HH")
val str: String = format1.format(date)//获取小时字符串
(str, 1)
}
).groupBy(_._1)
.map{
case (hour,iter)=>{
(hour,iter.size)//返回小时出现了多少次
}
}
sc.stop()
}
根据条件筛选过滤数据,符合要求的保留,不符合的丢弃,过滤之后分区不变,分区的数据可能不均衡因为数据有些可能被不符合丢弃了,导致出现数据倾斜
def main(args: Array[String]): Unit = {
val rdd: SparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
val sc = new SparkContext(rdd)
val value: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4))
value.filter(item=>item%2==0).collect().foreach(println)
sc.stop()
}
随机抽取数据
需要传递三个参数:
第一个参数需要传递一个布尔值,抽取数据是否放回
第二个参数指定的是被抽取的概率,double类型
第三个参数表示,抽取数据时随机算法的种子,如果不穿第三个参数,那么使用的当前系统的时间,那么就是随机的,如果指定了随机算法的种子就说明每个值出现的概率已经确定
def main(args: Array[String]): Unit = {
val rdd: SparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
val sc = new SparkContext(rdd)
val value: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4,5,6,7,8,9))
println(value.sample(false, 0.4).collect().mkString(","))//随机抽取,每次抽取数据不放回
sc.stop()
}
数据去重
def main(args: Array[String]): Unit = {
val rdd: SparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
val sc = new SparkContext(rdd)
val value: RDD[Int] = sc.makeRDD(List(1, 2, 3, 3,2,1,4,4,5))
value.distinct().collect().foreach(print)
sc.stop()
}
根据数据量缩减分区,用于大数据集过滤后提高小数据集的执行效率,减小调度成本
分区数量是可以比原来的分区数量大的,但是默认不打乱重组是没有作用的,所以我们要加上true进行shuffle打乱
def main(args: Array[String]): Unit = {
val rdd: SparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
val sc = new SparkContext(rdd)
val value: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4),4)//四个分区
//缩减成两个分区,默认情况下不打乱重组只缩减,第二参数是是否进行shuffle打乱重组处理boolean无规律
value.coalesce(2,true)
sc.stop()
}
其实就是包装了使用coalesce,加上true使用shuffle,使用这个默认就是进行shuffle,其实用coalesce就可以了
传入一个函数根据什么进行排序,第二个参数boolean默认是升序true,默认情况下不会改变分区,但是会有shuffle打乱操作
def main(args: Array[String]): Unit = {
val rdd: SparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
val sc = new SparkContext(rdd)
val value = sc.makeRDD(List(("1",1),("11",2),("2",3)),2)
//降序
value.sortBy(_._1.toInt,false).collect().foreach(println)
sc.stop()
}
def main(args: Array[String]): Unit = {
val rdd: SparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
val sc = new SparkContext(rdd)
val value: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4))
val value1: RDD[Int] = sc.makeRDD(List(3,4,5,6))
//交集
value.intersection(value1)
//并集
value.union(value1)
//差集
value.subtract(value1)
//拉链
value.zip(value1)
sc.stop()
}
对键值对类型数据根据指定分区规则对数据进行重分区
def main(args: Array[String]): Unit = {
val rdd: SparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
val sc = new SparkContext(rdd)
val value: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4),2)
value.map((_,1)).partitionBy(new HashPartitioner(2))
sc.stop()
}
按照数据相同的key进行value聚合,和scala一样两两聚合,先前面一个和后面一个然后操作的结果接着跟后面一个,如果key的数据只有一个,就不会参加运算直接返回
同分区内计算相同分区间计算也相同
def main(args: Array[String]): Unit = {
val rdd: SparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
val sc = new SparkContext(rdd)
val value = sc.makeRDD(List(("a",2),("a",4),("b",3),("b",1),("c",3)),2)
value.reduceByKey((item,next)=>{item+next}).collect().foreach(println)
sc.stop()
}
根据key进行分组形成一个对偶元组,元组中的抵押给元素就是key,元素中的第二个元素就是相同key的value集合
和groupby的区别:ByKey的value集合不会有key,groupby分组之后value会保留key
groupbykey和reduceByKey会导致数据打乱重组,存在shuffle操作,shuffle操作必须存到磁盘处理,不能再内存中数据等待,但是reduceByKey能再打乱前对key进行预聚合功能,性能更好一点
def main(args: Array[String]): Unit = {
val rdd: SparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
val sc = new SparkContext(rdd)
val value = sc.makeRDD(List(("a",2),("a",4),("b",3),("b",1),("c",3)),2)
value.groupByKey().map((k)=>{
(k._1,k._2.sum)
}).collect().foreach(println)
sc.stop()
}
reduceByKey只能够区间内区间外做相同运算,aggregateByKey就可以进行多重不同运算
该算子存在函数柯里化有两个参数列表()()
第一个参数列表,需要传递一个参数,表示初始值,用于蓬蒂安第一个key的时候和value进行分区内计算
第二个参数列表需要传递两个参数
第一个参数表示分区内计算规则,也就是相同区间计算规则
第二个参数表示分区间计算规则,也就是不同分区的计算规则
出现无法序列化报错更换scala版本,xml也要换
def main(args: Array[String]): Unit = {
val rdd: SparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
val sc = new SparkContext(rdd)
val value = sc.makeRDD(List(("a",2),("a",4),("b",3),("b",1),("c",3)),2)
value.aggregateByKey(0)(
(x,y)=>math.max(x,y),//第一个分区内计算函数
(x,y)=>x+y //分区间计算函数
).collect().foreach(println)
sc.stop()
}
小练习求平均值
def main(args: Array[String]): Unit = {
val rdd: SparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
val sc = new SparkContext(rdd)
val value = sc.makeRDD(List(("a",2),("a",4),("b",3),("b",1),("c",3)),2)
// 初始值第一次会是一个区间参数,我们传入一个元组初始值,用来存储总和和个数
value.aggregateByKey((0,0))(
(k,v)=>{//k第一次是初始值,v是下一个值
(k._1+v,k._2+1)
},
(k,v)=>{//区间之间经过上面区间内求完最后就是元组
(k._1+v._1,k._2+v._2)
}
).mapValues(//可以用模式匹配(k,v)类型
(num)=>{
num._1/num._2
}
).collect().foreach(println)
sc.stop()
}
分区内和分区间做相同的计算,和reduceByKey差不多,但是有初始值
def main(args: Array[String]): Unit = {
val rdd: SparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
val sc = new SparkContext(rdd)
val value = sc.makeRDD(List(("a",2),("a",4),("b",3),("b",1),("c",3)),2)
value.foldByKey(0)(_+_).collect().foreach(println)
sc.stop()
}
第一个值不进行运算而是转化结构
方法需要三个参数
第一个参数表示:将相同key的第一个数据进行结构的转换,实现操作
第二个参数表示:分区内的计算规则,需要指定类型
第三个参数表示:分区间的计算规则,需要指定类型
def main(args: Array[String]): Unit = {
val rdd: SparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
val sc = new SparkContext(rdd)
val value = sc.makeRDD(List(("a",2),("a",4),("b",3),("b",1),("c",3)),2)
value.combineByKey(
k=>(k,1),
(k:(Int,Int),v)=>{
(k._1+v,k._2+1)
},
(k:(Int,Int),v:(Int,Int))=>{
(k._1+v._1,k._2+v._2)
}
).map(
i=>(i._1,(i._2._1 / i._2._2))
).collect().foreach(println)
sc.stop()
}
两个不同数据源的数据相同的key的value会连接在一起,形成元组
如果两个相同数据源中key没有匹配上,就不会出现在结果当中
如果相同key有多个匹配的,会以此匹配,数据量会变大
不推荐使用
def main(args: Array[String]): Unit = {
val rdd: SparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
val sc = new SparkContext(rdd)
val value = sc.makeRDD(List(("a",2),("a",4),("b",3),("b",1),("c",3)))
val value1 = sc.makeRDD(List(("a",7),("a",4),("b",3),("b",1),("c",3)))
value.join(value1).collect().foreach(println)
sc.stop()
}
左连接右连接,一个左边有的就保留,右边没有的None
def main(args: Array[String]): Unit = {
val rdd: SparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
val sc = new SparkContext(rdd)
val value = sc.makeRDD(List(("a",2),("a",4),("c",3)))
val value1 = sc.makeRDD(List(("a",7),("a",4),("b",3),("b",1),("e",3)))
value.leftOuterJoin(value1).collect().foreach(println)
value.rightOuterJoin(value1).collect().foreach(println)
sc.stop()
}
cogroup = connect + group(分组连接)
根据相同的key分组,然后有就展示没有就是空的CompactBuffer存储,有的就是相同rdd存放在同一个CompactBuffer
(a,(CompactBuffer(2, 4),CompactBuffer(7, 4)))
def main(args: Array[String]): Unit = {
val rdd: SparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
val sc = new SparkContext(rdd)
val value = sc.makeRDD(List(("a",2),("a",4),("c",3)))
val value1 = sc.makeRDD(List(("a",7),("a",4),("b",3),("b",1),("e",3)))
value.cogroup(value1).collect().foreach(println)
sc.stop()
}
def main(args: Array[String]): Unit = {
val rdd: SparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
val sc = new SparkContext(rdd)
val value = sc.makeRDD(List( //模拟数据 时间戳 省份 城市 用户 广告
"12355314 2 56 88 113",
"12355314 2 56 88 32",
"12355314 5 56 88 3",
"12355314 7 56 88 23",
"12355314 3 56 88 32",
"12355314 7 56 88 13",
"12355314 5 56 88 3",
"12355314 2 56 88 12",
"12355314 2 56 88 113"
))
// 统计出每个省份每个广告被点击数量的排行top3
value.map {
case i=>{
((i.split(" ")(1),i.split(" ")(4)),1)
}
}.reduceByKey(_+_)
.map{case i=>{(i._1._1,(i._1._2,i._2))}}
.groupByKey()
.mapValues(_.toList.sortBy(_._2)(Ordering.Int.reverse).take(3))
.collect()
.foreach(println)
sc.stop()
}
行动算子就是触发作业job执行的方法,底层代码调用的是环境对象的runJob方法,比如collect()
这个和转换算子不一样,这属于行动算子,对数据两两计算直接就返回结果,转换算子返回的是新的rdd
将不同分区的数据按照分区顺序采集到Driver端内存中,形成数组
获取数据源中数据的第一个
获取数据源n个数据
数据排序后,取n个数据,默认升序,降序需要加上如下
def main(args: Array[String]): Unit = {
val rdd: SparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
val sc = new SparkContext(rdd)
val value = sc.makeRDD(List(23,4,5,6,23))
// 降序
value.takeOrdered(3)(Ordering.Int.reverse).foreach(println)
sc.stop()
}
和转换算子bykey不一样,直接返回结果,bykey的初始值智慧参与分区内计算,而这里的初始值会参与分区间计算和分区内计算
def main(args: Array[String]): Unit = {
val rdd: SparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
val sc = new SparkContext(rdd)
val value = sc.makeRDD(List(23,4,5,6,23),2)
println(value.aggregate(0)(_ + _, _ + _))
sc.stop()
}
和bykey一样,用来简化aggregate行动算子,同时也是初始值参与分区间计算和分区内计算
返回得是一个Map,统计的是里面的值出现的次数
返回一个Map,统计key出现的次数
def main(args: Array[String]): Unit = {
val rdd: SparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
val sc = new SparkContext(rdd)
val value = sc.makeRDD(List(1,2,3,4))
//两种foreach方式不同collect是从分区按顺序采集回来,直接输出是从分区中直接输出
value.foreach(println)
value.collect().foreach(println)
sc.stop()
}
当前一个rdd需要使用上一个rdd就是依赖关系,而当前rdd间接需要其他rdd就是称之为血缘关系,在rdd中rdd不会保存数据,但是会保存他们的关系,一旦出现错误,他就可以根据血缘关系重新读取进行计算
rdd中不存储数据,如果一个rdd需要重复使用,那么就会从头执行来获取数据,数据无法重用
如果执行较长比较重要的时候,我们也可以使用持久化操作,更加节省错误带来的执行时间
//前提是要已经执行了才能进行持久化
//cache持久化的操作,只能存储到内存中,会在血缘关系中添加新的依赖,出现问题可以冲头读取
rdd.cache()
//persist持久化的操作,存储到文件,临时文件,执行完作业就会删除
//,会在血缘关系中添加新的依赖,出现问题可以冲头读取
rdd.persist(StorageLevel.DISK_ONLY)//有多个级别选择
//checkpoint需要落盘,要制定检查点保存路径,当作业执行完毕之后
//不会被删除,一般保存路径都是在分布式存储系统HDFS
//一般两个一起用效率更高
//执行过程中,会切断血缘关系,重新建立新的血缘关系,等同于改变了数据源
sc.setCheckpointDir("cp")//检查点保存路径
rdd.cache()
rdd.checkpoint()
object spark_rdd_new35_value_Partitioner extends Serializable {
def main(args: Array[String]): Unit = {
val rdd1: SparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
val sc = new SparkContext(rdd1)
val rdd = sc.makeRDD(List(("kn",1),("b",3)))
val partRDD: RDD[(String, Int)] = rdd.partitionBy(new MyPartitioner)
partRDD.saveAsTextFile("output")
sc.stop()
}
class MyPartitioner extends Partitioner{
//分区数量可以指定固定
override def numPartitions: Int = 2
//根据数据的key值返回数据的分区索引(从0开始)
override def getPartition(key: Any): Int = {
key match {
case "kn"=>0
case _=>1
}
}
}
}
输出rdd分区文件,一个输出的文本,其他的输出的都以特殊方式存储,
前两个对数据类型没有什么要求,第三个必须要是键值对类型数据
输入对应路径即可读取
spark默认提供了简单数据聚合的累加器
val sumAcc = sc.longAccumulator("sum")//创建累加器,还有double等
rdd.foreach(
num=>{
sumAcc+=num //使用累加器
}
)
mapRDD.collect()
mapRDD.collect()
println(sumAcc.value)
//每一次调用行动算子都会执行一遍累加器,会造成多加,如果没有i行动算子的话那么不会执行
//一般我们会放到行动算子中进行累加器的操作
每个分区都会执行计算任务,那么就会有多个task,都用到同一个数据,会造成太多没有意义的数据,闭包程序都是以tesk为单位发送的,数据量大了之后会占用大量的内存
我们可以定义广播变量,其不可以修改也叫:分布式共享只读变量
定义了之后就可以使用了
//定义广播变量
val bc = sc.broadcast(传入封装的变量)