Python 3.x DataFrame.write.parquet()只使用一个执行器,不可缩放

Python 3.x DataFrame.write.parquet()只使用一个执行器,不可缩放,python-3.x,apache-spark,pyspark,databricks,Python 3.x,Apache Spark,Pyspark,Databricks,我读过: (以及它所指的) 我试图通过交叉连接将种子数据帧与一些其他数据帧绑定,生成一个包含100亿行的测试数据集。我的问题是,流程的最后一步final_df.write.parquet()只使用一个工作者/执行者,不管有多少工作者/执行者 这显然不能产生数十亿美元的规模。这就是问题所在 例如,在每个具有4个核心的3节点集群中,final_df具有64个分区,但只有一个执行器写入一个拼花文件,其中包含来自该数据帧的所有记录。我还尝试了12个节点,这将生成包含1936个分区的数据帧。但同

我读过:

  • (以及它所指的)

我试图通过
交叉连接
将种子数据帧与一些其他数据帧绑定,生成一个包含100亿行的测试数据集。我的问题是,流程的最后一步
final_df.write.parquet()
只使用一个工作者/执行者,不管有多少工作者/执行者

这显然不能产生数十亿美元的规模。这就是问题所在

例如,在每个具有4个核心的3节点集群中,
final_df
具有64个分区,但只有一个执行器写入一个拼花文件,其中包含来自该数据帧的所有记录。我还尝试了12个节点,这将生成包含1936个分区的数据帧。但同样的问题

一些意见:

  • 我认为64个分区来自2
    crossJoin()
    。一个节点是主节点,留下2个执行器,每个执行器有4个处理器,因此它的结果是:
    2*2*4*4=64
  • 如果我取消对
    coalesce()
    行的注释,分区数将减少到8个。但只有一个执行器写入一个拼花文件,其中包含来自该数据帧的所有记录
  • 如果我
    repartition()
    (no
    coalesce()
    ),那么我会得到8个文件,所有的执行器都会被使用,写操作也会被完美地分配。但现在问题转移到重新划分步骤,这是由一个执行者完成的。最后还是同样的问题
输出
2020-12-05T00:27:51.933995 spark.version 3.0.1
2020-12-05T00:27:51.934079读取种子文件
2020-12-05T00:27:52.713461数组长度:377,要生成的记录数:2000002,种子测向计数():14
2020-12-05T00:27:52.852547最终版本
2020-12-05T00:27:52.852749书写拼花地板
2020-12-05T00:28:00.823663拼花地板。
2020-12-05T00:28:08.757957最终数据计数()1989806

合并
。。。同上。。。
2020-12-05T00:12:22.620791合并
2020-12-05T00:12:22.860093最终版本
2020-12-05T00:12:22.860249书写拼花地板
2020-12-05T00:12:31.280416拼花地板。
2020-12-05T00:12:39.204093最终数据计数():1989806

再分配


需要较长时间的舞台DAG可视化:


PS:忽略
NUM\u RECORDS\u TO\u GENERATE
值与实际生成的记录数之间的轻微不匹配。这可能是
sqrt
中的一个数学问题,我不在乎它是否会被关闭数百万。

所以我解决了它,但我仍然不知道为什么旧代码只使用一个执行器

在与其他数据帧交叉连接之前,我添加了一个新步骤来重新划分原始数据帧。之后,生成的数据帧使用所有执行器

