我在S3上存储了100k + CSV文件(平均大小在2MB至10MB之间)。
在将所有CSV一起编译为一个DataFrame
之前,我需要分别读取每个文件,以便为CSV分配一个ID。我有一个脚本,可以很好地工作并且可以很好地分发多达2k个文件。在读取并处理了大约2k个文件后,作业速度变慢,执行程序不起作用(CPU使用率降低到5%)。原因可能是什么?我认为这不是分区问题,因为文件不是那么大。它与在Python中启动数千个进程有关吗?
是否最好在CSV中写一个额外的列(以便作业可以异步运行),然后一次读取所有CSV?批量重写脚本是最好的解决方案吗?
这是脚本。
files = list(bucketObj.objects.filter(Prefix=subfolder))
p = ThreadPool(numNodes)
logDFs = p.map(lambda x: process(bucket, columns, x) , files)
df = unionAll(*logDFs)
def process(bucket, nameMap, item):
logId = item['id']
key = item['file']
try:
logger.info(key + ' ---- Start\n')
fLog = spark.read.option("header", "true").option(
"inferSchema", "true").csv(buildS3Path(bucket) + key)
fLog = assignID(fLog)
return fLog
except Exception as e:
logger.info(key + ' ---- ERROR ---- ')
更新#1 我重新编写了代码,以并行读取每个CSV,并在ID后面附加一列并保存。然后,一个单独的脚本会批量读取现在格式正确的文件。这也有同样的问题-前1500个文件运行速度很快,然后变慢。该项目正在具有10名工作人员的AWS EMR上运行。
更新#2 我已经对该脚本进行了重新设计,使其可以批量处理500个。这种脚本运行得更好,但是在稳定状态下,工作人员仅使用10%的CPU(开始时他们最多使用60%)。
for i in range(0,numBatches):
p = ThreadPool(numNodes)
if (i == numBatches):
fileSlice = fileErrList[i*batchSize:]
else:
fileSlice = fileErrList[i*batchSize:((i+1)*batchSize)]
logger.info('\n\n\n --------------- \n\n\n')
logger.info('Starting Batch : ' + str(i))
logger.info('\n\n\n --------------- \n\n\n')
p.map(lambda x: addIdCsv(x, bucket, logger), fileSlice)
p.terminate()
答案 0 :(得分:0)
我有一个可行的解决方案,并且还学会了避免的事情:
spark.read.parquet('filepath')
确保将火花执行器配置调整为产生的线程数。为了最大化工作者cpu,让每个执行者都使用一个核心。在我的集群上,我有10个工人,每个工人都有4个核心和4GB的内存。每个执行程序都需要分配内存开销(最少384MB)。我在$SPARK_HOME/conf/spark-defaults.conf
的EMR主节点上添加了以下配置。
spark.executor.memory 500M
spark.executor.memoryOverhead 500M
spark.executor.cores 1
然后我生成40个线程:p = ThreadPool(40)