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 相关文章推荐
scrapy自定义pipeline类实现将采集数据保存到mongodb的方法
Apr 16 Python
详解使用Python处理文件目录的相关方法
Oct 16 Python
python3解析库BeautifulSoup4的安装配置与基本用法
Jun 26 Python
Python爬虫将爬取的图片写入world文档的方法
Nov 07 Python
python实现文件助手中查看微信撤回消息
Apr 29 Python
Python3.5局部变量与全局变量作用域实例分析
Apr 30 Python
详解如何从TensorFlow的mnist数据集导出手写体数字图片
Aug 05 Python
python飞机大战pygame游戏背景设计详解
Dec 17 Python
Python数据持久化存储实现方法分析
Dec 21 Python
解决jupyter notebook 前面书写后面内容消失的问题
Apr 13 Python
python logging通过json文件配置的步骤
Apr 27 Python
python filecmp.dircmp实现递归比对两个目录的方法
May 22 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
PHP网上调查系统
2006/10/09 PHP
PHP第一季视频教程(李炎恢+php100 不断更新)
2011/05/29 PHP
php 函数中使用static的说明
2012/06/01 PHP
解决php用mysql方式连接数据库出现Deprecated报错问题
2019/12/25 PHP
一些有用的JavaScript和jQuery的片段分享
2011/08/23 Javascript
javascript 获取浏览器版本
2015/01/21 Javascript
js使用cookie记录用户名的方法
2015/11/26 Javascript
jQuery短信验证倒计时功能实现方法详解
2016/05/25 Javascript
IONIC自定义subheader的最佳解决方案
2016/09/22 Javascript
详解vue组件通信的三种方式
2017/06/30 Javascript
Node.js简单入门前传
2017/08/21 Javascript
基于vue-cli vue-router搭建底部导航栏移动前端项目
2018/02/28 Javascript
vue toggle做一个点击切换class(实例讲解)
2018/03/13 Javascript
vue2.0 移动端实现下拉刷新和上拉加载更多的示例
2018/04/23 Javascript
vue使用jsonp抓取qq音乐数据的方法
2018/06/21 Javascript
jQuery扩展方法实现Form表单与Json互相转换的实例代码
2018/09/05 jQuery
js实现图片放大并跟随鼠标移动特效
2019/01/18 Javascript
在Vue项目中取消ESLint代码检测的步骤讲解
2019/01/27 Javascript
JS中比Switch...Case更优雅的多条件判断写法
2019/09/05 Javascript
Vue清除定时器setInterval优化方案分享
2020/07/21 Javascript
Vue触发input选取文件点击事件操作
2020/08/07 Javascript
[18:32]DOTA2 HEROS教学视频教你分分钟做大人-谜团
2014/06/12 DOTA
Python作用域用法实例详解
2016/03/15 Python
Python SqlAlchemy动态添加数据表字段实例解析
2018/02/07 Python
tensorflow: variable的值与variable.read_value()的值区别详解
2018/07/30 Python
python实现向微信用户发送每日一句 python实现微信聊天机器人
2019/03/27 Python
Python从入门到精通之环境搭建教程图解
2019/09/26 Python
Python生成验证码、计算具体日期是一年中的第几天实例代码详解
2019/10/16 Python
520使用Python实现“我爱你”表白
2020/05/20 Python
python 对一幅灰度图像进行直方图均衡化
2020/10/27 Python
基于html5 DeviceOrientation 实现微信摇一摇功能
2015/09/25 HTML / CSS
教师职位说明书
2014/07/29 职场文书
大学生毕业个人总结
2015/02/15 职场文书
银行实习推荐信
2015/03/27 职场文书
我的法兰西岁月观后感
2015/06/09 职场文书
Win11怎么添加用户?Win11添加用户账户的方法
2022/07/15 数码科技