Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/299.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 将cache()和count()应用于DataRicks中的Spark数据帧非常慢[pyspark]_Python_Apache Spark_Pyspark_Azure Databricks - Fatal编程技术网

Python 将cache()和count()应用于DataRicks中的Spark数据帧非常慢[pyspark]

Python 将cache()和count()应用于DataRicks中的Spark数据帧非常慢[pyspark],python,apache-spark,pyspark,azure-databricks,Python,Apache Spark,Pyspark,Azure Databricks,我在Databricks集群中有一个spark数据帧,有500万行。我想要的是缓存这个spark数据帧,然后应用.count(),以便下一个操作运行得非常快。我过去做过20000行,效果很好。然而,在我尝试这样做的过程中,我遇到了以下悖论: 数据帧创建 步骤1:从Azure Data Lake存储帐户读取800万行 read_avro_data=spark.read.format(“avro”).load(路径列表)#路径列表[0]='abfss://storage_container_name

我在Databricks集群中有一个spark数据帧,有500万行。我想要的是缓存这个spark数据帧,然后应用.count(),以便下一个操作运行得非常快。我过去做过20000行,效果很好。然而,在我尝试这样做的过程中,我遇到了以下悖论:

数据帧创建

步骤1:从Azure Data Lake存储帐户读取800万行

