ITPub博客

首页 > 数据库 > 数据库开发技术 > Spark 的 repartition 慎用,coalesce 可能没卵用!

Spark 的 repartition 慎用,coalesce 可能没卵用!

原创 数据库开发技术 作者:安瑞哥是码农 时间:2024-09-04 17:46:57 0 删除 编辑


上篇文章为了验证 Spark 任务需要的内存,跟单个 partition 的数据量大小,以及数据计算逻辑复杂度有关,整个测试过程花了我4天时间,但从阅读量来看呢,就还,Emm... 容我先桑心一会。


上次的测试案例中,有个最大的特点,就是把 MySQL 作为数据源的时候,无论数据量多大,Spark 默认都只有一个分区。


这也就导致了每次数据量增加时,需要分配给 Spark 单个 partition 的内存越来越大,而隔壁的 HDFS,不管数据量增加到多大,Spark 却都可以淡定的保证内存一直不变。


因为当 HDFS 作为数据源时,无论多大的数据量,都会默认把它都切成一个个 128m 的数据块,这样就导致进入 Spark 的单个partition 数据量都很小。


这个时候肯定有同学会问:既然是这样的话,那 Spark 在读取 MySQL 的时候,如果直接扩展默认的分区为多个,这样需要的内存不就少些了吗?


这个想法到底靠不靠谱呢?


这篇文章,我们就一起来验证一下。



0. 解题思路


如果要把 Spark 读取 MySQL 时默认的1个分区,扩展到多个分区,Spark 能提供的最简单解决办法,目前为止有两个。


一个是利用「coalesce」算子:根据源码的解释,它能够在修改默认分区数量的同时,还能尽量避免数据的 shuffle,是我们这次测试的首选;


另一个是利用「repartition」算子:是 Spark 用来修改默认数据分区的传统方法。


那么接下来,我们用这两种方法,来分别试一试。



1. 利用「coalesce」算子


因为目前为止,Spark 读取 MySQL,只能以 jdbc 的方式,所以这个「coalesce」算子的使用,就只能这样:


按理说,这样应该就可以把原本只有1个 partition 的数据,给拆成了 8 个 partition 了吧(当前 MySQL 表有1600w+条数据)。


但是,从我的实测结果来看,人家貌似根本不吃这一套,从分区打印信息来看


丝毫没有变化,一点面子都不给。


也就是说,想利用「coalesce」改变 MySQL 数据分区的想法,落空了。



2. 利用「repartition」算子


同样的,把上面的「coalesce」给换成「repartition」就可以了:


这一次,从程序打印的分区数量来看,确实是生效了。



但是,付出的代价却是「惨痛」的。


当前 MySQL 的 1600w+ 数据量,是由之前 2 个 1G 的数据文件写进去的,在不尝试改变分区数之前,Spark 对它进行读取、运算时,对 executor 内存的设置只需要 2G。


--executor-memory 2g

然鹅,当我这次把分区数调整为 8 之后,你猜怎么着?


这 2G 的 executor 内存完全不够用(跑一段时间后就 OOM),后面我就只能逐渐增加这个内存大小。


直到把这个 executor 的内存,从开始的 2G,给逐渐加到 14G(就这么以一次1G的幅度试出来的)


--executor-memory 14g

人家才能跑成功。


关键是,费这么大内存换来的,却是一个「负性价比」的结果。


先看不做分区调整前,Spark 进程跑完需要的时间:


可以看到,过程非常简单,就2个stage,总耗时也就不到1分钟。


再看把分区调整到 8个之后,Spark 进程跑完需要的时间:


居然超过了8分钟,最最关键的是,扩展到8个分区之后,那8个分区需要的总处理时间,也就花了1秒钟而已,就问你惊不惊喜,意不意外?



3. 没事别扩展分区


对于「coalesce」来说,Spark 还算聪明,至少对于我当前的这个案例来说,它应该是预估出了,扩展分区数量并不是一个聪明的选择(费内存不说,还耗时间),所以,人家干脆就不鸟你,直接不生效。


相比之下,「repartition」就听话多了,要它扩展它就扩展,只不过呢,扩展分区之后的代价,需要你自行承担。


从原来的1个分区,扩展到8个,哪些数据进入到哪个分区,肯定就要根据一定的规则来,这里就一定会对数据进行比如「取哈希」或者「排序」(俗称 shuffle),这两个手段中的任意一个只要一上,就注定了 Spark 需要额外的「时间」还有「空间」。


这也就解释了,为什么上面把原本 1个分区的 MySQL 数据,给扩展到 8个之后,内存翻了好几倍不说,时间也翻了好几倍。


正所谓,赔了夫人又折兵,不划算惨了。



最后


为什么想到出这样一期实测,因为在现实开发中,发现有很多同学特别喜欢用这一招。


他们朴素的认为,想要提高一个分布式计算的效率,一个有效的办法,就是去增加计算时的「并行度」,但殊不知,在提高了并行度的同时,其他隐形成本可能也跟着大大提高了,得不偿失。


根据我的经验,类似这种问题,最好的解决方案,应该是尽量去避开这种不支持「分布式数据抽取」的数据源,如果说非要去读,且数据量巨大的话。


那么可以用其他抽取工具,先把它们以流的方式,或者小批量多次的方式,抽取到比如分布式消息队列、或者分布式文件系统中。


然后,再用 Spark 来读取,这样,无论你的数据读取效率,还是处理效率,都会有个质的提升。


你觉得呢?




你可以添加我的私人微信,拉你入技术讨论群,跟一群热爱技术的小伙伴一起成长...


原文作者:安瑞哥是码农;如有侵权,请联系管理员删除。

请登录后发表评论 登录
全部评论

注册时间:2022-02-16

  • 博文量
    228
  • 访问量
    115417