Spark处理数据排序问题如何避免OOM


Posted in Python onMay 21, 2020

错误思想

举个列子,当我们想要比较 一个 类型为 RDD[(Long, (String, Int))] 的RDD,让它先按Long分组,然后按int的值进行倒序排序,最容易想到的思维就是先分组,然后把Iterable 转换为 list,然后sortby,但是这样却有一个致命的缺点,就是Iterable 在内存中是一个指针,不占内存,而list是一个容器,占用内存,如果Iterable 含有元素过多,那么极易引起OOM

val cidAndSidCountGrouped: RDD[(Long, Iterable[(String, Int)])] = cidAndSidCount.groupByKey()
    // 4. 排序, 取top10
    val result: RDD[(Long, List[(String, Int)])] = cidAndSidCountGrouped.map {
      case (cid, sidCountIt) =>
        // sidCountIt 排序, 取前10
        // Iterable转成容器式集合的时候, 如果数据量过大, 极有可能导致oom
        (cid, sidCountIt.toList.sortBy(-_._2).take(5))
    }

首先,我们要知道,RDD 的排序需要 shuffle, 是采用了内存+磁盘来完成的排序.这样能有效避免OOM的风险,但是RDD是全部排序,所以需要针对性的过滤Key值来进行排序

方法一 利用RDD排序特点

//把long(即key值)提取出来
    val cids: List[Long] = categoryCountList.map(_.cid.toLong)
    val buffer: ListBuffer[(Long, List[(String, Int)])] = ListBuffer[(Long, List[(String, Int)])]()
    //根据每个key来过滤RDD
    for (cid <- cids) {
      /*
      List((15,(632972a4-f811-4000-b920-dc12ea803a41,10)), (15,(f34878b8-1784-4d81-a4d1-0c93ce53e942,8)), (15,(5e3545a0-1521-4ad6-91fe-e792c20c46da,8)), (15,(66a421b0-839d-49ae-a386-5fa3ed75226f,8)), (15,(9fa653ec-5a22-4938-83c5-21521d083cd0,8)))
      目标:
      (9,List((199f8e1d-db1a-4174-b0c2-ef095aaef3ee,9), (329b966c-d61b-46ad-949a-7e37142d384a,8), (5e3545a0-1521-4ad6-91fe-e792c20c46da,8), (e306c00b-a6c5-44c2-9c77-15e919340324,7), (bed60a57-3f81-4616-9e8b-067445695a77,7)))
       */
      val arr: Array[(String, Int)] = cidAndSidCount.filter(cid == _._1)
        .sortBy(-_._2._2)
        .take(5)
        .map(_._2)
      buffer += ((cid, arr.toList))
    }
    buffer.foreach(println)

这样做也有缺点:即有多少个key,就有多少个Job,占用资源

方法二 利用TreeSet自动排序特性

def statCategoryTop10Session_3(sc: SparkContext,
                  categoryCountList: List[CategroyCount],
                  userVisitActionRDD: RDD[UserVisitAction]) = {
    // 1. 过滤出来 top10品类的所有点击记录
    // 1.1 先map出来top10的品类id
    val cids = categoryCountList.map(_.cid.toLong)
    val topCategoryActionRDD: RDD[UserVisitAction] = userVisitActionRDD.filter(action => cids.contains(action.click_category_id))


    // 2. 计算每个品类 下的每个session 的点击量 rdd ((cid, sid) ,1)
    val cidAndSidCount: RDD[(Long, (String, Int))] = topCategoryActionRDD
      .map(action => ((action.click_category_id, action.session_id), 1))
      // 使用自定义分区器 重点理解分区器的原理
      .reduceByKey(new CategoryPartitioner(cids), _ + _)
      .map {
        case ((cid, sid), count) => (cid, (sid, count))
      }
    
    // 3. 排序取top10
//因为已经按key分好了区,所以用Mappartitions ,在每个分区中新建一个TreeSet即可
    val result: RDD[(Long, List[SessionInfo])] = cidAndSidCount.mapPartitions((it: Iterator[(Long, (String, Int))]) => {
//new 一个TreeSet,并同时指定排序规则
   var treeSet: mutable.TreeSet[CategorySession] = new mutable.TreeSet[CategorySession]()(new Ordering[CategorySession] {
          override def compare(x: CategorySession, y: CategorySession): Int = {
            if (x.clickCount >= y.clickCount) -1 else 1
          }
        })
   var id = 0l
  iter.foreach({
    case (l, session) => {
      id = l
      treeSet.add(session)
    if (treeSet.size > 10) treeSet = treeSet.take(10)
          }
        })
        Iterator(id, treeSet)
      })
  
    result.collect.foreach(println)
    
    Thread.sleep(1000000)
  }
}

