elasticsearch-hadoop,Apache Spark,Pyspark,elasticsearch Hadoop" /> elasticsearch-hadoop,Apache Spark,Pyspark,elasticsearch Hadoop" />

Apache spark 用于rdd.saveAsNewApiHadoop文件和解决方法的Spark驱动程序内存

Apache spark 用于rdd.saveAsNewApiHadoop文件和解决方法的Spark驱动程序内存,apache-spark,pyspark,elasticsearch-hadoop,Apache Spark,Pyspark,elasticsearch Hadoop,我对一个特殊的spark方法,saveAsNewAPIHadoopFile有问题。上下文是我正在使用pyspark,将1k、10k、50k、500k、1m记录的RDD索引到ElasticSearch(ES)中 由于各种原因,Spark上下文在2gb驱动程序和单个2gb执行器的情况下动力不足 直到500k左右,我才遇到java堆大小的问题。将spark.driver.memory增加到大约4gb,我就能够索引更多。然而,这项工作的时间是有限制的,我们希望索引超过500k、1m、5m、20m的记录

我对一个特殊的spark方法,
saveAsNewAPIHadoopFile
有问题。上下文是我正在使用pyspark,将1k、10k、50k、500k、1m记录的RDD索引到ElasticSearch(ES)中

由于各种原因,Spark上下文在2gb驱动程序和单个2gb执行器的情况下动力不足

直到500k左右,我才遇到java堆大小的问题。将
spark.driver.memory
增加到大约4gb,我就能够索引更多。然而,这项工作的时间是有限制的,我们希望索引超过500k、1m、5m、20m的记录

由于各种原因,也不得不使用pyspark。瓶颈和断点似乎是一个火花阶段,名为
take at SerDeUtil.scala:233
,即无论RDD有多少个分区,它都会下降到一个,我假设是收集分区并准备索引的驱动程序

现在-我想知道,在这种限制条件下,是否有一种有效的方法可以继续使用如下方法:

to_index_rdd.saveAsNewAPIHadoopFile(
    path='-',
    outputFormatClass="org.elasticsearch.hadoop.mr.EsOutputFormat",
    keyClass="org.apache.hadoop.io.NullWritable",
    valueClass="org.elasticsearch.hadoop.mr.LinkedMapWritable",
    conf={
        "es.resource":"%s/record" % index_name,
        "es.nodes":"192.168.45.10:9200",
        "es.mapping.exclude":"temp_id",
        "es.mapping.id":"temp_id",
    }
)
为了寻求一个好的解决办法,我不妨晾一些脏衣服。我有一个非常低效的解决方法,它使用
zipWithIndex
将RDD分块,并将这些子集发送到上面的索引函数。看起来有点像这样:

def index_chunks_to_es(spark=None, job=None, kwargs=None, rdd=None, chunk_size_limit=10000):

    # zip with index
    zrdd = rdd.zipWithIndex()

    # get count
    job.update_record_count(save=False)
    count = job.record_count

    # determine number of chunks
    steps = count / chunk_size_limit
    if steps % 1 != 0:
            steps = int(steps) + 1

    # evenly distribute chunks, while not exceeding chunk_limit
    dist_chunk_size = int(count / steps) + 1

    # loop through steps, appending subset to list for return
    for step in range(0, steps):

        # determine bounds
        lower_bound = step * dist_chunk_size
        upper_bound = (step + 1) * dist_chunk_size
        print(lower_bound, upper_bound)

        # select subset
        rdd_subset = zrdd.filter(lambda x: x[1] >= lower_bound and x[1] < upper_bound).map(lambda x: x[0])

        # index to ElasticSearch
        ESIndex.index_job_to_es_spark(
            spark,
            job=job,
            records_df=rdd_subset.toDF(),
            index_mapper=kwargs['index_mapper']
        )
更新#3 下面是SerDeUtil.scala:233上运行的
take的DAG可视化:

对于更小的作业(大约1k行),saveAsNewAPIHadoopFile的DAG是一个DAG,因为500k行尝试从未实际触发,因为上面的
SerDeUtil
阶段似乎触发了更大RDD的java堆大小问题:


我仍然有点困惑,为什么这可以解决问题,但它确实解决了。当使用
spark.jdbc.read
从MySQL读取行时,通过传递边界,生成的RDD似乎以这样一种方式进行分区,
saveAsNewAPIHadoopFile
对于大型RDD是成功的

为DB行提供Django模型,以便获得第一列和最后一列ID:

records = records.order_by('id')
start_id = records.first().id
end_id = records.last().id
然后,将它们传递给
spark.read.jdbc

sqldf = spark.read.jdbc(
    settings.COMBINE_DATABASE['jdbc_url'],
    'core_record',
    properties=settings.COMBINE_DATABASE,
    column='id',
    lowerBound=bounds['lowerBound'],
    upperBound=bounds['upperBound'],
    numPartitions=settings.SPARK_REPARTITION
)
RDD的调试字符串显示原始RDD现在有
10
分区:

(32) PythonRDD[11] at RDD at PythonRDD.scala:48 []
 |   MapPartitionsRDD[10] at javaToPython at NativeMethodAccessorImpl.java:-2 []
 |   MapPartitionsRDD[9] at javaToPython at NativeMethodAccessorImpl.java:-2 []
 |   ShuffledRowRDD[8] at javaToPython at NativeMethodAccessorImpl.java:-2 []
 +-(10) MapPartitionsRDD[7] at javaToPython at NativeMethodAccessorImpl.java:-2 []
    |   MapPartitionsRDD[6] at javaToPython at NativeMethodAccessorImpl.java:-2 []
    |   JDBCRDD[5] at javaToPython at NativeMethodAccessorImpl.java:-2 []

我的理解是,您可以看到,在问题的调试字符串和上面的这个字符串中,都有一个手动/显式重新划分到
32
,我认为这足以减轻
saveAsNewAPIHadoopFile
调用的内存压力,但显然是数据帧(变成了RDD)从最初的
spark.jdbc.read
甚至在下游都很重要。

对ES进行索引是一个更大的应用程序的一个重要但很小的部分,它利用spark进行其他处理。在小型计算机上开发以实现高效的内存管理,但根据应用程序工作的数据集,部署将有更大的计算机(更大的驱动程序内存、更多的执行器等)。我将RDD的调试字符串作为问题的更新。
(32) PythonRDD[11] at RDD at PythonRDD.scala:48 []
 |   MapPartitionsRDD[10] at javaToPython at NativeMethodAccessorImpl.java:-2 []
 |   MapPartitionsRDD[9] at javaToPython at NativeMethodAccessorImpl.java:-2 []
 |   ShuffledRowRDD[8] at javaToPython at NativeMethodAccessorImpl.java:-2 []
 +-(10) MapPartitionsRDD[7] at javaToPython at NativeMethodAccessorImpl.java:-2 []
    |   MapPartitionsRDD[6] at javaToPython at NativeMethodAccessorImpl.java:-2 []
    |   JDBCRDD[5] at javaToPython at NativeMethodAccessorImpl.java:-2 []