一次 Spark SQL 性能提升10倍的经历(转载)
1. 遇到了啥问题
是酱紫的,简单来说:并发执行 spark job 的时候,并发的提速很不明显。

嗯,且听我慢慢道来,啰嗦点说,类似于我们内部有一个系统给分析师用,他们写一些 sql,在我们的 spark cluster 上跑。随着分析师越来越多,sql job 也越来越多,等待运行的时间也越来越长,我们就在想怎么把 sql 运行的时间加快一点。我们的整个架构是 spark 1.6.1 on YARN 的,经过分析一些 sql 发现其实大多数分析语句都是比较简单的统计 sql,集群资源也还算多,一条简单的 sql 语句就把整个集群资源的坑占着略显不合适,有点飞机马达装到拖拉机上的赶脚,所以第一步,我们想,支持 spark job 的并行运行。
ok,初步方案有了,我们就做了如下几步改善工作:
首先设置
spark.scheduler.mode为FAIR模式,首先spark.scheduler.mode有FIFO,FAIR两种模式,FIFO是说提交的job,都是顺序执行的,后提交的 job 一定要等之前提交的 job 完全执行结束后才可以执行;FAIR是说,如果之前提交的 job 没有用完集群资源的话,后提交的job可以即刻开始运行。关于这点在官方文档上有详细的解释:
其次,我们生成了 10 个 pool,所谓的 pool,可以理解为资源池,或者通道。你可以在提交 job 的时候指定提交到哪个 pool 里面,可以简单的理解为我们把所有的集群资源分成 10 份,然后在提交 job 的时候指定在哪一份资源中运行这个 job。

最后,我们在提交 job 的时候指定提交到的 pool 名字,只需要在提交 job 之前设置一个 sparkContext 的参数即可:
sc.setLocalProperty("spark.scheduler.pool", "your_pool_id")
看似很简单,但能知道上面这些配置的也算是用 spark 比较熟练的人了吧,我迫不及待的测试了一下速度,发现了一个从古至今的大真理:理想很美好,现实很骨干啊。测试下来,发现多个 job 并行运行的时间并没有节省多少。
2. 原因排查
上面把问题说得很清楚了:多 job 并行的时候,运行速度并没有明显提升。但是原理上应该不会如此,只要一个 sql job 不需要全局所有集群资源,理论上来说会有较大提升的。下面是一组简单的数据对比:

虽然看到,并行计算后时间只需要之前的 50%,但是这里需要说明一下,这个数据不够稳定的哦,比如说偶尔会新增 10来秒 这样子的。这里 暂且接受提升 50% 的速度这样一个结论吧。
但是,理论上来说,还能提升更多,不满足 50% 的提升效率,我们接着深度解读 spark web ui 上的一些分析数据,尝试找找能否把速度再度提升一下。终于找到了核心原因,下面我就把整个排查的过程详细记录下来:
找一个花费时间较长的 job,进去看看执行的详情,这里我们用 job id 为 796 的这个 job

发现 job 796 有两个 stage,且有 99% 的时间都花在第一个 stage 1590 上了,而且需要注意的是,这个 stage 有 237.6mb 的数据读取,有可能需要经过网络从其他 hdfs 节点读过来,难道跟网络 I/O 有关?继续点进去看看。

进来这个 stage 内部,似乎发现问题所在了,首先我们先关注下图中标记的几个点,可以总结出几个点:
- 首先,该 stage 内的所有任务在 executor 上真正执行的时间【可以理解为 cpu time】是 2s
- 其次,该 stage 内任务执行完成的时间是 1.1 m,大概是 66s,可以理解为【wall time】
- 该 stage 内所有的 task,
schedule delay的时间中位数是 0.5s,最大达到 1s【真正执行的时间也才 2s 哦】 - 该 stage 内一共有 336 个task


