Spark入门2

回顾

Spark是一个内存计算框架
在MR基础上做一个扩展


  • RDD resilient distributed dataset
  • transformation:变换 lazy map() filter
  • action 动作 count first take(n)
  • rdd.persist() 持久化
  • rdd. cache() == rdd.persist();
  • SparkContext : 到Spark集群的链接 SparkConf
  • local //本地模式,通过线程模拟
  • spark-shell --master local[4]
  • val rdd = sc.textFile(“file:///x/x/”,n);//n:并发程度

  • rdd.map
  • rdd.flatMap() //压扁操作

压扁操作 :把集合打开 元素释放出来

x to 5

非压扁操作
在这里插入图片描述
每一个都生成一个集合,一共五个集合

flatMap压扁操作
在这里插入图片描述

伪集合操作

1 两个集合

rdd1 = tom tom tpom tomaslee
rdd2 = tom tomaslee bob

0 reduce

def reduce(f: (T, T) ⇒ T): T

根据映射函数f,对RDD中的元素进行二元计算,返回计算结果。
scala> var rdd1 = sc.makeRDD(1 to 10,2)
rdd1: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[36] at makeRDD at :21
 
scala> rdd1.reduce(_ + _)
res18: Int = 55
 
scala> var rdd2 = sc.makeRDD(Array(("A",0),("A",2),("B",1),("B",2),("C",1)))
rdd2: org.apache.spark.rdd.RDD[(String, Int)] = ParallelCollectionRDD[38] at makeRDD at :21
 
scala> rdd2.reduce((x,y) => {
     |       (x._1 + y._1,x._2 + y._2)
     |     })
res21: (String, Int) = (CBBAA,6)

2 去除重复

rdd.distinct()//产生驱虫的新的rdd

3 collect 转换成数组

def collect(): Array[T]

collect用于将一个RDD转换成数组。

scala> var rdd1 = sc.makeRDD(1 to 10,2)
rdd1: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[36] at makeRDD at :21
 
scala> rdd1.collect
res23: Array[Int] = Array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)



在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
4 union联合

5 交集 intersection

6 差集 减法

7 笛卡尔积
在这里插入图片描述
在这里插入图片描述
8 fold

def fold(zeroValue: T)(op: (T, T) ⇒ T): T

fold是aggregate的简化,将aggregate中的seqOp和combOp使用同一个函数op。

scala> rdd1.fold(1)(
     |       (x,y) => x + y    
     |     )
res19: Int = 58
 
##结果同上面使用aggregate的第一个例子一样,即:
scala> rdd1.aggregate(1)(
     |           {(x,y) => x + y}, 
     |           {(a,b) => a + b}
     |     )
res20: Int = 58

rdd.aggregate(value)(seqOp, combOp)

刚才说到reduce()和fold(),这两个函数有一个问题,那就是它们的返回值必须与rdd的数据类型相同,啥意思呢?比如刚才那个例子,l的数据是Int,那么reduce()和flod()返回的也必须是Int。
aggregate()函数就打破了这个限制。比如我返回(Int, Int)。这很有用,比如我要计算平均值的时候。
要算平均值,我就有两个值是要求的,一个是rdd的各元素的累加和,另一个是元素计数,我初始化为(0, 0)。
那么就是

val l = List(1,2,3,4)
l.aggregate(0, 0)(seqOp, combOp)

那么seqOp和combOp怎么写呢?而combOp又是啥意思呢?
我们将seqOp写为

(x, y) => (x._1 + y, x._2 + 1)
val l = List(1,2,3,4)
l.reduce((x, y) => x + y)

对于这个x,它代指的是返回值,而y是对rdd各元素的遍历。
在aggregate()这也一样,x不是返回值吗,我返回值是(Int, Int)啊,它有两个元素啊,我可以用x._1和x._2来代指这两个元素的,y不是rdd的元素遍历吗,那我x._1 + y就是各个元素的累加和啊,x._2 + 1就是元素计数啊。遍历完成后返回的(Int, Int)就是累加和和元素计数啊。
按理说有这么一个函数就应该结束了,后边那个combOp是干嘛的?
因为我们的计算是分布式计算,这个函数是将累加器进行合并的。
例如第一个节点遍历1和2, 返回的是(3, 2),第二个节点遍历3和4, 返回的是(7, 2),那么将它们合并的话就是3 + 7, 2 + 2,用程序写就是

(x, y) => (x._1 + y._1, x._2 + y._2)
val l = List(1,2,3,4)
r = l.aggregate(0, 0)((x, y) => (x._1 + y, x._2 + 1), (x, y) => (x._1 + y._1, x._2 + y._2))
m = r._1 / r._2.toFload

action

