什么是Spark的Task倾斜?
假设当我们提交资源的到yarn上的时候
- executor个数为6个
- 每个executor的cores为4个
- task的个数为6个
理想的情况是:每一个executor做一个task,那么6个executor都在工作,6个task同时执行,只要服务器配置一致,6个task的数据量一致的话,那么数据是很快就可以执行完毕的
我自己实际测试的情况如下:
从图上可以看到,我有6个executor,并且有6个task,然而这6个task都集中在两个executor上面执行。这不是我想象中的样子。这样的结果是什么,数据处理的负担就给了两个executor,其他的executor都在围观。就好像下面这个样子:
问题的探究
通过网络搜索,得知应该是spark的数据本地化的原因:
关于数据本地化的介绍spark的官网有描述
下面是我粘贴复制的:
数据位置
数据位置可能会对Spark作业的性能产生重大影响。如果数据和在其上运行的代码在一起,那么计算往往很快。但是如果代码
和数据是分开的,那么必须移动到另一个。通常,将序列化代码从一个地方运送到另一个地方比一块数据更快,因为代码大小比数据小得多。Spark围绕数据局部性的一般原则构建其调度。
数据位置是数据与处理它的代码的接近程度。根据数据的当前位置,有多个级别的位置。从最近到最远的顺序:
- PROCESS_LOCAL数据与正在运行的代码位于同一JVM中。这是最好的地方
- NODE_LOCAL数据在同一节点上。示例可能位于同一节点上的HDFS中,也可能位于同一节点上的另一个执行程序中。这比PROCESS_LOCAL因为数据必须在进程之间传输要慢一些
- NO_PREF 可以从任何地方快速访问数据,并且没有位置偏好
- RACK_LOCAL数据位于同一机架服务器上。数据位于同一机架上的不同服务器上,因此需要通过网络发送,通常通过单个交换机
- ANY 数据在网络上的其他位置,而不在同一个机架中
Spark更喜欢在最佳位置级别安排所有任务,但这并不总是可行的。在任何空闲执行程序上没有未处理数据的情况下,Spark会切换到较低的位置级别。有两个选项:a)等待忙碌的CPU释放以启动同一服务器上的数据任务,或b)立即在需要移动数据的更远的地方启动新任务。
Spark通常会做的是等待繁忙的CPU释放的希望。一旦超时到期,它就开始将数据从远处移动到空闲CPU。每个级别之间的回退等待超时可以单独配置,也可以在一个参数中一起配置; 有关详细信息,请参阅配置页面spark.locality上的 参数。如果您的任务很长并且看到不良位置,您应该增加这些设置,但默认情况下通常效果很好。
大概的意思就是:spark在给executor分配task的时候,会按照数据所在位置的级别,分配任务。位置级别的优先级排列顺序如下:
类型 | 描述 | 解释 |
---|---|---|
PROCESS_LOCAL | 进程本地化 | 数据与代码都在JVM里保存 |
NODE_LOCAL | 节点本地化 | 数据与代码在同一节点上 |
NO_PREF | 没有偏好 | 可以在任何地方快速访问数据 |
RACK_LOCAL | 机架本地化 | 数据在同一个机架的不同的服务器上 |
ANY | 任何地方 | 数据在网络上的其他位置,而不在同一个机架中 |
现在对于我之前提出来的问题有一个这样的解决方案:
对于前文提到的问题来说。6个executor,6个task,想要让每一个executor都要跑task的话,每一个executor的core设置为1就可以了,那么这样一来,每一个executor就只有一个core,最多只能执行一个task,所以,剩下的task就会移交到另外的5个executor上面执行了。
但是另外一个问题又出现了
集群的资源利用不上来啊,如果这么去设置的话。
那么就从task入手吧,将task的粒度分得稍微细一点,这样一来,刚开始的时候,比如:
6个executor 4个cores 我有24个task
那么每个executor都在执行task。24个task并行执行。所有的executor都在工作。这样速度就快了