Pyspark 在进行数据转换时,如何减少迭代时间?
我有两个数据转换,它在迭代时运行得非常慢 我可以使用哪些一般策略来提高绩效 输入数据:Pyspark 在进行数据转换时,如何减少迭代时间?,pyspark,Pyspark,我有两个数据转换,它在迭代时运行得非常慢 我可以使用哪些一般策略来提高绩效 输入数据: +-----------+-------+ | key | val | +-----------+-------+ | a | 1 | | a | 2 | | b | 1 | | b | 2 | | b | 3 | +-----------+-------+ 我迭代的代码如
+-----------+-------+
| key | val |
+-----------+-------+
| a | 1 |
| a | 2 |
| b | 1 |
| b | 2 |
| b | 3 |
+-----------+-------+
我迭代的代码如下所示:
从pyspark.sql导入函数为F
Output=/my/function/Output
input_df=/my/function/input
定义我的计算函数输入函数:
按键计算值列与最大值的差值
关键字参数:
input_df pyspark.sql.DataFrame:输入数据帧
返回:
pyspark.sql.DataFrame
最大值=输入值\
.groupBykey\
.aggF.maxF.colval.aliasmax\u val
连接的_df=输入_df\
.joinmax_df,键
差分测向=连接测向\
.带ColumnDiff,F.colmax_val-F.colval
返回差分
我花了4次构建迭代来获得正确的max_df,4次加入正确的join_df,4次获得正确的diff_df
这代表了以下方面的全部工作:
pipeline_1:
transform_A:
work_1 : input -> max_df
(takes 4 iterations to get right): 4 * max_df
work_2: max_df -> joined_df
(takes 4 iterations to get right): 4 * joined_df + 4 * max_df
= 4 joined_df + 4 max_df
work_3: joined_df -> diff_df
(takes 4 iterations to get right): 4 * diff_df + 4 * joined_df + 4 * max_df
total work:
transform_A
= work_1 + work_2 + work_3
= 4 * max_df + (4 * joined_df + 4 * max_df) + (4 * diff_df + 4 * joined_df + 4 * max_df)
= 12 * max_df + 8 * joined_df + 4 * diff_df
输出数据:
+-----------+-------+--------+
| key | val | diff |
+-----------+-------+--------+
| a | 1 | 1 |
| a | 2 | 0 |
| b | 1 | 2 |
| b | 2 | 1 |
| b | 3 | 0 |
+-----------+-------+--------+
重构
对于实验/快速迭代,最好将代码重构为几个较小的步骤,而不是一个较大的步骤
这样,您首先计算上游单元,将数据写回Foundry,并在后续步骤中使用此预计算的数据。如果在不改变这些早期步骤逻辑的情况下继续重新计算,那么除了一次又一次地做额外的工作之外,你什么也不做
具体地说:
从pyspark.sql导入函数为F
输出=/my/function/output\u max
input_df=/my/function/input
定义我的计算函数输入函数:
按键计算最大值
关键字参数:
input_df pyspark.sql.DataFrame:输入数据帧
返回:
pyspark.sql.DataFrame
最大值=输入值\
.groupBykey\
.aggF.maxF.colval.aliasmax\u val
返回最大值
output=/my/function/output\u
input_df=/my/function/input
max_df=/my/function/output_max
定义我的计算功能最大值,输入值:
计算max和input的联合输出
关键字参数:
max_df pyspark.sql.DataFrame:输入数据帧
input_df pyspark.sql.DataFrame:输入数据帧
返回:
pyspark.sql.DataFrame
连接的_df=输入_df\
.joinmax_df,键
返回
输出=/my/function/Output\u diff
已联接的\ u df=/my/function/output\已联接
定义my_compute_functionjoined_df:
按键计算值列与最大值的差值
关键字参数:
加入了_dfpyspark.sql.DataFrame:输入数据帧
返回:
pyspark.sql.DataFrame
差分测向=连接测向\
.带ColumnDiff,F.colmax_val-F.colval
返回差分
相反,您执行的工作如下所示:
pipeline_2:
transform_A:
work_1: input -> max_df
(takes 4 iterations to get right): 4 * max_df
transform_B:
work_2: max_df -> joined_df
(takes 4 iterations to get right): 4 * joined_df
transform_C:
work:3: joined_df -> diff_df
(takes 4 iterations to get right): 4 * diff_df
total_work:
transform_A + transform_B + transform_C
= work_1 + work_2 + work_3
= 4 * max_df + 4 * joined_df + 4 * diff_df
如果假设max_-df、joined_-df和diff_-df的计算成本相同,那么管道1.total_-work=24*max_-df,而管道2.total_-work=12*max_-df,因此可以期望迭代速度提高2倍
缓存
对于任何“小”数据集,都应该缓存它们。这将为管道保留内存中的行,而不需要从写回的数据集中提取。”考虑到许多不同的因素,small有点武断,但是Spark很好地尝试缓存它,不管发生什么,如果它太大,就会警告你
在这种情况下,根据您的设置,您可以缓存max_df和joined_df的中间层,具体取决于您正在开发的步骤
函数调用
您应该尽可能地坚持使用原生PySpark方法,而不要直接在数据上使用Python方法,即在单个行上循环,执行UDF。PySpark方法调用用Scala编写的底层Spark方法,这些方法直接针对数据而不是Python运行时运行,如果您只是将Python用作与该系统交互的层,而不是作为与数据交互的系统,那么您将获得Spark本身的所有性能优势
在上面的示例中,只调用本机PySpark方法,因此此计算将非常快
缩减取样
如果您可以导出自己的大型输入数据集的精确样本,则可以将其用作转换的模拟输入,直到您完善了逻辑并希望根据完整集对其进行测试为止
在上述情况下,我们可以在执行任何之前的步骤之前,将输入_df降采样为单个键
在编写一行PySpark代码之前,我个人将样本和缓存数据集放在1M行以上,这样一来,我的周转时间非常快,而且我不会因为数据集太大而慢慢发现语法错误
全部
良好的开发管道如下所示:
执行特定物化的离散代码块,您希望稍后重用,但不需要反复重新计算
减小采样到“小”尺寸
缓存的“小”数据集用于非常快速的抓取
PySpark本机代码仅用于利用快速底层Spark库