1 count
2 collect
3 countByvalue
4 teke()

提取前n个元素,数组

5 first

  提取第一个元素,一个值

6 top()

提取末尾的n个元素
rdd.top(2) ==> 3,3

7 rdd.takeOrdered(3)

1 2 3

8 rdd.takeSample

9 fold()

10 .aggreate() == > (9,4)

rdd.aggregate((0,0))((x , y ))......

11 foreach()

rdd.foreach(println)

mean () //均值

variance() //方差





持久化

persist() //
1 Spark默认持久化对象到jvm heap中没有串行化

2 如果是off-heap或者磁盘存储必须是串行化的

3 串行化级别
在这里插入图片描述
在这里插入图片描述

rdd.persist(StrongeLevel.DISK_ONLY);		//存放到磁盘

操纵key-value

ETL(extract, transform, and load)

-----------抽取-------变换-------------加载---------------------------------
1 创建pairRDD

rdd.map(x=>(xxx,yyy))

在这里插入图片描述
在这里插入图片描述
2 pairRDD操作

准备
在这里插入图片描述
在这里插入图片描述

a.prdd.reduceByKey((x,y)=>x + y)			//	按照key分组操作,key不变 ,value运算。

//结果:
{(1,2),(3,10)}
b.prdd.groupByKey().collect

//结果
Array((1,CompactBuffer(2)),(3,CompactBuffer(4,6)))

在这里插入图片描述
在这里插入图片描述

4 rdd.mapValues()

rdd.mapValues(x=>x + 1) ===> {(1,3),(3,5),(3,7)}

5 rdd.flatMapValues()

将值压扁,在和key组合成新的pair

6 keys

取出每个新的pair的key,形成key集合
prdd。ksys()

7 values

rdd4.values.collect

在这里插入图片描述
8 sortByKey()
在这里插入图片描述
在这里插入图片描述
按照value排序
转换key value
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

Aggregations:聚合

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

单词统计

1 准备txt文件
在这里插入图片描述
在这里插入图片描述
2 压扁
x:每一行通过空格压扁
在这里插入图片描述
在这里插入图片描述

3 映射 每个(单词,1)

在这里插入图片描述
在这里插入图片描述

4 Reduce

在这里插入图片描述

在这里插入图片描述

总写
在这里插入图片描述
java写
在这里插入图片描述

=============================================================================

countByValue

//直接结算个数,按照key进行累加
rdd2.countByValue() === rdd2.map(x=>(x,1)).reduceByKey((x,y)=> x + y)

combineByKey
//paste:进入复制模式
在这里插入图片描述

调整并发程度

线程分配

===============================================================
在这里插入图片描述
在这里插入图片描述

1 制定分区数

rdd.reduceByKey(f:op,numParitions : Int){..}

2 检查分区数

val = rdd.partitions.size

3 重新指定分区数目

rdd.reparitions(4,true)//不推荐,等价于hadoop:job.setNumReduceTasks(4);
rdd.coalesce(4) // 推荐

对数据排列

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

PariRDD可用的action

====================================================================
1 准备数据

val rdd = sc.parallelize(Array((1,2),(3,4),(5,6)))

2 countByKey

//对每一个key统计个数,形成tuple
rdd.countByKey();	//(1,1),(3,2)

3 collectAsMap

//将tuple转换成map<Key-value>
rdd.collectAsMap()	//1-->2,3-->6

3 lookup(3)

//找出所有同key的value
rdd.lookup(3)//WrappedArry(4,6)

数据分区

数据分区:

在分布式集群里,网络通信的代价很大,减少网络传输可以极大提升性能。

mapreduce框架的性能开支主要在io和网络传输,io因为要大量读写文件,它是不可避免的,但是网络传输是可以避免的,把大文件压缩变小文件,从而减少网络传输,但是增加了cpu的计算负载。

spark里面io也是不可避免的,但是网络传输spark里面进行了优化:

spark把rdd进行分区(分片),放在集群上并行计算。

同一个rdd分片100个,10个节点,平均一个节点10个分区

当进行sum型的计算的时候,先进行每个分区的sum,然后把sum值shuffle传输到主程序进行全局sum,所以进行sum型计算对网络传输非常小。

但对于进行join型的计算的时候,需要把数据本身进行shuffle,网络开销很大。

spark是如何优化这个问题的呢?

spark把key-value rdd通过key的hashcode进行分区,而且保证相同的key存储在同一个节点上,这样对改rdd进行key聚合时,就不需要shuffle过程

我们进行mapreduce计算的时候为什么要尽兴shuffle?,就是说mapreduce里面网络传输主要在shuffle阶段,shuffle的根本原因是相同的key存在不同的节点上,按key进行聚合的时候不得不进行shuffle。shuffle是非常影响网络的,它要把所有的数据混在一起走网络,然后它才能把相同的key走到一起。要尽兴shuffle是存储决定的。

