Spark编程核心—2,RDD详解

本文详细解析Spark编程中的核心组件RDD,涵盖RDD的工作流程、Transformation和Action算子,以及缓存与数据分区策略。通过代码实战展示如何利用RDD进行数据过滤、UV统计和TopN问题解决。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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()
  }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值