spark的RDD的详解。。。。

Resilient Distributed Dataset(RDD),弹性分布式数据集,是Spark上的一个核心抽象,表示用于并行计算的,不可修改的,对数据集合进行分区的分布式的数据结构。不同来源的数据 都可以经过转换变为RDD 再由Spark进行处理。
        这个数据集的全部或部分可以缓存在内存中,在多次计算间重用。它是spark提供的一个特殊集合类。普通的集合数据作为一个整体,但RDD中的数据进行了分区Partition处理,这样做的目的就是为了分布式。如:传统List(1,2,3,4)是一个整体,RDD可能就是RDD(1,2) (3,4)。这样如需计算时,就把1和2发送给一个worker,把3和4发送给另一个worker。按分区完成数据的分发进行分布式运算。
        主要分为三部分组成:数据分片、计算函数、RDD依赖(lineage血缘关系)
        Lineage让RDD有了生命,可以进行向前的追溯,当某个节点计算错误时,只需要根据Lineage重新计算相关的操作而不必回滚整个程序,这点对懒执行和容错特别有意义。

创建RDD 有两种方式:1、直接读取外部数据源(S3, hdfs, 文件);2利用scala的集合类来构建RDD 

sc.makeRDD()   sc.textFile()   sc.parallelize()  这个三个都可以指定分区数

rdd.collect 操作的话,会把所有partiton的数据以Array的形式返回,这样效率会很低,并且有可能数据量超过了内存 ,导致了OOM. 所以在开发中这个操作不要用了。

查看分区结构
            spark并没有原生的提供rdd的分区查看工具 我们可以自己来写一个

            import org.apache.spark.rdd.RDD
            import scala.reflect.ClassTag
            object su {
                def debug[T: ClassTag](rdd: RDD[T]) = {
                    rdd.mapPartitionsWithIndex((i: Int, iter: Iterator[T]) => {
                        val m = scala.collection.mutable.Map[Int, List[T]]()
                        var list = List[T]()
                        while (iter.hasNext) {
                            list = list :+ iter.next
                        }
                        m(i) = list
                        m.iterator
                    }).collect().foreach((x: Tuple2[Int, List[T]]) => {
                        val i = x._1
                        println(s"partition:[$i]")
                        x._2.foreach { println }
                    })
                }
            }

上面的单例对象用来方便查看RDD的分区的数据