spark从这个教训中得到启发,spark会把key进行分区,也就是key的hashcode进行分区,相同的key,hashcode肯定是一样的,所以它进行分区的时候100t的数据分成10分,每部分10个t,它能确保相同的key肯定在一个分区里面,而且它能保证存储的时候相同的key能够存在同一个节点上。

比如一个rdd分成了100份,集群有10个节点,所以每个节点存10份,每一分称为每个分区,spark能保证相同的key存在同一个节点上,实际上相同的key存在同一个分区。

key的分布不均决定了有的分区大有的分区小。没法分区保证完全相等,但它会保证在一个接近的范围。

所以mapreduce里面做的某些工作里边,spark就不需要shuffle了,spark解决网络传输这块的根本原理就是这个。

进行join的时候是两个表,不可能把两个表都分区好,通常情况下是把用的频繁的大表事先进行分区,小表进行关联它的时候小表进行shuffle过程。

大表不需要shuffle。

模版是:

val userData = sc.sequenceFileUserID,UserInfo

.partitionBy(new HashPartition(100))//构造100个分区

.persist()

从分区中获益的操作:cogroup(), groupwith(),join(),leftOuterJoin(),rightOuterJoin(),groupByKey(),reduceByKey(),cobimeByKey(),lookup()

所有基于key的操作都会获益

对于诸如cogroup()和join()这样的二元操作,预先进行数据分区会让其中至少一个rdd(使用已知分区器的那个rdd)不发生数据shuffle,如果两个rdd使用同样的分区方式,并且它们还缓存在同样的机器上(比如一个rdd是通过mapvalues()从另一个rdd中创建出来的,这两个rdd就会拥有相同的key和分区方式),或者其中rdd还没有被计算出来,那么跨界点的shuffle(数据混洗)不会发生了。

mapreduce一般要求本地网卡达到20兆!即便进行了压缩!

import org.apache.hadoop.hive.ql.exec.persistence.HybridHashTableContainer.HashPartition
import org.apache.hadoop.mapred.lib
import org.apache.spark.SparkConf
import org.apache.spark.sql.SparkSession
import org.apache.spark.storage.StorageLevel
import org.apache.spark.HashPartitioner
/**
  * Created by zengxiaosen on 16/9/23.
  */
object PartitionVisitCount {

  /*
  大表小表关联
   */
  def main(args: Array[String]): Unit = {
    val sparkConf = new SparkConf().setAppName("useUDF").setMaster("local")
    val ss = SparkSession.builder().config(sparkConf).getOrCreate()
    val sc = ss.sparkContext

    val fileRDD = sc.textFile("/opt/tarballs/spark_kafka/beifengspark/src/main/scala/2015082818")
      .filter(line=>line.length>0)
      .map{
        line =>
          val arr = line.split("\t")
          val date = arr(17).substring(0,10)
          val guid = arr(5)
          val url = arr(1)
          val uid = arr(18)
          (uid,(guid,url)) //key-value:tuple2
      }.partitionBy(new HashPartitioner(10)) //采用了hashcode分片方式,分成了10份,十个分区,每个分区10分
      /*
      相同的key在同一个分区,在进行任务调用时候,大表不需要任何shuffle
      只需要shuffle小表
       */
      .persist(StorageLevel.DISK_ONLY)


    /*
    parallelize有两个参数,第一个是他的list,第二个是分区数
    分区数可以不给,不给的情况下默认就是它的核数
     */
    //比如里面存的是我的用户id
    val rdd = sc.parallelize(List(1,2,3,4,5,6,7),10)
      .map(i => (i+"", i+"str"))

    fileRDD.join(rdd).foreach(println)
    /*
    如果fileRDD后面还会关联好多个其他的rdd1,rdd2。。。rddn
    就要先把大的fileRDD进行分区
    这样优化了网络传输

     */


  }

}

分区器决策

1 获得rdd的分区器

val par:Option[Partitioner] = rdd.partitioner			//获得分区器Option
par.isDefined()										//判断分区器是否定义
val par0:Partition = par.get()				//获得分区对象

2 分区有益的方法

cogroup(),groupWith(),jion(),leftOuterJoin(),rightOuterJoin()
groupByKey(),reduceBykey(),combineBykey(),lookup()

Rank
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

PageRank

PageRank原理

自定义分区器

1 继承Partitioner
//显示完整导入类型
在这里插入图片描述
val rdd = sc…

val rdd2 = rdd.partitionBy(new TwoPartitioner()) //查看打印输出

rdd.groupByKey()

  • 5
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

oifengo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值