Hadoop中的Python框架的使用指南

发表于 5年以前  | 总阅读数:365 次

最近,我加入了Cloudera,在这之前,我在计算生物学/基因组学上已经工作了差不多10年。我的分析工作主要是利用Python语言和它很棒的科学计算栈来进行的。但Apache Hadoop的生态系统大部分都是用Java来实现的,也是为Java准备的,这让我很恼火。所以,我的头等大事变成了寻找一些Python可以用的Hadoop框架。

在这篇文章里,我会把我个人对这些框架的一些无关科学的看法写下来,这些框架包括:

  • Hadoop流
  • mrjob
  • dumbo
  • hadoopy
  • pydoop
  • 其它

最终,在我的看来,Hadoop的数据流(streaming)是最快也是最透明的选项,而且最适合于文本处理。mrjob最适合于在Amazon EMR上快速工作,但是会有显著的性能损失。dumbo 对于大多数复杂的工作都很方便(对象作为键名(key)),但是仍然比数据流(streaming)要慢。

请继续往下阅读,以了解实现细节,性能以及功能的比较。

一个有趣的问题

为了测试不同的框架,我们不会做"统计词数"的实验,转而去转化谷歌图书N-元数据。 N-元代表一个n个词构成的元组。这个n-元数据集提供了谷歌图书文集中以年份分组的所有1-,2-,3-,4-,5-元记录的统计数目。 在这个n-元数据集中的每行记录都由三个域构成:n-元,年份,观测次数。(您能够在http://books.google.com/ngrams取得数据)。

我们希望去汇总数据以观测统计任何一对相互临近的词组合所出现的次数,并以年份分组。实验结果将使我们能够判断出是否有词组合在某一年中比正常情况出现的更为频繁。如果统计时,有两个词在四个词的距离内出现过,那么我们定义两个词是"临近"的。 或等价地,如果两个词在2-,3-或者5-元记录中出现过,那么我们也定义它们是"临近"的。 一次,实验的最终产物会包含一个2-元记录,年份和统计次数。

有一个微妙的地方必须强调。n-元数据集中每个数据的值都是通过整个谷歌图书语料库来计算的。从原理上来说,给定一个5-元数据集,我可以通过简单地聚合正确的n-元来计算出4-元、3-元和2-元数据集。例如,当5-元数据集包含


    (the, cat, in, the, hat)    1999   20
    (the, cat, is, on, youtube)  1999   13
    (how, are, you, doing, today) 1986  5000

时,我们可以将它聚合为2-元数据集以得出如下记录


    (the, cat) 1999  33   // 也就是, 20 + 13

然而,实际应用中,只有在整个语料库中出现了40次以上的n元组才会被统计进来。所以,如果某个5元组达不到40次的阈值,那么Google也提供组成这个5元组的2元组数据,这其中有一些或许能够达到阈值。出于这个原因,我们用相邻词的二元数据,隔一个词的三元组,隔两个词的四元组,以此类推。换句话说,与给定二元组相比,三元组多的只是最外层的词。除了对可能的稀疏n元数据更敏感,只用n元组最外层的词还有助于避免重复计算。总的来说,我们将在2元、3元、4元和5元数据集上进行计算。

MapReduce的伪代码来实现这个解决方案类似这样:


    def map(record):
      (ngram, year, count) = unpack(record)
      // 确保word1为字典第一个字
      (word1, word2) = sorted(ngram[first], ngram[last])
      key = (word1, word2, year)
      emit(key, count)

    def reduce(key, values):
      emit(key, sum(values))

硬件

这些MapReduce组件在一个大约20GB的随机数据子集上执行。完整的数据集涵盖1500个文件;我们用这个脚本选取一个随机子集。文件名保持完整,这一点相当重要,因为文件名确定了数据块的n-元中n的值。

Hadoop集群包含5个使用CentOS 6.2 x64的虚拟节点,每个都有4个CPU,10GB RAM,100GB硬盘容量,并且运行CDH4。集群每次能够执行20个并行运算,每个组件能够执行10个减速器。

集群上运行的软件版本如下:


      Hadoop:2.0.0-cdh4.1.2
      Python:2.6.6
      mrjob:0.4-dev
      dumbo:0.21.36
      hadoopy:0.6.0
      pydoop:0.7(PyPI)库中包含最新版本
      java:1.6

实现

大多数Python框架都封装了Hadoop Streaming,还有一些封装了Hadoop Pipes,也有些是基于自己的实现。下面我会分享一些我使用各种Python工具来写Hadoop jobs的经验,并会附上一份性能和特点的比较。我比较感兴趣的特点是易于上手和运行,我不会去优化某个单独的软件的性能。

在处理每一个数据集的时候,都会有一些损坏的记录。对于每一条记录,我们要检查是否有错并识别错误的种类,包括缺少字段以及错误的N元大小。对于后一种情况,我们必须知道记录所在的文件名以便确定该有的N元大小。

所有代码可以从 GitHub 获得。

Hadoop Streaming

Hadoop Streaming 提供了使用其他可执行程序来作为Hadoop的mapper或者reduce的方式,包括标准Unix工具和Python脚本。这个程序必须使用规定的语义从标准输入读取数据,然后将结果输出到标准输出。直接使用Streaming 的一个缺点是当reduce的输入是按key分组的时候,仍然是一行行迭代的,必须由用户来辨识key与key之间的界限。

下面是mapper的代码:


    #! /usr/bin/env python

    import os
    import re
    import sys

    # determine value of n in the current block of ngrams by parsing the filename
    input_file = os.environ['map_input_file']
    expected_tokens = int(re.findall(r'([\d]+)gram', os.path.basename(input_file))[0])

    for line in sys.stdin:
      data = line.split('\t')

      # perform some error checking
      if len(data) < 3:
        continue

      # unpack data
      ngram = data[0].split()
      year = data[1]
      count = data[2]

      # more error checking
      if len(ngram) != expected_tokens:
        continue

      # build key and emit
      pair = sorted([ngram[0], ngram[expected_tokens - 1]])
      print >>sys.stdout, "%s\t%s\t%s\t%s" % (pair[0], pair[1], year, count)

下面是reducer:


    #! /usr/bin/env python

    import sys

    total = 0
    prev_key = False
    for line in sys.stdin:
      data = line.split('\t')
      curr_key = '\t'.join(data[:3])
      count = int(data[3])

      # found a boundary; emit current sum
      if prev_key and curr_key != prev_key:
        print >>sys.stdout, "%s\t%i" % (prev_key, total)
        prev_key = curr_key
        total = count
      # same key; accumulate sum
      else:
        prev_key = curr_key
        total += count

    # emit last key
    if prev_key:
      print >>sys.stdout, "%s\t%i" % (prev_key, total)

Hadoop流(Streaming)默认用一个tab字符分割健(key)和值(value)。因为我们也用tab字符分割了各个域(field),所以我们必须通过传递给Hadoop下面三个选项来告诉它我们数据的健(key)由前三个域构成。


    -jobconf stream.num.map.output.key.fields=3
    -jobconf stream.num.reduce.output.key.fields=3

要执行Hadoop任务命令


    hadoop jar /usr/lib/hadoop-0.20-mapreduce/contrib/streaming/hadoop-streaming-2.0.0-mr1-cdh4.1.2.jar \
        -input /ngrams \
        -output /output-streaming \
        -mapper mapper.py \
        -combiner reducer.py \
        -reducer reducer.py \
        -jobconf stream.num.map.output.key.fields=3 \
        -jobconf stream.num.reduce.output.key.fields=3 \
        -jobconf mapred.reduce.tasks=10 \
        -file mapper.py \
        -file reducer.py

注意,mapper.py和reducer.py在命令中出现了两次,第一次是告诉Hadoop要执行着两个文件,第二次是告诉Hadoop把这两个文件分发给集群的所有节点。

Hadoop Streaming 的底层机制很简单清晰。与此相反,Python以一种不透明的方式执行他们自己的序列化/反序列化,而这要消耗更多的资源。 而且,如果Hadoop软件已经存在,Streaming就能运行,而不需要再在上面配置其他的软件。更不用说还能传递Unix 命令或者Java类名称作 mappers/reducers了。

Streaming缺点是必须要手工操作。用户必须自己决定如何将对象转化为为成键值对(比如JSON 对象)。对于二进制数据的支持也不好。而且如上面说过的,必须在reducer手工监控key的边界,这很容易出错。

mrjob

mrjob是一个开放源码的Python框架,封装Hadoop的数据流,并积极开发Yelp的。由于Yelp的运作完全在亚马逊网络服务,mrjob的整合与EMR是令人难以置信的光滑和容易(使用 boto包)。

mrjob提供了一个Python的API与Hadoop的数据流,并允许用户使用任何对象作为键和映射器。默认情况下,这些对象被序列化为JSON对象的内部,但也有支持pickle的对象。有没有其他的二进制I / O格式的开箱即用,但有一个机制来实现自定义序列化。

值得注意的是,mrjob似乎发展的非常快,并有很好的文档。

所有的Python框架,看起来像伪代码实现:


    #! /usr/bin/env python

    import os
    import re

    from mrjob.job import MRJob
    from mrjob.protocol import RawProtocol, ReprProtocol

    class NgramNeighbors(MRJob):

      # mrjob allows you to specify input/intermediate/output serialization
      # default output protocol is JSON; here we set it to text
      OUTPUT_PROTOCOL = RawProtocol

      def mapper_init(self):
        # determine value of n in the current block of ngrams by parsing filename
        input_file = os.environ['map_input_file']
        self.expected_tokens = int(re.findall(r'([\d]+)gram', os.path.basename(input_file))[0])

      def mapper(self, key, line):
        data = line.split('\t')

        # error checking
        if len(data) < 3:
          return

        # unpack data
        ngram = data[0].split()
        year = data[1]
        count = int(data[2])

        # more error checking
        if len(ngram) != self.expected_tokens:
          return

        # generate key
        pair = sorted([ngram[0], ngram[self.expected_tokens - 1]])
        k = pair + [year]

        # note that the key is an object (a list in this case)
        # that mrjob will serialize as JSON text
        yield (k, count)

      def combiner(self, key, counts):
        # the combiner must be separate from the reducer because the input
        # and output must both be JSON
        yield (key, sum(counts))

      def reducer(self, key, counts):
        # the final output is encoded as text
        yield "%s\t%s\t%s" % tuple(key), str(sum(counts))

    if __name__ == '__main__':
      # sets up a runner, based on command line options
      NgramNeighbors.run()

mrjob只需要安装在客户机上,其中在作业的时候提交。下面是要运行的命令:


    export HADOOP_HOME="/usr/lib/hadoop-0.20-mapreduce"
    ./ngrams.py -r hadoop --hadoop-bin /usr/bin/hadoop --jobconf mapred.reduce.tasks=10 -o hdfs:///output-mrjob hdfs:///ngrams

编写MapReduce的工作是非常直观和简单的。然而,有一个重大的内部序列化计划所产生的成本。最有可能的二进制计划将需要实现的用户(例如,为了支持typedbytes)。也有一些内置的实用程序日志文件的解析。最后,mrjob允许用户写多步骤的MapReduce的工作流程,在那里从一个MapReduce作业的中间输出被自动用作输入到另一个MapReduce工作。

(注:其余的实现都非常相似,除了包具体的实现,他们都能被找到here.。)

dumbo

dumbo 是另外一个使用Hadoop流包装的框架。dumbo出现的较早,本应该被许多人使用,但由于缺少文档,造成开发困难。这也是不如mcjob的一点。

dumbo通过typedbytes执行序列化,能允许更简洁的数据传输,也可以更自然的通过指定JavaInputFormat读取SequenceFiles或者其他格式的文件,比如,dumbo也可以执行Python的egg和Java的JAR文件。

在我的印象中, 我必须要手动安装dumbo中的每一个节点, 它只有在typedbytes和dumbo以eggs形式创建的时候才能运行。 就像他会因为onMemoryErrors终止一样,他也会因为使用组合器停止。

运行dumbo任务的代码是:


    dumbo start ngrams.py \
        -hadoop /usr \
        -hadooplib /usr/lib/hadoop-0.20-mapreduce/contrib/streaming \
        -numreducetasks 10 \
        -input hdfs:///ngrams \
        -output hdfs:///output-dumbo \
        -outputformat text \
        -inputformat text

hadoopy

hadoopy 是另外一个兼容dumbo的Streaming封装。同样,它也使用typedbytes序列化数据,并直接把 typedbytes 数据写到HDFS。

它有一个很棒的调试机制, 在这种机制下它可以直接把消息写到标准输出而不会干扰Streaming过程。它和dumbo很相似,但文档要好得多。文档中还提供了与 Apache HBase整合的内容。

用hadoopy的时候有两种发发来启动jobs:

  • launch 需要每个节点都已经安装了Python/hadoopy ,但是在这之后的负载就小了。
  • launch_frozen 不要求节点上已经安装了Python,它会在运行的时候安装,但这会带来15秒左右的额外时间消耗(据说通过某些优化和缓存技巧能够缩短这个时间)。

必须在Python程序中启动hadoopy job,它没有内置的命令行工具。

我写了一个脚本通过launch_frozen的方式启动hadoopy


    python launch_hadoopy.py

用launch_frozen运行之后,我在每个节点上都安装了hadoopy然后用launch方法又运行了一遍,性能明显好得多。

pydoop

与其他框架相比,pydoop 封装了Hadoop的管道(Pipes),这是Hadoop的C++ API。 正因为此,该项目声称他们能够提供更加丰富的Hadoop和HDFS接口,以及一样好的性能。我没有验证这个。但是,有一个好处是可以用Python实现一个Partitioner,RecordReader以及RecordWriter。所有的输入输出都必须是字符串。

最重要的是,我不能成功的从PIP或者源代码构建pydoop。

其他

  • happy 是一个用Jython来写Hadoop job的框架,但是似乎已经挂了
  • Disco 成熟的,非Hadoop 的 MapReduce.实现,它的核心使用Erlang写的,提供了Python的API,它由诺基亚开发,不如Hadoop应用广泛。
  • octopy 是一个纯Python的MapReduce实现,它只有一个源文件,并不适于"真正的"计算。
  • Mortar是另一个Python选择,它不久前才发布,用户可以通过一个网页应用提交Apache Pig 或者 Python jobs 处理放置在 Amazon S3上的数据。
  • 有一些更高层次的Hadoop生态体系中的接口,像 Apache Hive和Pig。Pig 可以让用户用Python来写自定义的功能,是通过Jython来运行。 Hive 也有一个Python封装叫做hipy
  • (Added Jan. 7 2013) Luigi 是一个用于管理多步作业流程的Python框架。它与Apache Oozie 有一点相似,但是它内置封装了Hadoop Streaming(轻量级的封装)。Luigi有一个非常好的功能是能够在job出错的时候抛出Python代码的错误堆栈,而且它的命令行界面也非常棒。它的README文件内容很多,但是却缺少详尽的参考文档。Luigi 由Spotify 开发并在其内部广泛使用。

本地java

最后,我使用新的Hadoop Java API接口实施了MR任务,编译完成后,这样来运行它:


    hadoop jar /root/ngrams/native/target/NgramsComparison-0.0.1-SNAPSHOT.jar NgramsDriver hdfs:///ngrams hdfs:///output-native

关于计数器的特别说明

在我的MR jobs的最初实现里,我用计数器来跟踪监控不良记录。在Streaming里,需要把信息写到stderr。事实证明这会带来不容忽视的额外开销:Streaming job花的时间是原生java job的3.4倍。这个框架同样有此问题。

将用Java实现的MapReduce job作为性能基准。 Python框架的值是其相对于Java的性能指标的比率。

2015422114844368.png \(807×266\)

Java明显最快,,Streaming要多花一半时间,Python框架花的时间更多。从mrjob mapper的profile数据来看,它在序列化/反序列化上花费了大量时间。dumbo和hadoopy在这方面要好一点。如果用了combiner 的话dumbo 还可以更快。

特点比较

大多来自各自软件包中的文档以及代码库。

2015422114912983.png \(1190×483\)

结论

Streaming是最快的Python方案,这面面没有任何魔力。但是在用它来实现reduce逻辑的时候,以及有很多复杂对象的时候要特别小心。

所有的Python框架看起来都像是伪码,这非常棒。

mrjob更新快,成熟的易用,用它来组织多步MapReduce的工作流很容易,还可以方便地使用复杂对象。它还可以无缝使用EMR。但是它也是执行速度最慢的。

还有一些不是很流行的 Python 框架,他们的主要优势是内置了对于二进制格式的支持,但如果有必要话,这个完全可以由用户代码来自己实现。

就目前来看:

  • Hadoop Streaming是一般情况下的最佳选择,只要在使用reducer的时候多加小心,它还是很简单易用的。
  • 从计算开销方面考虑的话,选择mrjob,因为它与Amazon EMR结合最好。
  • 如果应用比较复杂,包含了复合键,要组合多步流程,dumbo 最合适。它比Streaming慢,但是比mrjob快。

如果你在实践中有自己的认识,或是发现本文有错误,请在回复里提出。

 相关推荐

刘强东夫妇:“移民美国”传言被驳斥

京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。

发布于:1年以前  |  808次阅读  |  详细内容 »

博主曝三大运营商,将集体采购百万台华为Mate60系列

日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。

发布于:1年以前  |  770次阅读  |  详细内容 »

ASML CEO警告:出口管制不是可行做法,不要“逼迫中国大陆创新”

据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。

发布于:1年以前  |  756次阅读  |  详细内容 »

抖音中长视频App青桃更名抖音精选,字节再发力对抗B站

今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。

发布于:1年以前  |  648次阅读  |  详细内容 »

威马CDO:中国每百户家庭仅17户有车

日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。

发布于:1年以前  |  589次阅读  |  详细内容 »

研究发现维生素 C 等抗氧化剂会刺激癌症生长和转移

近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。

发布于:1年以前  |  449次阅读  |  详细内容 »

苹果据称正引入3D打印技术,用以生产智能手表的钢质底盘

据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。

发布于:1年以前  |  446次阅读  |  详细内容 »

千万级抖音网红秀才账号被封禁

9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...

发布于:1年以前  |  445次阅读  |  详细内容 »

亚马逊股东起诉公司和贝索斯,称其在购买卫星发射服务时忽视了 SpaceX

9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。

发布于:1年以前  |  444次阅读  |  详细内容 »

苹果上线AppsbyApple网站,以推广自家应用程序

据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。

发布于:1年以前  |  442次阅读  |  详细内容 »

特斯拉美国降价引发投资者不满:“这是短期麻醉剂”

特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。

发布于:1年以前  |  441次阅读  |  详细内容 »

光刻机巨头阿斯麦:拿到许可,继续对华出口

据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。

发布于:1年以前  |  437次阅读  |  详细内容 »

马斯克与库克首次隔空合作:为苹果提供卫星服务

近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。

发布于:1年以前  |  430次阅读  |  详细内容 »

𝕏(推特)调整隐私政策,可拿用户发布的信息训练 AI 模型

据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。

发布于:1年以前  |  428次阅读  |  详细内容 »

荣耀CEO谈华为手机回归:替老同事们高兴,对行业也是好事

9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI操控无人机能力超越人类冠军

《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI生成的蘑菇科普书存在可致命错误

近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。

发布于:1年以前  |  420次阅读  |  详细内容 »

社交媒体平台𝕏计划收集用户生物识别数据与工作教育经历

社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”

发布于:1年以前  |  411次阅读  |  详细内容 »

国产扫地机器人热销欧洲,国产割草机器人抢占欧洲草坪

2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。

发布于:1年以前  |  406次阅读  |  详细内容 »

罗永浩吐槽iPhone15和14不会有区别,除了序列号变了

罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。

发布于:1年以前  |  398次阅读  |  详细内容 »
 相关文章
Android插件化方案 5年以前  |  237269次阅读
vscode超好用的代码书签插件Bookmarks 2年以前  |  8108次阅读
 目录