[转] - spark推荐 - 从50多分钟到3分钟的优化
从50多分钟到3分钟的优化
某推荐系统需要基于Spark用ALS算法对近一天的数据进行实时训练, 然后进行推荐. 输入的数据有114G, 但训练时间加上预测的时间需要50多分钟, 而业务的要求是在15分钟左右, 远远达不到实时推荐的要求, 因此, 我们与业务侧一起对Spark应用进行了优化.
另外提一下, 该文最好与之前我写的另一篇blog < Spark + Kafka 流计算优化 > 一起看, 因为一些细节我不会再在该文中描述.
优化分析
从数据分析, 虽然数据有114G, 但ALS的模型训练时间并不长, 反而是数据加载和ALS预测所占用的时间较长. 因此我们把重点放在这两点的优化中.
从Spark的Web UI可以看到, 数据加载的job中task的数量较多, 较小. 模型预测的job也是有这样的问题, 因此, 可以猜测并行度过大造成了集群的协调负荷过重.
仅降低并行度和优化JVM 参数
我们用常见的”rdd.repartitionBy”把并行度降低后, 作业计算耗时减少并不明显, 且在模型预测的job中会有executor死掉的现象. 进而查看日志, 发现是内存占用过多, yarn把Spark应用给kill了.
为解决该现象, 加入这些JVM参数: “ -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:MaxTenuringThreshold=72 -XX:NewRatio=2 -XX:SurvivorRatio=6 -XX:+CMSParallelRemarkEnabled -XX:+ParallelRefProcEnabled -XX:+CMSPermGenSweepingEnabled -XX:+CMSClassUnloadingEnabled -XX:MaxTenuringThreshold=31 -XX:SurvivorRatio=8 -XX:+ExplicitGCInvokesConcurrent -XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses -XX:+AlwaysPreTouch -XX:-OmitStackTraceInFastThrow -XX:+UseCompressedStrings -XX:+UseStringCache -XX:+OptimizeStringConcat -XX:+UseCompressedOops -XX:+CMSScavengeBeforeRemark -XX:+UseBiasedLocking -XX:+AggressiveOpts " 具体说明和原因请参考本人的另一篇blog < Spark + Kafka 流计算优化 >.
笛卡尔积操作中的预先分块
作了上述例行优化后, ALS预测步骤的耗时减少依然不明显, 为发掘原因, 我们看了下源码, 惊奇的发现在ALS预测中竟然是用了笛卡尔积操作, 114G的数据少说也有几千万行记录, 几千万行记录进行笛卡尔积, 不慢才怪吧.
还好, 我们有扩展版的ALS预测方法, 可以将数据预先分块, 而不必一行行的进行笛卡尔积, 加快了笛卡尔积的速度.
val recommendations = ExtMatrixFactorizationModelHelper.recommendProductsForUsers( model.get, numRecommendations, 420000, StorageLevel.MEMORY_AND_DISK_SER )
该方法会对model中的userFeatures和itemFeatures矩阵进行预先分块, 减少网络包的传输量和笛卡尔积的计算量.
这里的第三个参数是每一个块所包含的行数, 此处的420000表示当我们对userFeatures或itemFeatures进行分块时, 每一个块包含了矩阵的420000行.
如何计算这里的一个块要包含多少行呢, 举例如下:
由于ALS算法中设置的rank是10, 因此生成的userFeatures和itemFeatures的个数是10, 它们的每一行是(Int,Array[Double]), 其中Array.size是10.
因此可根据如下计算每行所占的空间大小:
空Array[10]的大小=16+8*10=96Byte. 数组中的元素是Double, 十个Double对象的大小是 16*10=160Byte. 作为key的Integer的大小是16Byte. 因此每行占空间96+160+16=212Byte.
另外,要计算带宽: 由于是千兆网卡,因此带宽为1Gbit,转换为Byte也就是128MByte的带宽.
考虑到” spark.akka.frameSize=100”以及网络包包头需要占的空间, 和Java的各种封装要占的空间, 我们计划让1个block就几乎占满带宽, 也就是一个block会在100MByte左右.
因此, 一个block要占 60*1024*1024/212=296766行, 因此blocksize=494611, 考虑到各个object也占内存, 因此行数定为420000左右.
在分块后ExtMatrixFactorizationModelHelper.recommendProductsForUsers中会对块进行重新分区, 以达到基于块的均匀分布.
提高文件加载速度
以前都是加载小文件, 每个文件才几M大小, 远远低于Hadoop的块大小, 使得IO频繁, 文件也频繁打开关闭, 加载速度自然就慢. 为解决该问题, 我们使用sc.wholeTextFiles(dirstr,inputSplitNum)来加载HDFS的文件到Spark中. 该方法使用Hadoop的CombineFileInputFormat把多个小文件合并成一个Split再加载到Spark中.
但其实, 加载速度对整个Job的运行效率影响不大, 效果有限.