到这里,问题根源基本上已经知道了,即 job 796 的大多数时间都被消耗在 stage 1590 的 336 个task 的 secheduler delay 上面了。
3. 如何解决
上面问题几乎已经明确了,现在就该看看肿么解决了。我当时是这样去考虑的:
- 为什么 scheduler delay 会这么大
因为资源不够,要解决这个问题,似乎唯一的办法就是增加集群资源了。可是哥们,集群是你想加就能加的吗?那可是要砸钱的呀?而且如果公司缺机器的话,想加集群资源也要经过 申请->审批->采购->分配->集群配置 大大小小几个阶段,说不一定等你找到女朋友了都还没搞定啊。
当时想着加资源这个方案短期不可取后,有那么几分钟是觉得有点烧脑的。我就静静的看着 web ui,心里在算,一个 task 如果平均 scheduler delay 0.5s 的话,这 336 个 task 就得 delay 118 秒,基本上都到 2 分钟了。这 delay 的时间可真够长的啊,就在算这个数值的时候,突然想到这样一个公式:total delay time = average delay time * task number。现在我们的问题是要解决 total delay time,那完全可以从两方面去解决呀:
- 降低 average delay time:目前来看似乎唯一的方法是砸钱加资源
- 降低 task 数:粗略来看,简单的降低 task 数的话,应该是能减少 total delay time 的,但是如果task 数降低了,意味着每个 task 需要处理的数据量就多了,那其他的时间应该是会增加一些的,比如说
Task Deserialization Time, Result Serialization Time, GC Time, Duration等。减少 task 数究竟能不能提高整体运行速度,似乎乍一看还真不好确定。
反正砸钱加资源这个方案暂时是行不通的,要不就再仔细分析一下降低task数这个方案。这里我们在仔细参考一下下图中这一列指标:


我们用 75 分位的统计数据来做一个假设:假设我们把每一个 task 的数据量加 10 倍,那么预计的 task metrics 75 分位大概是一个什么样的数值,假设这些指标都是线性增长的话:
- Duration: 扩大到 10 倍,14ms
- Scheduler Delay: 这个指标不用估计
- Task Deserialization Time: 扩大到 10 倍,6ms
- GC Time: 扩大到 10 倍,最多1ms
- Result Serialization Time: 扩大到 10 倍,最多1ms
- Getting Result Time: 扩大到 10 倍,最多1ms
- Peak Execution Memory: 扩大到 10 倍,最多 1b
- Input Size / Records: 扩大到 10 倍,918.8 KB * 2 / 2
- Shuffle Write Size / Records 0: 扩大到 10 倍,294.0 B * 2/ 20
可以看到,这样大概估计下来,除去 Scheduler Delay 的时间,其实其他时间也没消耗多少,都是毫秒级的,看起来应该是完全可行的呀。
正准备这样测试的时候,我忽然想到,为什么现在的 metrics 统计是这样的结构的啊,这么多 task?一般来说,一个 task 对应到 hdfs 上的一个 parquet 文件【该项目中所有数据文件都是用 parquet 压缩后存储到 hdfs 上的】,难道是现在存在 hdfs 上的 parquet 文件个数过多,每个文件太小?突然有一种恍然大悟的感觉,赶紧看看现在 hdfs 上文件的结构,如下所示:
aotao@mac007:~/Desktop/dyes/git-mercury/mercury-computing$hadoop fs -ls -h hdfs://hadoop-archive-cluster/hfmkt/level2/datayes/parquet/20161117/20161117_Transaction_SZ | cat -n | tail
-rw-r--r-- taotao hfmkt 1.3 M -- : hdfs://hadoop-archive-cluster/hfmkt/level2/datayes/parquet/20161117/20161117_Transaction_SZ/part-r-00310-38d7cc53-60d2-40b3-a945-0cb5832f30de.gz.parquet
-rw-r--r-- taotao hfmkt 1.4 M -- : hdfs://hadoop-archive-cluster/hfmkt/level2/datayes/parquet/20161117/20161117_Transaction_SZ/part-r-00311-38d7cc53-60d2-40b3-a945-0cb5832f30de.gz.parquet
-rw-r--r-- taotao hfmkt 2.9 M -- : hdfs://hadoop-archive-cluster/hfmkt/level2/datayes/parquet/20161117/20161117_Transaction_SZ/part-r-00312-38d7cc53-60d2-40b3-a945-0cb5832f30de.gz.parquet
-rw-r--r-- taotao hfmkt 1.2 M -- : hdfs://hadoop-archive-cluster/hfmkt/level2/datayes/parquet/20161117/20161117_Transaction_SZ/part-r-00313-38d7cc53-60d2-40b3-a945-0cb5832f30de.gz.parquet
-rw-r--r-- taotao hfmkt 1.9 M -- : hdfs://hadoop-archive-cluster/hfmkt/level2/datayes/parquet/20161117/20161117_Transaction_SZ/part-r-00314-38d7cc53-60d2-40b3-a945-0cb5832f30de.gz.parquet
-rw-r--r-- taotao hfmkt 1.7 M -- : hdfs://hadoop-archive-cluster/hfmkt/level2/datayes/parquet/20161117/20161117_Transaction_SZ/part-r-00315-38d7cc53-60d2-40b3-a945-0cb5832f30de.gz.parquet
-rw-r--r-- taotao hfmkt 899.4 K -- : hdfs://hadoop-archive-cluster/hfmkt/level2/datayes/parquet/20161117/20161117_Transaction_SZ/part-r-00316-38d7cc53-60d2-40b3-a945-0cb5832f30de.gz.parquet
-rw-r--r-- taotao hfmkt 2.3 M -- : hdfs://hadoop-archive-cluster/hfmkt/level2/datayes/parquet/20161117/20161117_Transaction_SZ/part-r-00317-38d7cc53-60d2-40b3-a945-0cb5832f30de.gz.parquet
-rw-r--r-- taotao hfmkt 1.0 M -- : hdfs://hadoop-archive-cluster/hfmkt/level2/datayes/parquet/20161117/20161117_Transaction_SZ/part-r-00318-38d7cc53-60d2-40b3-a945-0cb5832f30de.gz.parquet
-rw-r--r-- taotao hfmkt 460.9 K -- : hdfs://hadoop-archive-cluster/hfmkt/level2/datayes/parquet/20161117/20161117_Transaction_SZ/part-r-00319-38d7cc53-60d2-40b3-a945-0cb5832f30de.gz.parquet
可以看到,现在有 300 多个文件【上面只是一部分,还有十几个在另外一个文件夹里,一个 sql 会统计两个文件夹里的数据文件】,而且我仔细看了一下,每个文件大小最小的有很多 1kb 的,最大的有 2.9mb 的。难怪了,原来核心根源在这里。再结合上面关于 metrics 的分析,我心里大概确信了,只要把 parquet 文件的问题解决就行了,方法就是压缩 parquet 文件个数,控制每个 parquet 文件的大小即可。