1.RDD分为两类
        PairRDD
            键值对类型的RDD
        RDD
            普通类型的RDD

    2.PairRDD提供的方法
        (1) aggregateByKey(zeroValue)(func1,func2)
            zeroValue表示初始值,初始值会参与func1的计算
            在分区内,按key分组,把每组的值进行fun1的计算
            再将每个分区每组的计算结果按fun2进行计算

            val rdd = sc.parallelize(List(("cat",2), ("dog",5),("cat",4),("dog",3),("cat",6),("dog",3),("cat",9),("dog",1)),2);
            scala> su.debug(rdd)
                partition:[0]
                (cat,2)
                (dog,5)
                (cat,4)
                (dog,3)
                partition:[1]
                (cat,6)
                (dog,3)
                (cat,9)
                (dog,1)
            scala> import scala.math._
            scala> rdd.aggregateByKey(0)(max(_,_),_+_);
                Array((dog,8), (cat,13))

        (2) groupByKey
            val rdd = sc.parallelize(List(("cat",2), ("dog",5),("cat",4),("dog",3),("cat",6),("dog",3),("cat",9),("dog",1)),2);
            rdd.groupByKey()

        (3) reduceByKey 按照键来进行合并处理
            var rdd = sc.makeRDD( List( ("hello",1),("spark",1),("hello",1),("world",1) ) )
            rdd.reduceByKey(_+_);
                    
        (4) join
            val rdd1 = sc.makeRDD(List(("cat",1),("dog",2)))
            val rdd2 = sc.makeRDD(List(("cat",3),("dog",4),("tiger",9)))
            rdd1.join(rdd2);

        (5) partitionBy
            通常我们在创建RDD时指定分区规则 将会导致 数据自动分区
            我们也可以通过partitionBy方法人为指定分区方式来进行分区
            常见的分区器有:
                HashPartitioner
                RangePartitioner

            import org.apache.spark._
            var rdd = sc.makeRDD(List((2,"aaa"),(9,"bbb"),(7,"ccc"),(9,"ddd"),(3,"eee"),(2,"fff")),2);
            rdd.partitionBy(new HashPartitioner(2))//按照键的 hash%分区数 得到的编号去往指定的分区 这种方式可以实现将相同键的数据 分发给同一个分区的效果
            rdd.partitionBy(new RangePartitioner(2,rdd))//将数据按照键的字典顺序进行排序 再分区

    3.普通RDD
        (1) 集合间的操作
            distinct 去重
                val rdd = sc.makeRDD(List(1,3,5,7,9,3,7,10,23,7));
                rdd.distinct
            union 并集 -- 也可以用++实现
                val rdd1 = sc.makeRDD(List(1,3,5));
                val rdd2 = sc.makeRDD(List(2,4,6,8));
                val rdd = rdd1.union(rdd2);
                val rdd = rdd1 ++ rdd2;
            intersection 交集
                val rdd1 = sc.makeRDD(List(1,3,5,7));
                val rdd2 = sc.makeRDD(List(5,7,9,11));
                val rdd = rdd1.intersection(rdd2);
            subtract 差集
                val rdd1 = sc.makeRDD(List(1,3,5,7,9));
                val rdd2 = sc.makeRDD(List(5,7,9,11,13));
                val rdd =  rdd1.subtract(rdd2);

        (2) collect 收集
            //将rdd分布式存储在集群中不同分区的数据 获取到一起 组成一个数组返回
            //要注意 这个方法将会把所有数据搞到一个机器内 容易造成内存的溢出 在生产环境下千万慎用
            rdd.collect

        (3) take 获取前几个数据
            val rdd = sc.makeRDD(List(52,31,22,43,14,35))
            rdd.take(2)

        (4) takeOrdered(n) 先将rdd中的数据进行升序排序 然后取前n个
            val rdd = sc.makeRDD(List(52,31,22,43,14,35))
            rdd.takeOrdered(3)

        (5) top(n) 先将rdd中的数据进行降序排序 然后取前n个
            val rdd = sc.makeRDD(List(52,31,22,43,14,35))
            rdd.top(3)    
        
        (6) map 将函数应用到rdd的每个元素中
            val rdd = sc.makeRDD(List(1,3,5,7,9))
            rdd.map(_*10)

        (7) filter 用来从rdd中过滤掉不符合条件的数据
            val rdd = sc.makeRDD(List(1,3,5,7,9));
            rdd.filter(_<5);

        (8) flatMap 扁平map处理
            val rdd = sc.makeRDD(List("hello world","hello count","world spark"),2)
            //Array(Array(hello, world), Array(hello, count), Array(world, spark))
            rdd.map(_.split{" "})
            //Array[String] = Array(hello, world, hello, count, world, spark)
            rdd.flatMap(_.split{" "})
        
        (9) cache 缓存
            可以为rdd设置缓存
            rdd.cache()
            这样当未来需要重新获取rdd中的数据时 不需要重新创建 直接可以从还从中得到数据从而提升效率
            这个缓存信息可以在spark的ui管理界面中查看到
            
        (10) persist 缓存
            import org.apache.spark.storage.StorageLevel
            rdd.persist(StorageLevel.MEMORY_ONLY_SER)

        (11) cartesian 笛卡尔积
            val rdd1 = sc.makeRDD(List(1,2,3))
            val rdd2 = sc.makeRDD(List("a","b"))
            rdd1.cartesian(rdd2);

        (12) coalesce(n,true/false) 扩大或缩小分区
            val rdd = sc.makeRDD(List(1,2,3,4,5),2)
            rdd9.coalesce(3,true);//如果是扩大分区 需要传入一个true 表示要重新shuffle
            rdd9.coalesce(2);//如果是缩小分区 默认就是false 不需要明确的传入

        (13) repartition(n) 等价于上面的coalesce
            
        (14) count 统计rdd中元素的个数
            val rdd = sc.makeRDD(List(1,2,3,4,5),2)
            rdd.count

        (15) countApprox 统计rdd的元素个数 计算一个近似值
            可以传入一个时间毫秒值 时间越长求出的结果精度越高
            val rdd = sc.makeRDD(1 to 1000000000,5)
            rdd.countApprox(5 * 1000)

        (16) mapPartitionsWithIndex 分别遍历分区做不同的处理
            val rdd = sc.makeRDD(List(1,2,3,4,5),2);
            rdd.mapPartitionsWithIndex((i,iter)=>{
                var list = List[String]()
                if(i==0){
                    while(iter.hasNext){
                        list = list :+ (iter.next + "a")
                    }
                } else {
                    while(iter.hasNext){
                        list = list :+ (iter.next + "b")
                    }
                }
                list.iterator
            });
            
        (17) saveAsTextFile 按照文本方式保存分区数据
            val rdd = sc.makeRDD(List(1,2,3,4,5),2);
            rdd.saveAsTextFile("/root/work/aaa")
        
        (18) textFile 读取文件数据成为rdd
            val rdd = sc.textFile("/root/work/words.txt",2);

        (19) sortBy 将rdd中的数据经过函数处理根据处理结果将原始数据进行排序
            val rdd = sc.makeRDD(List(123,324,1,35,23,5));    
            rdd.sortBy(x=>x);//升序
            rdd.sortBy(x=>x,false);//降序
            rdd.sortBy(x=>{if(x<100)x*1000 else x})

        (20) zip 拉链操作
            val rdd1 = sc.makeRDD(List("aaa","bbb","ccc"));
            val rdd2 = sc.makeRDD(List(1,2,3));
            rdd1.zip(rdd2)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值