0、拓展
-
spark数据移动的两种方式:
- IO移动:指数据不需要区分每一条数据归属于哪个分区,既不需要通过partitioner
- shuffle:指数据需要经过洗牌,计算其分区号,好确认数据区到哪个特定的分区
- 如果有的行为不需要区分记录,直接本地IO去拉取数据,那么这种本地IO一定比先partitioner,再计算,shuffle落文件,最后IO去拉取速度快的多
-
spark的人性化:
- spark提供很多封装的方法,都是经过尝试、经验推算出实现方式的,不需要人为干预
- 遇到不懂的方法,点进去源码看一眼
1、spark面向数据集操作
- 带函数操作:map、flatMap等
- 单元素操作:union、cartesion等
- kv元素操作:cogroup、join等
- 排序操作:sorted等
- 聚合计算操作:combineByKey、reduceByKey等
2、代码示例
-
带函数操作
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("-")) -
单元素操作
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 -
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) -
排序操作
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) -
聚合计算操作
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)