上图,是val inputData = sc.wholeTextFiles(dirstr,80) 和RangePartition.
貌似加载速度也好不到哪儿去.第一个job用了11min
'
上图, 用的是val inputData = sc.textFile(dirstr)和RangeRepartition,同等情况下,加载也需要11min. (见job6)

上图,是val inputData = sc.wholeTextFiles(dirstr,120) 和RangePartition.
貌似提高了wholeTextFiles()的split数量可以提升性能, 从8.4min多(此时split为80左右)到现在的8.1min

上图,是val inputData = sc.wholeTextFiles(dirstr,220) 和RangePartition. 第一次加载提升到6.0min.
其它job由于loclity的问题,时间有所拉长, 影响不大.

上图,是val inputData = sc.wholeTextFiles(dirstr,512) 和RangePartition. 第一次加载要7.1min. 估计220个split应该是个比较好的值了. 按比例就是 220(file split数) / 27000(小文件数) = 0.8% , 该场景中每个小文件10M左右. 也就是每个file split包含123个小文件, 每个file split 1230M, 也就是约1G左右.
减少笛卡尔积计算量
回到对笛卡尔积计算的优化, 因为50分钟的计算量基本上都是耗在笛卡尔积的计算上的.
我们先看一下task的分布图, 由图看到, 数据并不是十分散列:
猜测原因肯是通过Array.mkString.hashCode作为key并不能保证数据的均匀散列.因此, 我们disable掉了在笛卡尔积计算前的预分块时的再分区, 而是把再分区提到分块之前.
这样一来, task分布稍微均衡了一些, 但依然不甚理想. 为了合理的降低task数量和均匀task的分布, 我们进一步使用了Spark扩展版本的自动分区功能.
val userFactors = ExtSparkHelper.repartionPairRDDBySize(oldUsrFact, ThreeM)
这个方法有两个参数, 第一个是需要分区的RDD, 第二个是我们期望的每一个task的input data的大小. 一般来说, input data与计算产生的data的大小相差不大, 但笛卡尔积却不同, 有可能产生上百倍的中间数据量. 因此, 我这里设置的每个task的input data是3M, 计算产生的中间数据刚好在1G左右, 一个executor可以同时跑3个task, 也算是比较理想的.
优化后的task分布图也比较理想, 十分均匀且没有任何浪费, 如下:

因此, 这一步优化后, 原笛卡尔积的运行速度从几十分钟变为十几秒.
整个ALS应用原来跑114G数据需要20多分钟,如下图现在只需要不到4min:

试了一下跑一天的全量数据, 共231.9G, 则原来需要1个多小时,现在只要不到6分钟, 如下图:

