我只是一般地同时学习pyspark和Spark,并且开始编写了以下代码,该代码生成了一个随机有向图的片段列表(可能有重复的行)。如果您在装有edge_count=10**9
的家用计算机上运行此代码,它将由于内存不足而崩溃(很久以后由于无法将df
放入内存而导致崩溃) 。不管batch_size
,崩溃发生在大约相同数量的边缘。
很明显,我没有完全理解persist
应该做的事情。之所以列出数据帧,是因为我找不到任何干净的方法来创建足够大的单个数据帧,因为它们是不可变的。
我知道可以通过增加堆大小来减轻这种情况,但是我对这里的理论问题更感兴趣。我也知道您可以将persist(<stuff>)
替换为checkpoint()
来解决此问题;这会强制将每个子图写入磁盘。
import gc
import numpy as np
import pandas as pd
import pyspark
def generate_digraph(edge_count=10**9, batch_size=9*10**4,
steps_to_python_gc=2.5*10**7, domain_size=10**8):
sc = pyspark.context.SparkContext.getOrCreate()
sc.setCheckpointDir("~/.transitive_closure")
spark = pyspark.sql.SparkSession(sc)
spark.conf.set("spark.sql.execution.arrow.enabled", "true")
batches_to_python_gc = steps_to_python_gc // batch_size
batch_count = int(np.ceil(edge_count/batch_size))
domain_size = 10**8
digraph=[]
for i in range(batch_count):
if i%batches_to_python_gc == 0:
print("Taking out the trash...")
gc.collect()
if i == batch_count-1 and edge_count % batch_size > 0:
batch_size = edge_count % batch_size
new_origins = np.random.randint(0,domain_size,dtype=np.int32,
size=(batch_size,1))
new_termini = np.random.randint(0,domain_size,dtype=np.int32,
size=(batch_size,1))
partial_digraph = pd.DataFrame(np.concatenate(
[new_origins, new_termini], 1), columns=("origin", "terminus")
)
digraph.append(spark.createDataFrame(partial_digraph).
persist(pyspark.StorageLevel.DISK_ONLY))
if i == batch_count-1:
gc.collect()
print("Concatenating...")
df=pd.concat([partial_digraph.toPandas() for partial_digraph in digraph])
sc.stop()
return df
当然,有许多分布式计算任务,这些任务涉及将大量已处理的数据写入磁盘。 Spark如何处理这些任务?如何创建驻留在磁盘上的Spark可用大对象?能够在内存中保留一些东西,并在分配其他内存时将其与其他任务并行地写入磁盘,这是特别好的,这是我本来以为我可以持久化的方式(并且我希望我仍然可以,一旦我了解了它是如何工作的。