导入操作系统,数学 导入pyspark.sql.F函数 从日期时间导入日期时间作为dt def日志(*args): 打印(dt.now().isoformat()++.join([str(s)表示参数中的s])) 日志('spark.version',str(spark.version)) 日志(“读取种子文件”) spark.conf.set(“fs.azure.account.key.myaccount.dfs.core.windows.net”,“我的密钥”) seed_df=spark.read.csv(“abfss://fs1@myaccount.dfs.core.windows.net/seed.csv”,header=True) #NUM_RECORDS_TO_GENERATE=10_000_000_000 要生成的记录数=2\u 000\u 000 NUM_RECORDS_TO_GENERATE=NUM_RECORDS_TO_GENERATE+(NUM_RECORDS_TO_GENERATE%seed_df.count()) array_len=int(math.sqrt(NUM_RECORDS_TO_GENERATE/seed_df.count())) 日志(“数组长度:%s,要生成的记录数:%s,种子测向计数():%s”%(数组长度,要生成的记录数,种子测向计数()) df1=spark.createDataFrame(数据=[[1]*数组长度]] df2=df1.带列('exploded',F.exploded(df1[''u 1'])。drop('u 1') #------新步骤--------------- #通过这个最后的函数,write.parquet()使用所有执行器并向上扩展 df2=df2.repartition(df2.rdd.getNumPartitions()) df3=df2.crossJoin(df2)#包含数组_len ^2=NUM_RECORDS_TO_GENERATE/seed_df.count()记录 newdf=df3。交叉连接(seed_df)包含要生成的NUM_记录 final_df=newdf.withColumn('uniq_row_id',F.单调地增加_id()).drop('exploded')#添加唯一的id列 #日志(“重新分区”) #final_df=final_df.repartition(int(final_df.rdd.getNumPartitions()/2)) #日志(“合并”) #final_df=final_df.coalesce(int(final_df.rdd.getNumPartitions()/2)) 日志(“final_df.rdd.getNumPartitions():”,final_df.rdd.getNumPartitions()) 原木(“书写拼花地板”) 最终拼花地板(“abfss://fs1@myaccount.dfs.core.windows.net/%s/parquet-%s“%(dt.now().isoformat(),NUM_RECORDS_TO_GENERATE)) 原木(‘拼花地板’) 日志('final_df.rdd.count():',final_df.rdd.count())
新步骤:


如果要删除新步骤:
所以我解决了它,但我仍然不知道为什么旧代码只使用一个执行器

在与其他数据帧交叉连接之前,我添加了一个新步骤来重新划分原始数据帧。之后,生成的数据帧使用所有执行器

导入操作系统,数学 导入pyspark.sql.F函数 从日期时间导入日期时间作为dt def日志(*args): 打印(dt.now().isoformat()++.join([str(s)表示参数中的s])) 日志('spark.version',str(spark.version)) 日志(“读取种子文件”) spark.conf.set(“fs.azure.account.key.myaccount.dfs.core.windows.net”,“我的密钥”) seed_df=spark.read.csv(“abfss://fs1@myaccount.dfs.core.windows.net/seed.csv”,header=True) #NUM_RECORDS_TO_GENERATE=10_000_000_000 要生成的记录数=2\u 000\u 000 NUM_RECORDS_TO_GENERATE=NUM_RECORDS_TO_GENERATE+(NUM_RECORDS_TO_GENERATE%seed_df.count()) array_len=int(math.sqrt(NUM_RECORDS_TO_GENERATE/seed_df.count())) 日志(“数组长度:%s,要生成的记录数:%s,种子测向计数():%s”%(数组长度,要生成的记录数,种子测向计数()) df1=spark.createDataFrame(数据=[[1]*数组长度]] df2=df1.带列('exploded',F.exploded(df1[''u 1'])。drop('u 1') #------新步骤--------------- #通过这个最后的函数,write.parquet()使用所有执行器并向上扩展 df2=df2.repartition(df2.rdd.getNumPartitions()) df3=df2.交叉连接(df2)
import os, math
import pyspark.sql.functions as F
from datetime import datetime as dt

def log(*args):
  print(dt.now().isoformat() + ' ' + ' '.join([str(s) for s in args]))

log('spark.version', str(spark.version))

log("reading seed file")
spark.conf.set("fs.azure.account.key.myaccount.dfs.core.windows.net", "my key")
seed_df = spark.read.csv("abfss://fs1@myaccount.dfs.core.windows.net/seed.csv", header=True)

# NUM_RECORDS_TO_GENERATE = 10_000_000_000
NUM_RECORDS_TO_GENERATE = 2_000_000
NUM_RECORDS_TO_GENERATE = NUM_RECORDS_TO_GENERATE + (NUM_RECORDS_TO_GENERATE % seed_df.count())
array_len = int(math.sqrt(NUM_RECORDS_TO_GENERATE / seed_df.count()))

log("array_len: %s, NUM_RECORDS_TO_GENERATE: %s, seed_df.count(): %s" % (array_len, NUM_RECORDS_TO_GENERATE, seed_df.count()))

df1 = spark.createDataFrame(data=[[ [1] * array_len ]])
df2 = df1.withColumn('exploded', F.explode(df1['_1'])).drop('_1')
df3 = df2.crossJoin(df2) # contains array_len ^ 2 = NUM_RECORDS_TO_GENERATE / seed_df.count() records
newdf = df3.crossJoin(seed_df) # contains NUM_RECORDS_TO_GENERATE
final_df = newdf.withColumn('uniq_row_id', F.monotonically_increasing_id()).drop('exploded') # add unique id column

# log("repartitioning")
# final_df = final_df.repartition(int(final_df.rdd.getNumPartitions() / 2))
# log("coalesceing")
# final_df = final_df.coalesce(int(final_df.rdd.getNumPartitions() / 2))

log("final_df.rdd.getNumPartitions(): ", final_df.rdd.getNumPartitions())
log('writing parquet')
final_df.write.parquet("abfss://fs1@myaccount.dfs.core.windows.net/%s/parquet-%s" % (dt.now().isoformat(), NUM_RECORDS_TO_GENERATE))
log('wrote parquet.')
log('final_df.rdd.count():', final_df.rdd.count())

... same as above ...
2020-12-05T00:23:40.155481 repartitioning
2020-12-05T00:23:44.702251 final_df.rdd.getNumPartitions():  8
2020-12-05T00:23:44.702421 writing parquet
2020-12-05T00:23:50.478841 wrote parquet.
2020-12-05T00:23:52.174997 final_df.rdd.count(): 1989806
2020-12-08T17:31:25.674825 spark.version 3.0.1
2020-12-08T17:31:25.674927 reading seed file
2020-12-08T17:31:32.770631 array_len: 377, NUM_RECORDS_TO_GENERATE: 2000002, seed_df.count(): 14
2020-12-08T17:31:33.940648 uniq_df.rdd.getNumPartitions():  16
2020-12-08T17:31:33.940848 writing parquet
2020-12-08T17:31:37.658914 wrote parquet.
2020-12-08T17:31:39.612749 uniq_df.rdd.count(): 1989806
2020-12-08T17:37:16.896377 spark.version 3.0.1
2020-12-08T17:37:16.896478 reading seed file
2020-12-08T17:37:18.303734 array_len: 377, NUM_RECORDS_TO_GENERATE: 2000002, seed_df.count(): 14
2020-12-08T17:37:18.817331 uniq_df.rdd.getNumPartitions():  256
2020-12-08T17:37:18.817558 writing parquet
2020-12-08T17:37:28.015959 wrote parquet.
2020-12-08T17:37:37.973600 uniq_df.rdd.count(): 1989806