[转] - spark推荐 - 从50多分钟到3分钟的优化的更多相关文章
- 1.java小作业-计算1到100的整合-指定输入多少行输出就打印多少行-打印24小时60分钟每一分钟-重载基础练习-面向java编程初学者
可能有和我一样刚开始学习java的小伙伴们, 可以或多或少了解一点别的语言知识,我就是中途转过来的, 明白一点,关键不在语言本身····· 所以面对初学者来说,基础要学好, 下面列举几个没什么难度的小 ...
- 【MySQL】花10分钟阅读下MySQL数据库优化总结
1.花10分钟阅读下MySQL数据库优化总结http://www.kuqin.com2.扩展阅读:数据库三范式http://www.cnblogs.com3.my.ini--->C:\Progr ...
- top,它们的意思分别是1分钟、5分钟、15分钟内系统的平均负荷。
理解Linux系统负荷 作者: 阮一峰 日期: 2011年7月31日 一.查看系统负荷 如果你的电脑很慢,你或许想查看一下,它的工作量是否太大了. 在Linux系统中,我们一般使用uptime ...
- 个性化推荐调优:重写spark推荐api
最近用spark的mlib模块中的协同过滤库做个性化推荐.spark里面用的是als算法,本质上是矩阵分解svd降维,把一个M*N的用户商品评分矩阵分解为M*K的userFeature(用户特征矩阵) ...
- 2016最新 wamp2.5+windows 10安装CoedSgniffer代码格式检查:5分钟安装 30分钟入门和浏览常用命令
14:59 2016/1/112016最新 wamp2.5+windows 10安装CoedSgniffer代码格式检查:注意问题:1.手动安装2.5.0和pear安装方式都成功但是执行时无任何反映, ...
- python获取时间————前一天后一天前一小时后一小时前一分钟后一分钟
获取当天日期 一: import time print(time.strftime("%Y-%m-%d")) #输出当前日期 2018-05-01 二: import dateti ...
- hadoop的mapReduce和Spark的shuffle过程的详解与对比及优化
https://blog.csdn.net/u010697988/article/details/70173104 大数据的分布式计算框架目前使用的最多的就是hadoop的mapReduce和Spar ...
- mysql 中两个日期相减获得 天 小时 分钟 或者 小时:分钟的格式
/**有一个需求,要求获得两个日期想减的天数,小时数,分钟数.通过查找资料,于是乎我写出了如下代码,来获得两个字段.*/ IFNULL(CONCAT( ,'-',''), ),),'天')), ),) ...
- 用笛卡尔积来创建一千六百万大表 整体19分钟 大表建成两分钟 设置id13分钟
昨天拙文中讲述了用自增方式创建一千六百万大表的方案,这回讨论的是用笛卡儿积,实践证明这种方案更快. 2020年3月15日08点58分实验开始 创建仅有四千数据的tb_4thousand1表: SQL& ...
随机推荐
- CentOS7 命令笔记
网络 ifconfig已经过时,查看ip地址请使用ip addr或者ip link 服务 查看系统和硬件信息 cat /etc/os-release uname -r 显示正在使用的内核版本 dmid ...
- 微信支付WxpayAPI_php_v3 错误修改
微信sdk:WxpayAPI_php_v3 这是下载压缩包的目录结构. https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1 ce ...
- Python:提取网页中的电子邮箱
import requests, re #regex = r"([a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+)"#这个正则表达式过滤 ...
- [javase学习笔记]-6.2 类与对象的关系
这一节我们来看一下类与对象之间的关系. 我们学习java语言,目的就是用java语言对现实生活中的事物进行描写叙述.那么我们如何来描写叙述呢.这就引出了类,我们在实际实现时,是通过类的形式来体现的. ...
- 【Android】Android传感器
1.加速度传感器2.磁场传感器3.方向传感器4.陀螺仪传感器5.重力传感器6.线性加速度传感器7.温度传感器8.光线传感器9.距离传感器10.压力传感器11.计步传感器 首先先查看测试的安卓机拥有的传 ...
- 【Java】java数据库连接中C3P、DBCP、Druid连接池的使用
使用JDBC的步骤:1.加载数据库驱动2.通过DriverManager获得数据库连接3.通过Connection获得Statement对象4.使用Statement执行SQL语句.5.操作结果集合6 ...
- 我的Nginx配置文件
#user nobody; worker_processes 1; #error_log logs/error.log; #error_log logs/error.log notice; #erro ...
- linux每日命令(15):tail命令
tail 命令从指定点开始将文件写到标准输出.使用tail命令的-f选项可以方便的查阅正在改变的日志文件,tail -f filename会把filename里最尾部的内容显示在屏幕上,并且不断刷新, ...
- 【ARM】arm系列知识框架
[ARM编程模型] 硬件: 电路原理图 软件: 体系结构, 指令集, 寄存器组 [ARM编程技术] 汇编/C语言 编译, 链接, 烧写和调试 windows: MDK linux : gcc [AR ...
- Node入门教程(8)第六章:path 模块详解
path 模块详解 path 模块提供了一些工具函数,用于处理文件与目录的路径.由于windows和其他系统之间路径不统一,path模块还专门做了相关处理,屏蔽了彼此之间的差异. 可移植操作系统接口( ...