/*
根据传入的key值来决定分区号,让相同key进入相同的分区,能够避免多次shuffle
 */
class CategoryPartitioner(cids: List[Long]) extends Partitioner {
  // 用cid索引, 作为将来他的分区索引.
  private val cidWithIndex: Map[Long, Int] = cids.zipWithIndex.toMap
  
  // 返回集合的长度
  override def numPartitions: Int = cids.length
  
  // 根据key返回分区的索引
  override def getPartition(key: Any): Int = {
    key match {
      // 根据品类id返回分区的索引!  0-9
      case (cid: Long, _) =>
        cidWithIndex(cid)
    }
  }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python提示No module named images的解决方法
Sep 29 Python
介绍Python的@property装饰器的用法
Apr 28 Python
python比较2个xml内容的方法
May 11 Python
Windows下Anaconda的安装和简单使用方法
Jan 04 Python
详解Django的CSRF认证实现
Oct 09 Python
pandas使用apply多列生成一列数据的实例
Nov 28 Python
Django上使用数据可视化利器Bokeh解析
Jul 31 Python
python Dijkstra算法实现最短路径问题的方法
Sep 19 Python
python3.8下载及安装步骤详解
Jan 15 Python
django 获取字段最大值,最新的记录操作
Aug 09 Python
python 爬取腾讯视频评论的实现步骤
Feb 18 Python
关于python3 opencv 图像二值化的问题(cv2.adaptiveThreshold函数)
Apr 04 Python
Django 解决开发自定义抛出异常的问题
May 21 #Python
Python logging模块写入中文出现乱码
May 21 #Python
django的403/404/500错误自定义页面的配置方式
May 21 #Python
python 3.8.3 安装配置图文教程
May 21 #Python
Python中的xlrd模块使用原理解析
May 21 #Python
python中sklearn的pipeline模块实例详解
May 21 #Python
Python使用re模块验证危险字符
May 21 #Python
You might like
动画 《Pokemon Sword·Shield》系列WEB动画《薄明之翼》第2话声优阵容公开!
2020/03/06 日漫
prototype Element学习笔记(篇二)
2008/10/26 Javascript
javascript 清空form表单中某种元素的值
2009/12/26 Javascript
JavaScript 5 新增 Array 方法实现介绍
2012/02/06 Javascript
JQuery弹出层示例可自定义
2014/05/19 Javascript
使用jQuery实现图片遮罩半透明坠落遮挡
2015/03/16 Javascript
js实现的四级左侧网站分类菜单实例
2015/05/06 Javascript
js简单网速测试方法完整实例
2015/12/15 Javascript
jQuery实现的调整表格行tr上下顺序
2016/01/10 Javascript
探究Javascript模板引擎mustache.js使用方法
2016/01/26 Javascript
JS中setTimeout的巧妙用法前端函数节流
2016/03/24 Javascript
基于JS如何实现给字符加千分符(65,541,694,158)
2016/08/03 Javascript
解决前端跨域问题方案汇总
2016/11/20 Javascript
通过vue-router懒加载解决首次加载时资源过多导致的速度缓慢问题
2018/04/08 Javascript
express 项目分层实践详解
2018/12/10 Javascript
Vue插槽原理与用法详解
2019/03/05 Javascript
mongodb初始化并使用node.js实现mongodb操作封装方法
2019/04/02 Javascript
微信小程序自定义导航栏实例代码
2019/04/05 Javascript
详解vue中axios请求的封装
2019/04/08 Javascript
JS实现容器模块左右拖动效果
2020/01/14 Javascript
Vue强制组件重新渲染的方法讨论
2020/02/03 Javascript
python 示例分享---逻辑推理编程解决八皇后
2014/07/20 Python
Python操作RabbitMQ服务器实现消息队列的路由功能
2016/06/29 Python
深入浅析Python获取对象信息的函数type()、isinstance()、dir()
2018/09/17 Python
Python3.5以上版本lxml导入etree报错的解决方案
2019/06/26 Python
Python3操作Excel文件(读写)的简单实例
2019/09/02 Python
python3实现往mysql中插入datetime类型的数据
2020/03/02 Python
Python WebSocket长连接心跳与短连接的示例
2020/11/24 Python
python中函数返回多个结果的实例方法
2020/12/16 Python
python+selenium爬取微博热搜存入Mysql的实现方法
2021/01/27 Python
HTML5录音实践总结(Preact)
2020/05/07 HTML / CSS
高级技校毕业生自荐信
2013/11/18 职场文书
超市开学活动方案
2014/03/01 职场文书
房屋租赁意向书范本
2015/05/09 职场文书
2019年个人工作总结范文
2019/03/25 职场文书
vue/cli 配置动态代理无需重启服务的方法
2022/05/20 Vue.js