Spark基础02-RDD数据集操作

304 阅读2分钟

0、拓展

  • spark数据移动的两种方式:

    1. IO移动:指数据不需要区分每一条数据归属于哪个分区,既不需要通过partitioner
    2. shuffle:指数据需要经过洗牌,计算其分区号,好确认数据区到哪个特定的分区
    3. 如果有的行为不需要区分记录,直接本地IO去拉取数据,那么这种本地IO一定比先partitioner,再计算,shuffle落文件,最后IO去拉取速度快的多
  • spark的人性化:

    1. spark提供很多封装的方法,都是经过尝试、经验推算出实现方式的,不需要人为干预
    2. 遇到不懂的方法,点进去源码看一眼

1、spark面向数据集操作

  • 带函数操作:map、flatMap等
  • 单元素操作:union、cartesion等
  • kv元素操作:cogroup、join等
  • 排序操作:sorted等
  • 聚合计算操作:combineByKey、reduceByKey等

2、代码示例

  1. 带函数操作

    val dataRDD: RDD[Int] = sc.parallelize(List(1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1))
    // 需要传入函数去操作数据集
    val mapRDD = dataRDD.map(x=>{x+1})
    
    val filterRDD = dataRDD.filter( _>1)
    
    val flatMapRdd = flatMap(_.split("-"))
    
  2. 单元素操作

    val rdd1: RDD[Int] = sc.parallelize(1 to 5)
    val rdd2: RDD[Int] = sc.parallelize(3 to 10)
    
    // 差集: 有方向的
    // 1-2
    rdd1.subtract(rdd2).foreach(println) //1、2
    // 2-1
    rdd2.subtract(rdd1).foreach(println) //6、7、8、9、10
    
    // 交集
    rdd1.intersection(rdd2).foreach(println) //3、4、5
    
    // 笛卡尔积
    rdd1.cartesian(rdd2).foreach(println) //直接IO不走shuffle
    
    // 并集
    println(rdd1.partitions.size)  //1
    println(rdd2.partitions.size)  //1
    val unionRDD: RDD[Int] = rdd1.union(rdd2)//直接IO不走shuffle
    println(unionRDD.partitions.size) //2
    
  3. kv元素操作

    val kv1: RDD[(String ,Int)] = sc.parallelize(List(
          ("zhangsan", 17),
          ("zhangsan", 18),
            ("lisi", 11),
            ("wangwu", 13)
        ))
    
    val kv2: RDD[(String ,Int)] = sc.parallelize(List(
          ("zhangsan", 20),
            ("lisi", 21),
            ("wangwu", 23),
            ("zhaoliu", 11)
        ))
    
    val cogroup: RDD[(String, (Iterable[Int], Iterable[Int]))] = kv1.cogroup(kv2)
    
    cogroup.foreach(println) // (zhangsan,(CompactBuffer(17),CompactBuffer(20)))
    
    // 以下方法都是基于cogroup封装实现的
    val join: RDD[(String, (Int, Int))] = kv1.join(kv2)
    join.foreach(println)
    
    val left: RDD[(String, (Int, Option[Int]))] = kv1.leftOuterJoin(kv2)
    left.foreach(println)
    
    val right: RDD[(String, (Option[Int], Int))] = kv1.rightOuterJoin(kv2)
    right.foreach(println)
    
    val full: RDD[(String, (Option[Int], Option[Int]))] = kv1.fullOuterJoin(kv2)
    full.foreach(println)
    
  4. 排序操作

    val data: RDD[(String, Int)] = sc.parallelize(
       List(("hello", 5), ("spark", 3), ("hadoop", 6))
    )
    data.map(_.swap).sortByKey().foreach(println)
    data.sortBy(_._2).foreach(println)
    
  5. 聚合计算操作

    val data: RDD[(String, Int)] = sc.parallelize(List(
      ("zhangsan", 234),
      ("zhangsan", 5667),
      ("zhangsan", 343),
      ("lisi", 212),
      ("lisi", 44),
      ("lisi", 33),
      ("wangwu", 535),
      ("wangwu", 22)
    ))
    
    val grouped: RDD[(String, Iterable[Int])] = data.groupByKey()
    grouped.foreach(println) //(zhangsan,CompactBuffer(234, 5667, 343))
    
    println("--------sum,count,min,max,avg------------")
    
    // 使用reduceByKey实现sum,count,min,max,avg
    val sum: RDD[(String, Int)] = data.reduceByKey(_ + _)
    val max: RDD[(String, Int)] = data.reduceByKey((ov, nv) => {
      if (ov > nv) ov else nv
    })
    val min: RDD[(String, Int)] = data.reduceByKey((ov, nv) => {
      if (ov < nv) ov else nv
    })
    val count: RDD[(String, Int)] = data.mapValues(e => 1).reduceByKey(_ + _)
    val avg: RDD[(String, Int)] = sum.join(count).mapValues(e => e._1 / e._2)
    
    // groupByKey、reduceByKey 底层其实都是基于combineByKey封装实现的
    // 使用combineByKey实现avg
    val tmpx: RDD[(String, (Int, Int))] = data.combineByKey(
    
      // 第一条记录的value,例如:怎么放入hashmap
      // createCombiner: V => C,
      (value: Int) => (value, 1),
      // 如果有第二条记录,第二条以及后面的他们的value怎么放到hashmap
      // mergeValue: (C, V) => C,
      (oldValue: (Int, Int), newValue: Int) => (oldValue._1 + newValue, oldValue._2 + 1),
      // 合并溢写结果的函数
      // mergeCombiners: (C, C) => C)
      (v1: (Int, Int), v2: (Int, Int)) => (v1._1 + v2._1, v1._2 + v2._2)
    )
    tmpx.mapValues(e => e._1 / e._2).foreach(println)