RDD上的特殊方法

rdd.cache在触发action时会把计算结果算出来,与此同时会把对应的rdd放到内存里面,如果就触发一次action或者这个 rdd没有复用就没意义
cache会怎么用嫩:通常会吧数据先过滤一下保留有用的字段在rdd.cache

rdd.unpersist(true)      用对应的rdd点unpersist(true)就可以把他从内存中释放掉
参数填true是同步的,是阻塞,要这边释放掉drive端代码才可以往下走
默认是false是异步的,是不阻塞

但是用原本的存储格式是java对象的方式存储数据太大可以序列化一下,甚至压缩

sc.getConf.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer") 默认可写可不写
val rdd = sc.textFile("hdfs://linux01:8020/4.txt")  这是读hdfs上数据
import org.apache.spark.storage.StorageLevel   这是导包
rdd.persist(StorageLevel.MEMORY_ONLY_SER)      调用指定内存是否序列化的方式放在内存中
rdd.count

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

//参数1代表磁盘   参数2代表内存   参数三代表堆外内存    参数4序列化(false序列化)
//啥也不放,不放内存也不放磁盘
val NONE = new StorageLevel(false, false, false, false)
//只放磁盘
  *val DISK_ONLY = new StorageLevel(true, false, false, false)
  val DISK_ONLY_2 = new StorageLevel(true, false, false, false, 2)
  val MEMORY_ONLY = new StorageLevel(false, true, false, true)
  val MEMORY_ONLY_2 = new StorageLevel(false, true, false, true, 2)
  val MEMORY_ONLY_SER = new StorageLevel(false, true, false, false)
  val MEMORY_ONLY_SER_2 = new StorageLevel(false, true, false, false, 2)
  val MEMORY_AND_DISK = new StorageLevel(true, true, false, true)
  val MEMORY_AND_DISK_2 = new StorageLevel(true, true, false, true, 2)
  *val MEMORY_AND_DISK_SER = new StorageLevel(true, true, false, false)
  val MEMORY_AND_DISK_SER_2 = new StorageLevel(true, true, false, false, 2)
  val OFF_HEAP = new StorageLevel(true, true, true, false, 1)

persist


 1. cache、persist的使用场景:一个application多次触发Action,为了复用前面RDD计算好的数据,
 避免反复读取HDFS(数据源)中的数据和重复计算
 2.缓存,可以将数据缓存到内存或磁盘【executor所在的磁盘】,第一次触发action才放入到内存或磁盘,
 以后再出发Action会读取缓存的RDD的数据再进行操作并且复用缓存的数据
 3.一个RDD多次触发Action缓存才有意义
 4.如果将数据缓存到内存,内存不够,以分区位单位,只缓存部分分区的数据
 5.支持多种StorageLevel,可以将数据序列化,默认放入内存使用的是java对象存储,但是占用空间大,优
 点速度快,也可以使用其他的序列化方式
 6.cache底层调用的是persist方法,可以指定其他存储级别
 7.cache和persist方法,严格来说,不是Transformation,应为没有生成新的RDD,只是标记当前rdd
 要cache或persist
 8.	原始的数据,经过整理过滤后再进行cache或persist效果更佳,
 9.在生产环境建议使用Kryo作为默认序列化器,如果cache或persist,建议使用MEMORY_AND_DISK_SER
 

checkpoint

其实就是把数据进行持久化保存起来,做检查点,保存到靠谱的分布式文件系统(hdfs),理论上不会丢失

val rdd = sc.textFile("hdfs://linux01:8020/4.txt")  创建RDD从hdfs中读
val filtered = rdd.filter(_.contains("laozhao"))    对数据进行过滤
sc.setCheckpointDir("hdfs://linux01/checkpoint1")    指定保存到hdfs中的目录
filtered.checkpoint      chechpoint操作计算结果保存到hdfs中
checkpoint就是为了把中间计算好的结果保存到hdfs中以后再一个Application可以反复使用	
注意再调用chechpoint之前一定要指定一个存储目录
1.Checkpoint使用场景:适合复杂的计算(机器学习,迭代计算),为了避免丢失数据重复计算,可以将宝贵的中
间结果保存到hdfs中,中间结果安全
2.再调用的rdd的checkpoint方法之前,一定要 指定checkpoint的目录即sc.setCheckPointDir
3.为保证中间结果安全,将数据保存到hdfs中,分布式文件系统,可以保证数据不丢失
4.第一次触发Action,才做checkpoint,会额外触发一个job,这个job的目的就是将结果保存到hdfs中
5.如果rdd做了checkpoint,这个rdd以前的依赖关系就不再使用了
6.触发多次Action,checkpoint才有意义,多用于迭代计算
7.checkpoint严格说,不是Transformatin,只标记当前rdd要做checkpoint
8.如果chevkpoint前,对rdd进行了cache,可以避免数据重复计算,如果有caahe的数据有限使用cache,
如果cache的数据丢失再使用checkpoint

案例 广播关联

