目录
Spark编程核心—2,RDD详解
写一个spark应用程序的流程:
1、 加载数据集(获得RDD)
2、 使用transformation算子对RDD进行操作
Transformation算子是一系列懒执行的函数
3、 使用actions算子触发执行
Transformation算子对RDD的操作会先被记录,当actions算子触发后才会真正执行
伪代码:
子
lines = sc.textFile(“hdfs://master:9000/aaa.txt”)//加载数据集
rdd1 = lines.map(x=>(x,1)). //transformation算子
rdd1.reduceByKey(_+_).collect().foreach(println) //最后的foreach是action算子
常见的RDD算子
Transformation算子
map(f : T => U)
将函数应用于RDD中的每一个元素,将返回值构成新的RDD
//求每个元素平方
nums = sc.parallelize(List(1,2,3,4))
squared = nums.amp(x=>x*x)
println(result.collect().mkString(,))
fliter(f : T => Bool)
返回一个由通过传给filter()的函数的元素组成的RDD
flatMap(f : T => Seq[U])
将函数应用于RDD中的每个元素,将返回的迭代器中的所有内容组成一个新RDD
val lines = sc.parallelize(List("Hello World","hi"))
val words = lines.flatMap(line=> line.split(" "))
words.first() //返回hello
sample(fraction : Float)
对RDD中的元素随机采样,且可以进行替换
groupByKey()
对RDD进行按key分组
reduceByKey()
按照指定规则合并Value
val data = seq(("a",3),("b",4),("c",5))
sc.parallelize(data).reduceByKey((x,y) => x + y)
sc.parallelize(data).reduceByKey((x,y) => x + y , 10) //自定义并行度
union()
对两个RDD取并集
rdd1.union(rdd2)
join()
对两个RDD进行内连接,输出两个RDD中都存在的键
sort(c : Comparator[K])
排序,默认升序
sortByKey()
按 key 进行排序
sortBy()
自定义排序规则
partitionBy(p : Partition[K])
对RDD进行分区,返回新RDD
distinct()
去掉重复数据
Action算子
count()
统计 RDD 中元素的个数
foreach()
遍历 RDD 中的元素
foreachPartition()
以分区为单位遍历 RDD, map 和 mapPartition 可以与它们做类比,但 map 和 mapPartitions 是 transformations 算子
collect()
把运行结果拉回到 Driver 端
take(n)
取 RDD 中的前 n 个元素
reduce()
按照指定规则聚合 RDD 中的元素
val sum = rdd.reduce(_+_)
countByKey()
统计出 KV 格式的 RDD 中相同的 K 的个数
countByValue()
统计出 RDD 中每个元素的个数
RDD中一些值得注意的点
1、缓存
为了避免对同一RDD多次计算,可以让Spark对数据持久化。当我们让Spark持久化存储一个RDD时,计算出RDD的节点会分别保存他们所求出的分区数据。
出于不同的环境,我们可以选择不同的缓存级别。Scala和Java,默认情况下persist()会把数据以序列化的形式缓存在JVM的堆空间中。当然也可以自行更改存储位置。
通过org.apache.spark.storage.StorageLevel中设定持久化级别,共有五种,也可以在末尾加上“_2”来存两份
MEMORY_ONLY 只存储在内存
MEMORY_ONLY_SER 只存在内存且序列化
MEMORY_AND_DISK 如果内存存不下就存在硬盘上
MEMORY_AND_DISK_SER 同上,存的是序列化数据
DISK_ONLY 存硬盘上
2、数据分区
分区的主要目的是减少网络IO,其次有助于并行化处理。分区处理最大的受益者是基于键的RDD,如join()、groupWith()、combineByKey()、lookup()等等
关于分区的其他概念请看我上一篇博客(诶嘿~访问量+2)
//Scala自定义分区的方式
val sc = new SparkContext(...)
val userData = sc.sequenceFile[UserID,UserInfo]("hdfs://master:9000/aaa.txt")
.partitionBy(new HashPartitioner(100)) //自定义100个分区
.persist()
代码实战
数据过滤-Scala 版
/**
* 文件中的数据 :
* hello java
* hello java
* hello java
* hello java
* hello java
* hello hadoop
* hello hadoop
* hello hadoop
* hello hive
* hello hive
* hello world
* hello spark
* @author root
*/
object FilterMost {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setMaster("local").setAppName("FilterMost")
val sc = new SparkContext(conf)
val rdd : RDD[String] = sc.textFile("test")
val sampleRDD : RDD[String] = rdd.sample(false, 0.9)
val result = sampleRDD.map { x => (x.split(" ")(1),1) }.reduceByKey(_+_).map { x => {(x._2,x._1)}}
.sortByKey(false).first()._2
rdd.filter {(!_.contains(result))}.foreach(println)
sc.stop();
}
}
统计每个页面的 UV
部分数据如下:
日期 时间戳 用户 ID pageID 模块 用户事件
2017-05-13 1494643577030 null 54 Kafka View
2017-05-13 1494643577031 8 70 Kafka Register
2017-05-13 1494643577031 9 12 Storm View
2017-05-13 1494643577031 9 1 Scala View
2017-05-13 1494643577032 7 73 Scala Register
2017-05-13 1494643577032 16 23 Storm Register
object CountUV {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
conf.setMaster("local")
conf.setAppName("CountUV")
val sc = new SparkContext(conf)
val rdd = sc.textFile("userLog")
val result = rdd.filter(!_.split("\t")(2).contains("null")).map(x => {(x.split("\t")(3), x.split("\t")(2))})
.distinct().countByKey()
result.foreach(x => {println("PageId: " + x._1 + "\tUV: " + x._2)})
sc.stop();
}
}
public class CountUV {
public static void main(String[] args) {
SparkConf sparkConf = new SparkConf()
.setMaster("local")
.setAppName("CountUV");
final JavaSparkContext jsc = new JavaSparkContext(sparkConf);
JavaRDD<String> rdd = jsc.textFile("userLog");
JavaRDD<String> filteredRDD =
rdd.filter(new Function<String, Boolean>() {
/**
*
*/
private static final long serialVersionUID = 1L;
@Override
public Boolean call(String v1) throws Exception {
return !"null".equals(v1.split("\t")[2]);
}
});
JavaPairRDD<String, String> pairRDD =
filteredRDD.mapToPair(new PairFunction<String, String, String>() {
/**
*
*/
private static final long serialVersionUID = 1L;
@Override
public Tuple2<String, String> call(String t)
throws Exception {
String[] splits = t.split("\t");
return new Tuple2<String, String>(splits[3], splits[2]);
}
});
JavaPairRDD<String, String> distinctRDD = pairRDD.distinct();
Map<String, Object> resultMap = distinctRDD.countByKey();
for(Entry<String, Object> entry : resultMap.entrySet()) {
System.out.println("pageId:"+ entry.getKey() +
" UV:" + entry.getValue());
}
jsc.stop();
}
}
二次排序-scala 版
object SecondSort {
def main (args: Array[ String ] ): Unit = {
val sparkConf = new
SparkConf ().setMaster ("local").setAppName ("SecondSort")
val sc = new SparkContext (sparkConf)
val rdd = sc.textFile ("secondSort.txt")
val mapRDD = rdd.map (x => {(new SecondSortKey (x.split (" ") (0).toInt, x.split (" ") (1).toInt), null)})
val sortedRDD = mapRDD.sortByKey (false)
//val sortedRDD = mapRDD.sortBy(_._1, false)
sortedRDD.map (_._1).foreach (println)
sc.stop ()
}
}
class SecondSortKey ( val first: Int, val second: Int ) extends
Ordered[ SecondSortKey ] with Serializable {
def compare ( ssk: SecondSortKey ): Int = {
if ( this.first - ssk.first == 0 ) {
this.second - ssk.second
} else {
this.first - ssk.first
}
}
override
def toString ( ): String = {
this.first + " " + this.second
}
}
TopN 问题:找出每个班级中排名前三的分数-Java 版
部分数据:
class1 100
class2 85
class3 70
class1 102
class2 65
class1 45
object GroupTopN {
private val N = 3
def main ( args: Array[ String ] ): Unit = {
val sparkConf = new SparkConf().setMaster("local").setAppName("GroupTopN")
val jsc = new JavaSparkContext(sparkConf)
val rdd = jsc.textFile("scores.txt")
val pairRDD = rdd.mapToPair(new PairFunction[ String, String, Integer ]() {
@throws[ Exception ]
override def call ( t: String ): Tuple2[ String, Integer ] = {
val className = t.split("\t")(0)
val score = Integer.valueOf(t.split("\t")(1))
new Tuple2[ String, Integer ](className, score)
}
})
pairRDD.groupByKey.foreach(new VoidFunction[ Tuple2[ String, Iterable[ Integer ] ] ]() {
@throws[ Exception ]
override def call ( t: Tuple2[ String, Iterable[ Integer ] ] ): Unit = {
val className = t._1
val iter = t._2.iterator
val nums = new Array[ Integer ](N)
while ( {
iter.hasNext
}) {
val score = iter.next
var i = 0
while ( {
i < nums.length
}) {
if ( nums(i) == null ) {
nums(i) = score //给数组的前三个元素赋值
break //todo: break is not supported
}
else if ( score > nums(i) ) {
var j = 2
while ( {
j > i
}) {
nums(j) = nums(j - 1)
{
j -= 1; j + 1
}
}
nums(i) = score
break //todo: break is not supported
}
{
i += 1; i - 1
}
}
}
System.out.println(className)
for (i <- nums) {
System.out.println(i)
}
}
})
jsc.stop()
}
}