Chris Smith 回答了这个问题,并说我可以将其发布给 SO。他的回答:
因此,输入数据的大小本身并不是 EMR 的限制。还有很多其他因素。
也就是说,吸入 10TB 的数据是一项令人头疼的任务。仅仅读取这么多数据是非常残酷的,然后你就可以进行分桶/排序。
第一个问题是:制约因素是什么?您是否看到网络带宽已用尽?您是否看到 CPU 已满?磁盘 I/O 还是 iops?这些在数据节点上看起来如何?JobTracker 和 NameNodes 怎么样(在集群的其余部分都很好的情况下,将它们最大化并不罕见)?如果以上都不是,则可能是 Hadoop 资源已被用尽,需要进行不同的配置。
由于您没有提到争论的任何特定方面超出了它所处的阶段,这让我怀疑您对下面发生的事情没有太多的衡量标准。通常,您需要多次“测量,然后调整”,然后才能正确调整大工作。
作为一般经验法则,长时间处于“减少/复制”阶段是“你做错了”的一个非常强烈的指标。通常问题是您在排序/溢出/合并过程中受到束缚,节点以某种方式最大化磁盘 IO。Hadoop 有许多调整参数,对于具有大量映射器和缩减器的作业开始变得古怪,特别是如果两者之间存在很大的不平衡。同样,Karmasphere 和类似工具可以在这里为您提供很多帮助。需要调整的典型事情(我可能有些名字是错误的):
记录。特别是,像 dfs.namenode.logging.level 这样的东西对于在工作之前进行调整可能很重要。使用详细的日志记录完全有可能杀死自己。虽然自相矛盾,但它也可能是你的救赎,所以......
映射输出大小通常是“减少/复制”问题的关键因素。如果可能的话,考虑减少地图输出大小的方法。真的_应该比地图输入大小小得多。去掉 reduce 阶段不需要的任何数据。考虑使用紧凑的二进制序列化格式(Java 序列化会破坏您的性能),例如协议缓冲区或节俭(整数数据的大赢家)。考虑您的字符串在多大程度上可以用 ID/枚举表示。您可以使用组合器来减少必须通过网络发送的数据量吗?如果您有空闲的 CPU,请使用压缩(从 lzo 或 snappy 开始,但如果您还有更多 CPU 需要刻录,请考虑使用 gzip 甚至更强大的东西)。如果您仍然在地图任务日志中看到合并步骤需要很长时间,则需要进行一些调整:
io.sort.factor:可能应该更高。根据您的工作,您甚至可能会因拥有过多的映射器而受苦。io.sort.mb:与 io.sort.factor 密切相关,但又不同。如果您开始在节点上看到大量磁盘 i/o 压力,我会加快速度。这会占用内存,因此此参数涉及到真正的权衡。
mapred.job.reuse.jvm.num.tasks:仅当您的任务变得非常小时,但如果他们这样做,这是值得推高 mapred.reduce.parallel.copies:如果您不受 CPU 限制,您可能想要提高这个数字。您可能最终需要调整其他数字以平衡事情。
io.sort.record.percent:由于工作规模,这个是最不可能完全偏离标准的。通常,如果这是错误的,那是因为您的记录非常大或非常小。您想要达到的黄金比例是“16/(16 + 每条记录的字节数)”。
很难强调早期溢出对节点性能的影响。如果溢出,则意味着数据将被写出,然后再次读取,然后再次写出。在每个节点上。因此,如果您弄错了,添加更多节点也无济于事(实际上会使情况变得更糟)。您想查看一项作业溢出了多少记录与输出了多少地图记录。理想情况下,这些数字将相同。现在,如果你必须溢出,你就必须溢出(尽管这通常表明你做错了),但是每条记录只溢出一次到磁盘的作业只会压垮其他作业。
在减速器方面可能存在类似的问题。查看合并阶段的计数器。理想情况下,您希望溢出记录为 0 或至少 <= reducer 输入记录的数量。如果它更高......这就是你遇到性能问题的原因(说真的,这绝对是残酷的)。注意各种reducer溢出设置:mapred.job.shuffle.input.buffer.percent、mapred.job.shuffle.merge.percent、mapred.inmem.merge.threshold、io.sort.factor。mapred.inmem.merge.threshold 通常会为大工作而烦恼。前两个也经常搞砸,但这更多地是作为工作性质的函数,而不是作为工作规模的函数。
dfs.namenode.handler.count:如果你在 HDFS 中生成很多小文件,你肯定想把它推高
dfs.mapred.job.tracker.handler.count:看看你有多少任务才能知道是否应该更高。如果您正在创建在数百个节点上运行的数千个小任务,您将不会满足于这是 10
dfs.datanode.handler.count:这个与parallel.copies标志齐头并进。这个总是给我带来麻烦,因为我的第一直觉是把它提高到非常高,然后我只是在其他地方制造原木堵塞。;-) 无论如何,如果你考虑到有多少个映射器与多少个reducer 对话,那么将其提高一点可能是有意义的。
tasktracker.http.threads:如果你被困在reduce-copy中,这个问题不太可能出现。无论如何,它更接近它应该在的位置。mapred.local.dir:这是我经常不得不在非 EMR 集群上调整的一个,用于具有大量地图输出的作业。你真的可以成为磁盘绑定和磁盘空间绑定,所以我发现将路径更改为逗号分隔的目录列表很有帮助,每个驱动器一个。当然,使用 EMR 没有任何意义,但仍然指出了如何才能真正快速耗尽磁盘空间。
mapred.local.dir.minspacestart:您可能没有意识到这一点,但您的地图输出可能空间不足。调整这个值以确保每个任务在开始工作之前在系统上有足够的剩余空间可以真正节省你的培根。
请记住,Hadoop 真的是为每个主轴 2 个内核的系统设计的(这是摩尔定律之前的几次迭代),所有输入和输出都保留在 HDFS 内(允许大量的输入和输出捷径),1GigE每 8 个核心的端口,并且交换结构中的瓶颈很少。EMR 没有给你这样的东西。亚马逊试图提供一些不错的默认值来调整它,但很难为每个人通用地解决这个问题。EMR 的一个优势是您倾向于在每个节点上获得大量 RAM,因此您应该花一些时间确保以最佳方式使用 RAM 以最小化磁盘 I/O。Hadoop 也非常适合映射器消耗大量原始数据但输出相对较少数据的工作。您在每项工作中产生的所有数据都会进行大规模的分布式排序,默认情况下,Hadoop 会尝试这样做,同时将大量 RAM 和磁盘空间留给您的任务。让您的数据已经分桶/排序确实可以将大量工作从减速器推到映射器中,从而避免大量开销。很有可能,这就是你的问题所在。