read_avro_data=spark.read.format(“avro”).load(路径列表)#路径列表[0]='abfss://storage_container_name@存储\帐户\名称.dfs.core.windows.net/文件夹\ 1/文件夹\ 2/0/2020/06/02/00/00/27.avro'
avro_decoded=读取avro_数据。带列('Body_decoded',sql_function.decode(读取avro_data.Body,charset=“UTF-8”)。选择(“Body_decoded”)
datalake_spark_dataframe=datalake_spark_dataframe.union(avro_解码.withColumn(“Body_解码”,sql_函数.from_json(“Body_解码”,schema))。选择(*['Body_解码。{}'。所选列中x的格式(x)))
datalake_spark_dataframe.printSchema()
“根
|--id:string(nullable=true)
|--BatteryPercentage:float(nullable=true)
|--传感器已连接:整数(可空=假)
|--TemperatureOutside:浮点(可空=真)
|--ReceivedOn:string(nullable=true)”
datalake_spark_dataframe.rdd.getNumPartitions()#635个分区
此数据帧有800万行。有800万行,我的应用程序运行得很好,但我想在大数据环境中对我的应用程序进行压力测试。因为800万行不是大数据。因此,我将800万行Spark数据帧复制了287倍~22亿行。要进行复制,我执行了以下操作:

步骤2:复制800万行数据帧

有了最后的22亿行数据帧,我对我的数据做了一个时间窗口GroupBy,最后得到了数百万行。我已经写了大约500万行的分组数据集在我的问题的顶部

步骤3:按6小时的时间窗口对22亿行数据帧进行分组&应用.cache()和.count()

显示.count()之前的Spark UI

在计数执行期间触发UI

当我对spark数据帧应用以下命令时,完成此任务需要3个多小时,最终失败

我想补充一点,在重新分区之前和之后,作业在时间执行上具有相同的行为。因此,我进行了重新分区,以防默认值使作业运行非常慢。因此,我一直在添加分区,以防作业执行得更快

%sql集spark.sql.shuffle.partitions=1000000
datalake\u spark\u dataframe\u下采样。重新分区(1000000)
datalake_spark_dataframe_downsampled.cache()
datalake_spark_dataframe_downsampled.count()数据
以下是spark作业的输出:

我得到的错误是:

我的群集资源:

正如你所看到的,这不是RAM或CPU核心的问题,因为我有很多。 为什么即使在我应用了重新分区之后,作业也只拆分到5个阶段?基于我的48个vCPU内核,如何分割作业以使.cache()和.count()命令运行得更快


每个作业执行提供的屏幕截图 在8000万行上执行(8m*10次迭代=8000万行)


我认为您使用了非常大的随机分区数
1000000
,这就是为什么完成这项工作需要更多时间的原因

我将按照下面的逻辑,根据数据大小计算洗牌分区。比如说

假设有500万个数据来自20GB左右的数据

洗牌阶段输入=20 GB

所以洗牌分区的总数是20000MB/200MB=100

假设集群中只有50个核,在这种情况下,无序分区值为50,或者集群中有200个核,在这种情况下,无序分区值为200

选择高值作为洗牌分区值将有大量的洗牌数据&因此任务将需要更多的时间来完成,有时可能会失败


spark.sql.shuffle.partitions=50//50或100是更好的选择。

我过去在迭代for循环时遇到过类似的问题,因为我的迭代是动态的,取决于输入组合

我通过在每次迭代中持久化数据解决了性能问题(您可以尝试在ADLS2中持久化,或者如果是在Prem上,则在HDFS/Hive表中持久化)。在下一次迭代中,再次从该位置读取,并再次覆盖同一位置。存在网络滞后和效率低下。尽管如此,它还是将执行时间缩短了10倍

可能的原因可能是Spark血统(我相信每次迭代它都会一次又一次地执行之前的所有迭代)。使用覆盖保存数据可以避免这种情况。 我尝试了cache()和其他选项,但没有帮助我

编辑#1 试试这样的

datalake_spark_dataframe_new=datalake_spark_dataframe
datalake_spark_dataframe.write.mode("overwrite").option("header", "true").format("parquet").save("abfss://<ADLS_PATH>")
for i in range(287):
  print(i)
  datalake_spark_dataframe_new=spark.read.parquet("abfss://<ADLS_PATH>")
  datalake_spark_dataframe_new.union(datalake_spark_dataframe).write.mode("overwrite").option("header", "true").format("parquet").save("abfss://<ADLS_PATH>")
  print("done on iteration: {0}".format(i))
datalake\u spark\u dataframe\u new=datalake\u spark\u dataframe
datalake\u spark\u dataframe.write.mode(“覆盖”).option(“header”、“true”).format(“parquet”).save(“abfss://”)
对于范围内的i(287):
印刷品(一)
datalake\u spark\u dataframe\u new=spark.read.parquet(“abfss://”)
datalake\u spark\u dataframe\u new.union(datalake\u spark\u dataframe)。write.mode(“覆盖”)。选项(“标题”,“真”)。格式(“拼花”)。保存(“abfss://”)
打印(“在迭代时完成:{0}”。格式(i))
编辑#2 这应该比以前的版本更有效

for i in range(287):
  print(i)
  datalake_spark_dataframe.write.mode("append").option("header", "true").format("parquet").save("abfss://<ADLS_PATH>")
  print("done on iteration: {0}".format(i))

datalake_spark_dataframe_new=spark.read.parquet("abfss://<ADLS_PATH>")
范围(287)内的i的
:
印刷品(一)
datalake\u spark\u dataframe.write.mode(“append”).option(“header”、“true”).format(“parquet”).save(“abfss://”)
打印(“在迭代时完成:{0}”。格式(i))
datalake\u spark\u dataframe\u new=spark.read.parquet(“abfss://”)

谢谢您的回答。实际上,在计算正确的分区数时,这是一个很好的观点。但是我想添加,我忘记了,在重新分区之前和之后,作业在时间执行上具有相同的行为。因此,如果默认值为
import pyspark.sql.functions as sql_function
from pyspark.sql.types import StructType, StructField, StringType, IntegerType, FloatType, BooleanType, DateType, DoubleType, ArrayType

datalake_spark_dataframe_downsampled=datalake_spark_dataframe_new.withColumn(timestamp_column, sql_function.to_timestamp(timestamp_column, "yyyy-MM-dd HH:mm"))
datalake_spark_dataframe_downsampled=datalake_spark_dataframe_downsampled.groupBy("id", sql_function.window("ReceivedOn","{0} minutes".format(time_interval)))\
                                                                         .agg(
                                                                              sql_function.mean("BatteryPercentage").alias("BatteryPercentage"),
                                                                              sql_function.mean("SensorConnected").alias("OuterSensorConnected"),
                                                                              sql_function.mean("TemperatureOutside").alias("averageTemperatureOutside"))

columns_to_drop=['window']
datalake_spark_dataframe_downsampled=datalake_spark_dataframe_downsampled.drop(*columns_to_drop)

# From 2.2 billion rows down to 5 million rows after the GroupBy...
datalake_spark_dataframe_downsampled.repartition(100)
datalake_spark_dataframe_downsampled.cache()
datalake_spark_dataframe_downsampled.count() # job execution takes for ever

datalake_spark_dataframe_downsampled.rdd.getNumPartitions() #100 after re-partition
datalake_spark_dataframe_new=datalake_spark_dataframe
datalake_spark_dataframe.write.mode("overwrite").option("header", "true").format("parquet").save("abfss://<ADLS_PATH>")
for i in range(287):
  print(i)
  datalake_spark_dataframe_new=spark.read.parquet("abfss://<ADLS_PATH>")
  datalake_spark_dataframe_new.union(datalake_spark_dataframe).write.mode("overwrite").option("header", "true").format("parquet").save("abfss://<ADLS_PATH>")
  print("done on iteration: {0}".format(i))
for i in range(287):
  print(i)
  datalake_spark_dataframe.write.mode("append").option("header", "true").format("parquet").save("abfss://<ADLS_PATH>")
  print("done on iteration: {0}".format(i))

datalake_spark_dataframe_new=spark.read.parquet("abfss://<ADLS_PATH>")