方法确定了,那就干咯。
4. 效果对比
未来方便对比,我把 20161212 的数据文件处理了一下,保留 20161117 这天的数据文件【20161212 的数据文件整体上比 20161117 的数据文件要多 10%】,下面是对比结果:
parquet 文件个数
- 20161117 这天
taotao@mac007:~/Desktop/dyes/git-mercury/mercury-computing$hadoop fs -ls -h hdfs://hadoop-archive-cluster/hfmkt/level2/datayes/parquet/20161117/20161117_Transaction_S* | cat -n | tail -n 5
-rw-r--r-- taotao hfmkt 1.7 M -- : hdfs://hadoop-archive-cluster/hfmkt/level2/datayes/parquet/20161117/20161117_Transaction_SZ/part-r-00315-38d7cc53-60d2-40b3-a945-0cb5832f30de.gz.parquet
-rw-r--r-- taotao hfmkt 899.4 K -- : hdfs://hadoop-archive-cluster/hfmkt/level2/datayes/parquet/20161117/20161117_Transaction_SZ/part-r-00316-38d7cc53-60d2-40b3-a945-0cb5832f30de.gz.parquet
-rw-r--r-- taotao hfmkt 2.3 M -- : hdfs://hadoop-archive-cluster/hfmkt/level2/datayes/parquet/20161117/20161117_Transaction_SZ/part-r-00317-38d7cc53-60d2-40b3-a945-0cb5832f30de.gz.parquet
-rw-r--r-- taotao hfmkt 1.0 M -- : hdfs://hadoop-archive-cluster/hfmkt/level2/datayes/parquet/20161117/20161117_Transaction_SZ/part-r-00318-38d7cc53-60d2-40b3-a945-0cb5832f30de.gz.parquet
-rw-r--r-- taotao hfmkt 460.9 K -- : hdfs://hadoop-archive-cluster/hfmkt/level2/datayes/parquet/20161117/20161117_Transaction_SZ/part-r-00319-38d7cc53-60d2-40b3-a945-0cb5832f30de.gz.parquet
- 20161212 这天
taotao@mac007:~/Desktop/dyes/git-mercury/mercury-computing$hadoop fs -ls -h hdfs://hadoop-archive-cluster/hfmkt/level2/datayes/parquet/20161212/20161212_Transaction_S* | cat -n | tail -n 5
-rw-r--r-- taotao hfmkt 19.2 M -- : hdfs://hadoop-archive-cluster/hfmkt/level2/datayes/parquet/20161212/20161212_Transaction_SZ/part-r-00013-686bbce5-a7a1-4b5d-b25c-14cd9ddae283.gz.parquet
-rw-r--r-- taotao hfmkt 10.7 M -- : hdfs://hadoop-archive-cluster/hfmkt/level2/datayes/parquet/20161212/20161212_Transaction_SZ/part-r-00014-686bbce5-a7a1-4b5d-b25c-14cd9ddae283.gz.parquet
-rw-r--r-- taotao hfmkt 26.0 M -- : hdfs://hadoop-archive-cluster/hfmkt/level2/datayes/parquet/20161212/20161212_Transaction_SZ/part-r-00015-686bbce5-a7a1-4b5d-b25c-14cd9ddae283.gz.parquet
-rw-r--r-- taotao hfmkt 20.1 M -- : hdfs://hadoop-archive-cluster/hfmkt/level2/datayes/parquet/20161212/20161212_Transaction_SZ/part-r-00016-686bbce5-a7a1-4b5d-b25c-14cd9ddae283.gz.parquet
-rw-r--r-- taotao hfmkt 8.7 M -- : hdfs://hadoop-archive-cluster/hfmkt/level2/datayes/parquet/20161212/20161212_Transaction_SZ/part-r-00017-686bbce5-a7a1-4b5d-b25c-14cd9ddae283.gz.parquet
100个job并发执行时间
- 20161117 这天:99s
- 20161212 这天:16s
Spark Web UI 上一个 job 对比
- 20161117 这天