1.通常是为了实现mapside join,可以将Driver端的数据广播到属于该application的Executor,然后
通过Driver广播变量返回的引用,获取实现广播到Executor的数据
2.广播变量是通过BT的方式广播的(TorrentBroadcast),多个Executor可以相互传递数据,可以提供效率
3.sc.broadcast这个方法是阻塞的(同步的)
4.广播变量一但广播出去就不能改变,为了以后可以定期的改变要关联的数据,可以定义一个object[单例
对象],在函数内使用,并且加一个定时器,然后定期更新数据
5.广播到Executor的数据,可以在Driver获取到引用,然后这个引用会伴随着每一个Task发送到Executor,
然后通过这个引用,获取到事先广播好的数据
20090121000132095572000|125.213.100.123|show.51.com|/shoplist.php?phpfile=shoplist2.php&style=1&sex=137|Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Mozilla/4.0(Compatible Mozilla/4.0(Compatible-EmbeddedWB 14.59 http://bsalsa.com/ EmbeddedWB- 14.59  from: http://bsalsa.com/ )|http://show.51.com/main.php|


1.0.1.0|1.0.3.255|16777472|16778239|亚洲|中国|福建|福州||电信|350100|China|CN|119.306239|26.075302


上面是运营商的一条信息,ip地址对应下面的范围数据就是后面的对应的地理位置
这是自己封装的启动SparkContext的方法

import org.apache.spark.{SparkConf, SparkContext}

object SparkUtils {
  def createContext(isLocal: Boolean =true): SparkContext = {
    val conf = new SparkConf().setAppName(this.getClass.getSimpleName)
    if (isLocal){
      conf.setMaster("local[*]")
    }
    val sc =new SparkContext(conf)
    sc
  }

}

这是自己封装的转换十进制和二分查找的方法

import scala.collection.mutable.ArrayBuffer

object IpUtils {

  /**
   * 将IP地址转成十进制
   *
   * @param ip
   * @return
   */
  def ip2Long(ip: String): Long = {
    val fragments = ip.split("[.]")
    var ipNum = 0L
    for (i <- 0 until fragments.length) {
      ipNum = fragments(i).toLong | ipNum << 8L
    }
    ipNum
  }

  /**
   * 二分法查找
   *
   * @param lines
   * @param ip
   * @return
   */
  def binarySearch(lines: ArrayBuffer[(Long, Long, String, String)], ip: Long): Int = {
    var low = 0 //起始
    var high = lines.length - 1 //结束
    while (low <= high) {
      val middle = (low + high) / 2
      if ((ip >= lines(middle)._1) && (ip <= lines(middle)._2))
        return middle
      if (ip < lines(middle)._1)
        high = middle - 1
      else {
        low = middle + 1
      }
    }
    -1 //没有找到
  }

  def binarySearch(lines: Array[(Long, Long, String, String)], ip: Long): Int = {
    var low = 0 //起始
    var high = lines.length - 1 //结束
    while (low <= high) {
      val middle = (low + high) / 2
      if ((ip >= lines(middle)._1) && (ip <= lines(middle)._2))
        return middle
      if (ip < lines(middle)._1)
        high = middle - 1
      else {
        low = middle + 1
      }
    }
    -1 //没有找到
  }
}

这是具体广播关联的代码

import org.apache.spark.broadcast.Broadcast
import org.apache.spark.rdd.RDD
/*
    广播变量通常是为了实现mapSideJoin减少网络传输, 可以将小的数据广播到属于该application的Executor中,
    要广播的数据不宜太大, 要根据服务器的内存和分配的executor-memory决定
    广播变量是readonly, 一旦广播出去就不能改变了
    但是在Driver可以调用unpersist将光标变量释放,如果以后在Executor用到该变量,需要重新广播
 */
object IpDemo {
  def main(args: Array[String]): Unit = {
    val sc = SparkUtils.createContext(args(0).toBoolean)
    val ipLines = sc.textFile(args(1))
    //使用spark读取ip规则数据
    val ipRulesRDD: RDD[(Long, Long, String, String)] = ipLines.map(i => {
      //1.0.8.0|1.0.15.255|16779264|16781311|亚洲|中国|广东|广州||电信|440100|China|CN|113.280637|23.125178
      val fields = i.split("[|]")
      //ip最大
      val starNum = fields(2).toLong
      //ip最小
      val endNum = fields(3).toLong
      //省份
      val province = fields(6)
      //城市
      val city = fields(7)
      (starNum, endNum, province, city)
    })
    //  将整理后的ip规则数据整理到drive端
    val ipRulesInDrive: Array[(Long, Long, String, String)] = ipRulesRDD.collect()
    //将driver的ip规则数据广播出去 ,该方法时一个(同步)阻塞的方法
    val broadcastRef: Broadcast[Array[(Long, Long, String, String)]] = sc.broadcast(ipRulesInDrive)
    //读取访问日志数据
    val accesslog: RDD[String] = sc.textFile(args(2))
//切分访问数据
    val provinceAndOne = accesslog.map(e => {
      val fields = e.split("[|]")
      //得到运营商的真正ip地址
      val ip = fields(1)
      //通过封装的方法将ip转换成十进制
      val ipNum = IpUtils.ip2Long(ip)
      //通过driver返回的广播变量引用,获得事先已经广播到当前Excutor中的数据,用广播变量返回点value相当于得到了真正的广播数据
      val ipRulesInExcutor: Array[(Long,Long, String, String)] = broadcastRef.value
      //进行二分查找.之前写好的方法,参数写的是返回广播的真实数据和真正IP转的十进制
      val index = IpUtils.binarySearch(ipRulesInExcutor, ipNum)
      var province = "未知"
      if (index >= 0) {
        province = ipRulesInExcutor(index)._3
      }

      (province, 1)

    })
    val reduced = provinceAndOne.reduceByKey(_ + _)
    val res = reduced.collect()
    println(res.toBuffer)
  }

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值