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库