【问题标题】:Creating dictionary from large Pyspark dataframe showing OutOfMemoryError: Java heap space从显示 OutOfMemoryError: Java heap space 的大型 Pyspark 数据帧创建字典
【发布时间】:2020-11-16 10:57:40
【问题描述】:

我已经看到并尝试了许多关于此问题的 existing StackOverflow 帖子,但没有一个有效。我猜我的 JAVA 堆空间没有我的大型数据集预期的那么大,我的数据集包含 650 万行。我的 Linux 实例包含 4 核的 64GB 内存。根据suggestion,我需要修复我的代码,但我认为从 pyspark 数据帧制作字典的成本应该不会很高。如果有其他方法可以计算,请告诉我。

我只想从我的 pyspark 数据框制作一个 python 字典,这是我的 pyspark 数据框的内容,

property_sql_df.show() 显示,

+--------------+------------+--------------------+--------------------+
|            id|country_code|       name|          hash_of_cc_pn_li|
+--------------+------------+--------------------+--------------------+
|  BOND-9129450|          US|Scotron Home w/Ga...|90cb0946cf4139e12...|
|  BOND-1742850|          US|Sited in the Mead...|d5c301f00e9966483...|
|  BOND-3211356|          US|NEW LISTING - Com...|811fa26e240d726ec...|
|  BOND-7630290|          US|EC277- 9 Bedroom ...|d5c301f00e9966483...|
|  BOND-7175508|          US|East Hampton Retr...|90cb0946cf4139e12...|
+--------------+------------+--------------------+--------------------+

我想要的是用 hash_of_cc_pn_li 作为 key 和 id 作为 列表 值的字典。

预期输出

{
  "90cb0946cf4139e12": ["BOND-9129450", "BOND-7175508"]
  "d5c301f00e9966483": ["BOND-1742850","BOND-7630290"]
}

到目前为止我所尝试的,

方式 1: 导致 java.lang.OutOfMemoryError: Java heap space

%%time
duplicate_property_list = {}
for ind in property_sql_df.collect(): 
     hashed_value = ind.hash_of_cc_pn_li
     property_id = ind.id
     if hashed_value in duplicate_property_list:
         duplicate_property_list[hashed_value].append(property_id) 
     else:
         duplicate_property_list[hashed_value] = [property_id] 

方式 2: 由于缺少 pyspark 上的原生 OFFSET 而无法正常工作

%%time
i = 0
limit = 1000000
for offset in range(0, total_record,limit):
    i = i + 1
    if i != 1:
        offset = offset + 1
        
    duplicate_property_list = {}
    duplicate_properties = {}
    
    # Preparing dataframe
    url = '''select id, hash_of_cc_pn_li from properties_df LIMIT {} OFFSET {}'''.format(limit,offset)  
    properties_sql_df = spark.sql(url)
    
    # Grouping dataset
    rows = properties_sql_df.groupBy("hash_of_cc_pn_li").agg(F.collect_set("id").alias("ids")).collect()
    duplicate_property_list = { row.hash_of_cc_pn_li: row.ids for row in rows }
    
    # Filter a dictionary to keep elements only where duplicate cound
    duplicate_properties = filterTheDict(duplicate_property_list, lambda elem : len(elem[1]) >=2)
    
    # Writing to file
    with open('duplicate_detected/duplicate_property_list_all_'+str(i)+'.json', 'w') as fp:
        json.dump(duplicate_property_list, fp)

我现在在控制台上得到了什么:

java.lang.OutOfMemoryError: Java 堆空间

并在 Jupyter 笔记本输出

上显示此错误
ERROR:py4j.java_gateway:An error occurred while trying to connect to the Java server (127.0.0.1:33097)

这是我在这里提出的后续问题: Creating dictionary from Pyspark dataframe showing OutOfMemoryError: Java heap space

【问题讨论】:

  • 这是一个 XY 问题。您似乎确信您的设计(使用收集的数据在驱动程序上生成一个巨大的字典)应该可以工作。这只是我们不知道的解决方案的中间步骤(除了您的解决方案显然不可扩展的事实之外)。您需要添加更多信息:1.您打算用 dict 做什么(不能在工人身上完成)2.您的 Spark 内存设置是什么(以及在 OOM 之前实际使用了多少 64gb发生) 3. 65m 行需要多少内存 4. 之前/到 property_sql_df 的任何转换
  • @anky 不,先生,我试过了,但没有运气同样的内存错误

标签: java python pandas apache-spark pyspark


【解决方案1】:

为什么不在 Executor 中保留尽可能多的数据和处理,而不是收集到 Driver?如果我理解正确,您可以使用pyspark 转换和聚合并直接保存到 JSON,从而利用执行程序,然后将该 JSON 文件(可能已分区)作为字典加载回 Python。诚然,您引入了 IO 开销,但这应该可以让您绕过 OOM 堆空间错误。一步一步:

import pyspark.sql.functions as f


spark = SparkSession.builder.getOrCreate()
data = [
    ("BOND-9129450", "90cb"),
    ("BOND-1742850", "d5c3"),
    ("BOND-3211356", "811f"),
    ("BOND-7630290", "d5c3"),
    ("BOND-7175508", "90cb"),
]
df = spark.createDataFrame(data, ["id", "hash_of_cc_pn_li"])

df.groupBy(
    f.col("hash_of_cc_pn_li"),
).agg(
    f.collect_set("id").alias("id")  # use f.collect_list() here if you're not interested in deduplication of BOND-XXXXX values
).write.json("./test.json")

检查输出路径:

ls -l ./test.json

-rw-r--r-- 1 jovyan users  0 Jul 27 08:29 part-00000-1fb900a1-c624-4379-a652-8e5b9dee8651-c000.json
-rw-r--r-- 1 jovyan users 50 Jul 27 08:29 part-00039-1fb900a1-c624-4379-a652-8e5b9dee8651-c000.json
-rw-r--r-- 1 jovyan users 65 Jul 27 08:29 part-00043-1fb900a1-c624-4379-a652-8e5b9dee8651-c000.json
-rw-r--r-- 1 jovyan users 65 Jul 27 08:29 part-00159-1fb900a1-c624-4379-a652-8e5b9dee8651-c000.json
-rw-r--r-- 1 jovyan users  0 Jul 27 08:29 _SUCCESS
_SUCCESS

dict: 的形式加载到 Python:

import json
from glob import glob

data = []
for file_name in glob('./test.json/*.json'):
    with open(file_name) as f:
        try:
            data.append(json.load(f))
        except json.JSONDecodeError:  # there is definitely a better way - this is here because some partitions might be empty
            pass

终于

{item['hash_of_cc_pn_li']:item['id'] for item in data}

{'d5c3': ['BOND-7630290', 'BOND-1742850'],
 '811f': ['BOND-3211356'],
 '90cb': ['BOND-9129450', 'BOND-7175508']}

我希望这会有所帮助!谢谢你的好问题!

【讨论】:

  • 它终于可以工作了,我只需要修改Loading to Python as dict:部分,谢谢百万先生。
猜你喜欢
  • 2020-11-16
  • 2011-06-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-22
相关资源
最近更新 更多