• 【Spark 深入学习 07】RDD编程之旅基础篇03-键值对RDD


    ---------------------

    本节内容:

    · 键值对RDD出现背景

    · 键值对RDD转化操作实例

    · 键值对RDD行动操作实例

    · 键值对RDD数据分区

    · 参考资料

    ---------------------

       虽然大部分Spark的RDD操作都支持所有种类的对象,但是有少部分特殊的操作只能作用于键值对类型的RDD。这类操作中最常见的就是分布的shuffle操作,比如将元素通过键来分组或聚集计算.因此,键值对RDD也是很多程序不可缺失的一部分.

    一、键值对RDD出现背景

     mapreduce框架是把数据转化为Key-value,再聚合为key-values的过程,在spark里key-value rdd(pair rdd)同样是最常用的,在每个应用中基本会用到,pair rdd里面的元素是Tuple2,pair rdd的transform函数很多.pari rdd是很多程序的构成要素,因为他们提供了并行性操作各个键或跨节点重新进行分组的操作接口.pair rdd最简单的2种创建方法:

    (1)通过map创建实例所示:

    ---------------------

    val line =sc.textFile("/tmp/test/core-site.xml");

    val pairs=line.map(x=>(x.split(" ")(0),x));

    pairs.foreach(println);

    ------

    说明:

    map:读取将读取的每一行用空格的第一行为key,整行内容为value

    foreach:循环遍历打印出每个pair

    ---------------------

    (2)直接读取键值对类型的数据

    ---------------------

    val pairrdd = sc.parallelize(List((1,2),(3,4),(3,6)));

    pairrdd.foreach(println);

    ------

    说明:

    parallelize:从外部数据集读取键值对数据

    ---------------------

    二、键值对RDD转化操作实例

    1.例子:reduceByKey/groupByKey/

    ---------------------

    val pairrdd = sc.parallelize(List((1,2),(3,4),(3,6)));

    val result=pairrdd.reduceByKey((x,y)=>x+y);

    result.foreach(println);

    ------

    说明:

    reduceByKey:合并具有相同键的值

    ---------------------

    val pairrdd = sc.parallelize(List((1,2),(3,4),(3,6)));

    val result=pairrdd.groupByKey();

    result.foreach(println);

    ------

    说明:

    groupByKey:将同一个key的值都放到一个列表中,通过ShuffledRDD将每个partition中fetch过来,shuffle机制默认用的是hashShuffle,spark1.1版本引入sorted shuffle,速度更快。shuffle操作后面接着mapPartition()操作,生成MapPartitionRDD.

    2.例子:combineByKey/mapvalues

    ---------------------

    val initialScores = Array(("Fred", 88.0), ("Fred", 95.0), ("Fred", 91.0), ("Wilma", 93.0), ("Wilma", 95.0), ("Wilma", 98.0));

    val d1 = sc.parallelize(initialScores);

    type MVType = (Int, Double) //定义一个元组类型(科目计数器,分数);

    d1.combineByKey(

      score => (1, score),

      (c1: MVType, newScore) => (c1._1 + 1, c1._2 + newScore),

      (c1: MVType, c2: MVType) => (c1._1 + c2._1, c1._2 + c2._2)

    ).map { case (name, (num, socre)) => (name, socre / num) }.collect().foreach(println);

    ------

    说明:

    combineByKey: 

    a .score => (1, score),我们把分数作为参数,并返回了附加的元组类型。 以"Fred"为列,当前其分数为88.0 =>(1,88.0)  1表示当前科目的计数器,此时只有一个科目

    b.(c1: MVType, newScore) => (c1._1 + 1, c1._2 + newScore),注意这里的c1就是createCombiner初始化得到的(1,88.0)。在一个分区内,我们又碰到了"Fred"的一个新的分数91.0。当然我们要把之前的科目分数和当前的分数加起来即c1._2 + newScore,然后把科目计算器加1即c1._1 + 1

    c.(c1: MVType, c2: MVType) => (c1._1 + c2._1, c1._2 + c2._2),注意"Fred"可能是个学霸,他选修的科目可能过多而分散在不同的分区中。所有的分区都进行mergeValue后,接下来就是对分区间进行合并了,分区间科目数和科目数相加分数和分数相加就得到了总分和总科目数

    结果:

    (Wilma,95.33333333333333)

    (Fred,91.33333333333333)

    ---------------------

    val pairrdd = sc.parallelize(List((1,2),(3,4),(3,6)));

    val result=pairrdd.mapValues(x=>x+1);

    result.foreach(println);

    ------

    说明:

    mapValues:对每个键的值应用一个函数而不改变键的内容

    ---------------------

    3.例子:flatMapValues/keys/values/sotByKey

    ---------------------

    val pairrdd = sc.parallelize(List((1,2),(3,4),(3,6)));

    val result=pairrdd.flatMapValues(x=>(x to 5));

    result.foreach(println);

    ------

    说明:

    flatMapValues:对每个值应用一个返回迭代器函数,然后对返回的每个值都生成一个对应原键的键值对记录,通常用于符号化.

    结果:

    (1,2)

    (1,3)

    (1,4)

    (1,5)

    (3,4)

    (3,5)

    ---------------------

    val pairrdd = sc.parallelize(List((1,2),(3,4),(3,6)));

    val result=pairrdd.keys;//scala不要使用括号

    result.foreach(println);

    ------

    说明:

    keys:对返回一个仅包含键的RDD.

    ---------------------

    val pairrdd = sc.parallelize(List((1,2),(3,4),(3,6)));

    val result=pairrdd.values;//scala不要使用括号

    result.foreach(println);

    ------

    说明:

    values:对返回一个仅包含键的RDD.

    ---------------------

    val pairrdd = sc.parallelize(List((1,2),(3,4),(3,6)));

    val result=pairrdd.sortByKey(false);

    result.foreach(println);

    ------

    说明:

    sortByKey:对返回一个根据键排序的RDD.默认不填为true,ascending升序方式

    ---------------------

    4.例子:subtractByKey/join/leftOuterJoin/rightOuterJoin/coGroup

    ---------------------

    val rdd= sc.parallelize(List((1,2),(3,4),(3,6)));

    val other= sc.parallelize(List((3,6)));

    val result=rdd.subtract(other);

    result.foreach(println);

    ------

    说明:删除rdd RDD 键与other RDD中的键相同的元素.

    subtract:对返回一个根据键排序的RDD.

    结果:

    (3,4)

    (1,2)

    ---------------------

    val rdd= sc.parallelize(List((1,2),(3,4),(3,6)));

    val other= sc.parallelize(List((3,9)));

    val result=rdd.join(other);

    result.foreach(println);

    ------

    说明:

    join:对2个RDD进行内连接,key相同的进行操作

    结果:

    (3,(4,9))

    (3,(6,9))

    ---------------------

    val rdd= sc.parallelize(List((1,2),(3,4),(3,6)));

    val other= sc.parallelize(List((3,9)));

    val result=rdd.leftOuterJoin(other);

    result.foreach(println);

    ------

    说明:

    leftOuterJoin:左外连接,对2个rdd进行连接操作,确保左边(rdd RDD)的键一定存在

    结果:

    (1,(2,none))

    (3,(4,some(9)))

    (3,(6,some(9)))

    ---------------------

    val rdd= sc.parallelize(List((1,2),(3,4),(3,6)));

    val other= sc.parallelize(List((3,9)));

    val result=rdd.rightOuterJoin(other);

    result.foreach(println);

    ------

    说明:

    rightOuterJoin:右外连接,对2个rdd进行连接操作,确保右边(rdd RDD)的键一定存在

    结果:

    (3,(some(4),9))

    (3,(some(6),9))

    ---------------------

    val rdd= sc.parallelize(List((1,2),(3,4),(3,6)));

    val other= sc.parallelize(List((3,9)));

    val result=rdd.cogroup(other);

    result.foreach(println);

    ------

    说明:

    cogroup:将2个RDD拥有相同键的数据分组到一起

    结果:

    (1,(compactBuffer(2),compactBuffer()))

    (3,(compactBuffer(4,6),compactBuffer(9)))

    ---------------------

      

    三、键值对RDD行动操作实例

      和转换操作一样,所有基础RDD支持的传统行动操作也都在pair RDD上可用,除此之外,pair RDD还提供了一些额外的行动操作。

    1.例子:countByKey/collectAsMap/lookup(key)

    ---------------------

    val rdd= sc.parallelize(List((1,2),(3,4),(3,6)));

    val result=rdd.countByKey();

    result.foreach(println);

    ------

    说明:

    countByKey:对每个键对应的元素分别计数

    结果:

    (1,1)

    (3,2)

    ---------------------

    val rdd= sc.parallelize(List((1,2),(3,4),(3,6)));

    val result=rdd.collectAsMap();

    result.foreach(println);

    ------

    说明:

    collectAsMap:从结果我们可以看出,如果RDD中同一个Key中存在多个Value,那么后面的Value将会把前面的Value覆盖,最终得到的结果就是Key唯一,而且对应一个Value,《Spark快速大数据分析》第52页给出的结果是不对的。

    结果:

    (1,2)

    (3,6)

    ---------------------

    val rdd= sc.parallelize(List((1,2),(3,4),(3,6)));

    val result=rdd.lookup(3);

    result.foreach(println);

    ------

    说明:

    lookup:返回给定键对应的所有值

    结果:

    4

    6

    ---------------------

    三、参考资料

    1.http://blog.csdn.net/gamer_gyt/article/details/51747783,Spark 的键值对(pair RDD)操作,Scala实现

    2.http://blog.csdn.net/jiangpeng59/article/details/52538254,Spark核心RDD:combineByKey函数详解

  • 相关阅读:
    Spark基本架构及原理
    深度剖析Spark分布式执行原理
    Spark的集群管理器
    基于Redis的开源分布式服务Codis
    RabbitMQ 概念
    分布式事务实现
    优酷 视频上传服务
    深入解析Java中volatile关键字的作用
    mysql in查询 结果乱序 引发的思考
    不安装oracle客户端,用plsql连接oracle
  • 原文地址:https://www.cnblogs.com/licheng/p/6815313.html
Copyright © 2020-2023  润新知