5. 总结
首先,需要说明的是,这次优化应该还有提升的空间,虽然优化后整体从 204s 到 99s 再到 16s,提升了十倍多,确实很大,但是最后我们还是发现 16s 的情况下,scheduler delay 和 Task Deserialization Time 还是有占用了大部分时间,这里我觉得不能一味的在文件个数和大小上下功夫了。需要考虑到用户场景来做一个权衡。所以越到后期的优化,越考验产品功能的设计,当然这是后话了,就不在本文范围内讨论。
其次,这次优化,从发现问题,追根溯源,到最后解决问题,大概花了 1 小时,基本上还算不错。通过这次排查,还是真心感受到 spark 设计的完善,不得不说,作为一个开源项目,spark 最大的特点,我觉得应该是 spark 是由一帮非程序员设计实现的,而是一帮由程序员,架构师,产品经理组合起来一起干的,更像是一个产品,而不是一个开源项目。怪不得这帮人要去开个公司【databricks:我最看好的公司之一】,看来真的是 born this way。
转载自:https://blog.csdn.net/SunWuKong_Hadoop/article/details/84649900
一次 Spark SQL 性能提升10倍的经历(转载)的更多相关文章
- Web 应用性能提升 10 倍的 10 个建议
转载自http://blog.jobbole.com/94962/ 提升 Web 应用的性能变得越来越重要.线上经济活动的份额持续增长,当前发达世界中 5 % 的经济发生在互联网上(查看下面资源的统计 ...
- Elasticsearch Reindex性能提升10倍+实战
文章转载自: https://mp.weixin.qq.com/s?__biz=MzI2NDY1MTA3OQ==&mid=2247484134&idx=1&sn=750249a ...
- 存算分离下写性能提升10倍以上,EMR Spark引擎是如何做到的?
引言 随着大数据技术架构的演进,存储与计算分离的架构能更好的满足用户对降低数据存储成本,按需调度计算资源的诉求,正在成为越来越多人的选择.相较 HDFS,数据存储在对象存储上可以节约存储成本,但与此 ...
- Nacos 2.0 正式发布,性能提升 10 倍!!
3月20号,Nacos 2.0.0 正式发布了! Nacos 简介: 一个更易于构建云原生应用的动态服务发现.配置管理和服务管理平台. 通俗点讲,Nacos 就是一把微服务双刃剑:注册中心 + 配置中 ...
- 如何把 MySQL 备份验证性能提升 10 倍
JuiceFS 非常适合用来做 MySQL 物理备份,具体使用参考我们的官方文档.最近有个客户在测试时反馈,备份验证的数据准备(xtrabackup --prepare)过程非常慢.我们借助 Juic ...
- 八年技术加持,性能提升10倍,阿里云HBase 2.0首发商用
摘要: 早在2010年开始,阿里巴巴集团开始研究并把HBase投入生产环境使用,从最初的淘宝历史交易记录,到蚂蚁安全风控数据存储,HBase在几代阿里专家的不懈努力下,已经表现得运行更稳定.性能更高效 ...
- 如何把Go调用C的性能提升10倍?
目前,当Go需要和C/C++代码集成的时候,大家最先想到的肯定是CGO.毕竟是官方的解决方案,而且简单. 但是CGO是非常慢的.因为CGO其实一个桥接器,通过自动生成代码,CGO在保留了C/C++运行 ...
- mariadb使用with子句重写SQL性能提升5倍
几个月前,我们有个产品的开发反馈了个问题,说有个组织结构的查询很慢,几千行的复杂关联需要1秒钟,表示太慢了,原语句如下: SELECT org.org_id, org.dimension, org.o ...
- Databricks缓存提升Spark性能--为什么NVMe固态硬盘能够提升10倍缓存性能(原创)
我们兴奋的宣布Databricks缓存的通用可用性,作为统一分析平台一部分的 Databricks 运行时特性,它可以将Spark工作负载的扫描速度提升10倍,并且这种改变无需任何代码修改. 1.在本 ...
随机推荐
- jsp 安全
一. 身份验证和授权 认证是检验某人真正是他/她自称的那个人的过 程.在一个Servlet/JSP应用程序中,身份验证一般通过 检查用户名密码是否正确.授权是检查该级别的用户是 否具备访问权限.它适 ...
- jquery 第五章 jQuery操作表单与表格
1.回顾 对象.bind("事件名称",function(){ // }) 阻止冒泡事件 return false, event stopProapagation() 模拟事件 ...
- 大一上c语言学习总结
一: 近期C语言知识点易错点整理(1)main()----在c语言中称之为“主函数”,一个c程序有且仅有一个main函数,任何一个c程序总是从main函数开始执行,main函数后面的一对圆括号不能省略 ...
- HDU 5977 Garden of Eden
题解: 路径统计比较容易想到点分治和dp dp的话是f[i][j]表示以i为根,取了i,颜色数状态为j的方案数 但是转移这里如果暴力转移就是$(2^k)^2$了 于是用FWT优化集合或 另外http: ...
- js分析 猫_眼_电_影 字体文件 @font-face
0. 参考 https://developer.mozilla.org/zh-CN/docs/Web/CSS/@font-face 这是一个叫做@font-face 的CSS @规则 ,它允许网页开发 ...
- CentOS 7 yum方式快速安装MongoDB
一.安装环境及配置yum # more /etc/redhat-release CentOS Linux release 7.2.1511 (Core) # vi /etc/yum.repos.d/m ...
- 动态规划——Split Array Largest Sum
题意大概就是,给定一个包含非负整数的序列nums以及一个整数m,要求把序列nums分成m份,并且要让这m个子序列各自的和的最大值最小(minimize the largest sum among th ...
- 入门级----黑盒测试、白盒测试、手工测试、自动化测试、探索性测试、单元测试、性能测试、数据库性能、压力测试、安全性测试、SQL注入、缓冲区溢出、环境测试
黑盒测试 黑盒测试把产品软件当成是一个黑箱子,只有出口和入口,测试过程中只要知道往黑盒中输入什么东西,知道黑盒会出来什么结果就可以了,不需要了解黑箱子里面是如果做的. 即测试人员不用费神去理解软件里面 ...
- Python数据分析基础PDF
Python数据分析基础(高清版)PDF 百度网盘 链接:https://pan.baidu.com/s/1ImzS7Sy8TLlTshxcB8RhdA 提取码:6xeu 复制这段内容后打开百度网盘手 ...
- react中constructor()和super()的具体含义以及如何使用
1.constructor()---super( )的基本含义 constructor()--构造方法 这是ES6对类的默认方法,通过new命令生成对象实例时自动调用该方法.并且,该方法是类中必须有的 ...