繁体   English   中英

处理大输入时会产生非常慢的性能

[英]Spark very slow performance when processing big input

我正在使用Spark(在Scala中)读取一个包含用户列表和他们共享的页面的文件,并且我想通过它们共享的页面找到与给定用户相距一定距离的所有用户。

该程序的性能非常差,并且经常出现GC overhead limit exceeded错误。

我在具有8 GB内存的Mac OSX上本地运行Spark。 程序使用spark-submit ,参数为--driver-memory 5g并通过设置spark.cores.max分配了8个内核。 输入集是1.15 GB的文件。

是否有人指出哪种操作效率很低?是否有更好的替代方法?

提前致谢。

该代码在这里简要描述。

每个用户条目都包含一个制表符后他/她共享的页面,并且每个条目由两个换行符分隔,如下所示:

John Doe    <tab>    Page 1
            <tab>    Page 2
            <tab>    Page 3

User 2      <tab>    ...

首先,我使用newAPIHadoopFile读取输入文件。

val hdpConf = new Configuration(sc.hadoopConfiguration)
hdpConf.set("textinputformat.record.delimiter", "\n\n")
val hadoopFile = sc.newAPIHadoopFile("user_pages.list", classOf[TextInputFormat], classOf[LongWritable], classOf[Text], hdpConf)

现在,我将其变成成对的(user, Array(pagesShared))就像这样

val pagesPerUser = hadoopFile.map {
    line =>
        val line_splitted = line._2.toString.split("\t");
        (line_splitted(0), line_splitted.drop(1).mkString.split("\n"))
}

然后,我为每个用户和页面组合(page, user)创建一个包含单个(k,v)对的RDD。

val pageAndUser = pagesPerUser.flatMap(line => line._2.map(page => (line._1, page)))
    .map(...)
    .filter(...)

map使用replaceAll过滤页面标题,然后filter删除所有包含某些包含引号的标题的条目,并使用replaceAll matches()检查标题是否满足更多条件的正则表达式。

然后,我创建直接链接到另一个用户(user, user)的每个用户对,然后将其转换为格式为(user, Array(user))的RDD (user, Array(user))通过共享同一页面包含所有直接连接的用户)。

val pageAndUsers = pageAndUser.groupByKey.mapValues(_.toArray)
    .map(line => line._2)
val commonUsers = pageAndUsers.flatMap(users => users.map(user => (user, users)))
    .reduceByKey(_ ++ _).cache()
    .map(users => (users._1, users._2.distinct))

然后可以使用该RDD来进一步确定用户之间的距离,但是我认为性能下降的主要原因在于这些部分之一。

Spark UI显示,在确定commonUsers时,该程序在reduceByKeymap步骤似乎执行缓慢。 我确定它运行缓慢的方式是与其他程序员的解决方案进行比较。 此外,我经常收到GC溢出/堆空间超出错误,这表明我的代码中发生了一些内存泄漏。

编辑 :经过更多调查后,我很确定问题出在reduceByKey(_++_)步骤。 我尝试使用groupByKey代替,但是该程序似乎对我失败,并且每次在该特定点崩溃。

执行reduceByKey并使用它来组合可能增长为不确定大小的数据是很危险的。 例如,看起来您正在链接某种程度上共享页面的用户。 但是,如果您的用户之一链接到所有其他用户,该怎么办。 然后,您尝试在reduceByKey中构造的数组将变得非常大。 这是您的内存和GC问题的根源。

我希望如果您在此阶段运行时查看Spark UI,将会看到一些任务挂起。 这些将是您一个用户链接到多个用户的地方。 (也许所有这些都将挂起,在这种情况下,您的所有用户都将链接到您的所有用户)。

我会在您的reduceByKey(“ pageAndUsers” RDD)之前保存您的数据,然后查询该数据以查看发生了什么。

也许如果您的用户总数“很少”,则可以使用集合而不是数组,因为这会自动使用户对中的货币对价值随行而行,因此可能不会太大(取决于在您的数据上)。

但是,您需要查看数据以了解问题。 要使用刚刚提到的设置逻辑,这里有一些示例代码(不完全快速):

val pageAndUsers = pageAndUser.groupByKey.mapValues(_.toSet)
    .map(line => line._2)
val commonUsers = pageAndUsers.flatMap(users => users.map(user => (user, users)))
    .reduceByKey(_ ++ _).cache()

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM