1 读取Sequence File
读取文本格式我们可以使用text files,那么读取Sequence File该怎么办呢?
当然官网给我们提供了另外一种方式,sequenceFile[K, V] 方法,其中k和v是文件中的键值和值类型。他们实现了Writable接口。
- 准备数据
通过Hive创建一份Sequence File的数据
create table states_raw(code string, name string)
ROW FORMAT DELIMITED FIELDS TERMINATED BY " ";
load data local inpath "/home/hadoop/data/input.txt" overwrite into table states_raw;
create table states_seq(code string, name string)
ROW FORMAT DELIMITED FIELDS TERMINATED BY " "
STORED AS SEQUENCEFILE;
insert into table states_seq select * from states_raw;
- 代码
import org.apache.hadoop.io.BytesWritable
import org.apache.spark.{SparkConf, SparkContext}
object ReadSequenceFile {
def main(args: Array[String]): Unit = {
//创建SparkConf
val conf=new SparkConf().setAppName("ReadSequenceFile").setMaster("local[2]")
//创建SparkContext
val sparkContext=new SparkContext(conf)
//读取sequence文件
val seq_file=sparkContext.sequenceFile[BytesWritable,String]("hdfs://192.168.137.130:9000/user/hive/warehouse/states_seq")
seq_file.collect().foreach(println);
sparkContext.stop()
}
}
这时候你会发现上面的代码是报错的,错误提示为:没有进行序列化
18/05/22 22:06:13 ERROR TaskSetManager: Task 0.0 in stage 0.0 (TID 0) had a not serializable result: org.apache.hadoop.io.BytesWritable
Serialization stack:
- object not serializable (class: org.apache.hadoop.io.BytesWritable, value: )
- field (class: scala.Tuple2, name: _1, type: class java.lang.Object)
- object (class scala.Tuple2, (,hello java))
- element of array (index: 0)
- array (class [Lscala.Tuple2;, size 6); not retrying
18/05/22 22:06:13 INFO TaskSchedulerImpl: Removed TaskSet 0.0, whose tasks have all completed, from pool
18/05/22 22:06:13 INFO TaskSchedulerImpl: Cancelling stage 0
18/05/22 22:06:13 INFO DAGScheduler: ResultStage 0 (collect at ReadSequenceFile.scala:17) failed in 0.938 s due to Job aborted due to stage failure: Task 0.0 in stage 0.0 (TID 0) had a not serializable result: org.apache.hadoop.io.BytesWritable
原因:对于Sequence File的存储格式是一种KV数据格式,Key和Value都是二进制变长的数据,key为空使用value 存放实际的值, 这样是为了避免MR 在执行map 阶段的排序过程。
上面的代码中val seq_file=sparkContext.sequenceFile[BytesWritable,String]("hdfs://192.168.137.130:9000/user/hive/warehouse/states_seq");
这一句是没有问题的,问题出现在下面一句seq_file.collect().foreach(println);
- 修改
import org.apache.hadoop.io.BytesWritable
import org.apache.spark.{SparkConf, SparkContext}
object ReadSequenceFile {
def main(args: Array[String]): Unit = {
//创建SparkConf
val conf=new SparkConf().setAppName("ReadSequenceFile").setMaster("local[2]")
//创建SparkContext
val sparkContext=new SparkContext(conf)
//读取sequence文件
val seq_file=sparkContext.sequenceFile[BytesWritable,String]("hdfs://192.168.137.130:9000/user/hive/warehouse/states_seq")
//打印出key(把key转化为字节) value
//seq_file.map(x=>(x._1.copyBytes(),x._2)).collect().foreach(println);
//只取value,根据空格进行切分,读取切分后的值
seq_file.map(x=> (x._2.split(" "))).map(x=>(x(0),x(1))).collect().foreach(println)
sparkContext.stop()
- 结果
(hello,java)
(hello,hadoop)
(hello,hive)
(hello,sqoop)
(hello,hdfs)
(hello,spark)
2 使用Spark Core API 实现窗口函数:FIRST_VALUE,LAST_VALUE
对于这个窗口函数不理解的同学可以参考这篇博客
- FIRST_VALUE
import org.apache.spark.{SparkConf, SparkContext}
object FirstValue {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setAppName("FirstValueApp").setMaster("local[2]")
val sc = new SparkContext(conf)
val data = sc.parallelize(List(
("A", "A1"),
("A", "A2"),
("A", "A3"),
("B", "B1"),
("B", "B2"),
("B", "B3"),
("C", "C1")
))
//data.collect().foreach(println)
/*首先进行分组,在进行排序,结果如下:
(A,CompactBuffer(A1, A2, A3))
(B,CompactBuffer(B1, B2, B3))
(C,CompactBuffer(C1))
但是这时候core中并没有直接实现可以拿到first value 。
那我们要做的是拿到
(A,List((A1,A1), (A2,A1), (A3,A1)))
(B,List((B1,B1), (B2,B1), (B3,B1)))
(C,List((C1,C1)))
即,迭代出第一个元素即可
*/
data.groupByKey().sortByKey()
.map(x=>(x._1,firstValue(x._2)))
.collect().foreach(println)
//自定义一个方法
//进来一个迭代器,输出一个firstvalue
def firstValue(values: Iterable[String]) = {
values.head
}
sc.stop()
}
}
结果
(A,List((A1,A1), (A2,A1), (A3,A1)))
(B,List((B1,B1), (B2,B1), (B3,B1)))
(C,List((C1,C1)))
- LAST_VALUE
package cn.zhangyu
import org.apache.spark.{SparkConf, SparkContext}
/**
* Created by grace on 2018/5/23.
*/
object LastValue {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setAppName("FirstValueApp").setMaster("local[2]")
val sc = new SparkContext(conf)
val data = sc.parallelize(List(
("A", "A1"),
("A", "A2"),
("A", "A3"),
("B", "B1"),
("B", "B2"),
("B", "B3"),
("C", "C1")
))
//data.collect().foreach(println)
/*首先进行分组,在进行排序,结果如下:
(A,CompactBuffer(A1, A2, A3))
(B,CompactBuffer(B1, B2, B3))
(C,CompactBuffer(C1))
但是这时候core中并没有直接实现可以拿到lastValue 。
那我们要做的是拿到
(A,List((A1,A3), (A2,A3), (A3,A3)))
(B,List((B1,B3), (B2,B3), (B3,B3)))
(C,List((C1,C1)))
即,迭代最后一个元素即可
*/
data.groupByKey().sortByKey()
.map(x=>(x._1,lastValue(x._2)))
.collect().foreach(println)
//自定义一个方法
//进来一个迭代器,输出一个lastValue
def lastValue(values: Iterable[String]) = {
for(value <- values)
yield (value, values.last)
}
sc.stop()
}
}
结果:
(A,List((A1,A3), (A2,A3), (A3,A3)))
(B,List((B1,B3), (B2,B3), (B3,B3)))
(C,List((C1,C1)))
3 使用Spark Core API实现二次排序
package cn.zhangyu
import org.apache.spark.{SparkConf, SparkContext}
/**
* 使用Spark Core API实现二次排序
* 1) 自定义排序的key, 要实现Ordered和Serializable接口
* 2)将要排序的数据,映射成key为自定义排序的key,value就是原始的值
* 3)按照业务逻辑实现compare方法
* 4)使用sortByKey(false/true)算子按照自定义的key进行排序
* 5)丢弃key,取value
*/
object SecondSort {
def main(args: Array[String]) {
val conf = new SparkConf().setAppName("FirstValueApp").setMaster("local[2]")
val sc = new SparkContext(conf)
val data = sc.textFile("E:/IdeaProjects/SparkCore/src/resource/sort.txt")
/*
sort.txt
1,12
3,4
6,2
12,30
4,23
7,33
20,25
6,9
12,23
*/
//data.collect().foreach(println)
//切分
// data.map(x=>{
// x.split(",")
// //new SecondSortKey(splits(0).trim.toLong,splits(1).trim.toLong)
// }).collect().foreach(println)
data.map(x => {
val splits = x.split(",")
//(key,value) 这里的key将(c1,c2)作为一个整体传给自定义的SecondSortKey方法如果c1不相等就按照c1排序,如果c1相等就按照c2排序,value就是(c1,c2)
(new SecondSortKey(splits(0).trim.toLong, splits(1).trim.toLong), x)
}).sortByKey().map(x => x._2)
.collect().foreach(println)
sc.stop()
}
// 将(c1,c2)作为一个整体,当做是SecondSortKey
// 在scala中,第一个trait(接口)我们可以使用extends,如果还有其他trait,就用with
//这里自定义一个SecondSortKey方法实现了Ordered,Ordered接口的类具备了比较的特性,要实现他的compare方法
//这里的compare方法如果“this”对象比传递的对象(“that”)参数更小、相等或更大时,它返回一个负整数、0或正整数。
class SecondSortKey(val first:Long,val second:Long) extends Ordered[SecondSortKey] with Serializable{
override def compare(that: SecondSortKey): Int = {
if(this.first!=that.first){
(this.first-that.first).toInt
}else{
(this.second - that.second).toInt
}
}
}
}
结果:
1,12
3,4
4,23
6,2
6,9
7,33
12,23
12,30
20,25
注意: Scala中的比较器Ordered与Ordering个人感觉就是java中的Comparable和Comparator。
- Scala提供两个特质(trait)Ordered与Ordering用于比较。其中,Ordered混入(mix)Java的Comparable接口,而Ordering则混入Comparator接口。
- 众所周知,在Java中实现Comparable接口的类,其对象具有了可比较性;
- 实现comparator接口的类,则提供一个外部比较器,用于比较两个对象。
Ordered与Ordering的区别与之相类似:
Ordered特质定义了相同类型间的比较方式,但这种内部比较方式是单一的;
Ordered则是提供比较器模板,可以自定义多种比较方式。