排序--MapReduce .

本文详细解析了Hadoop集群如何实现1TB数据的高效排序,包括取样、分区过程及为何不采用HashPartitioner的原因。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

看了谷歌那篇论文后,对于这个排序的问题一直都有些疑惑,看了下面这篇博文后,有种豁然开朗的感觉。下面是这篇文章的内容。

 

1、1TB(或1分钟)排序的冠军 
作为分布式数据处理的框架,集群的数据处理能力究竟有多快?或许1TB排序可以作为衡量的标准之一。 

1TB排序,就是对1TB(1024GB,大约100亿行数据)的数据进行排序。2008年,Hadoop赢得1TB排序基准评估第一名,排序1TB数据耗时209秒。后来,1TB排序被1分钟排序所取代,1分钟排序指的是在一分钟内尽可能多的排序。2009年,在一个1406个节点组成的hadoop集群,在59秒里对500GB完成了排序;而在1460个节点的集群,排序1TB数据只花了62秒。 

这么惊人的数据处理能力,是不是让你印象深刻呢?呵呵 

下面我们来看看排序的过程吧。 


2、排序的过程 

1TB的数据?100亿条数据?都是什么样的数据呢?让我们来看几条: 

Java代码 
  1. .t^#\|v$2\         0AAAAAAAAAABBBBBBBBBBCCCCCCCCCCDDDDDDDDDDEEEEEEEEEEFFFFFFFFFFGGGGGGGGGGHHHHHHHH  
  2. 75@~?'WdUF         1IIIIIIIIIIJJJJJJJJJJKKKKKKKKKKLLLLLLLLLLMMMMMMMMMMNNNNNNNNNNOOOOOOOOOOPPPPPPPP  
  3. w[o||:N&H,         2QQQQQQQQQQRRRRRRRRRRSSSSSSSSSSTTTTTTTTTTUUUUUUUUUUVVVVVVVVVVWWWWWWWWWWXXXXXXXX  
  4. ^Eu)<n#kdP         3YYYYYYYYYYZZZZZZZZZZAAAAAAAAAABBBBBBBBBBCCCCCCCCCCDDDDDDDDDDEEEEEEEEEEFFFFFFFF  
  5. +l-$$OE/ZH         4GGGGGGGGGGHHHHHHHHHHIIIIIIIIIIJJJJJJJJJJKKKKKKKKKKLLLLLLLLLLMMMMMMMMMMNNNNNNNN  
  6. LsS8)|.ZLD         5OOOOOOOOOOPPPPPPPPPPQQQQQQQQQQRRRRRRRRRRSSSSSSSSSSTTTTTTTTTTUUUUUUUUUUVVVVVVVV  
  7. le5awB.$sm         6WWWWWWWWWWXXXXXXXXXXYYYYYYYYYYZZZZZZZZZZAAAAAAAAAABBBBBBBBBBCCCCCCCCCCDDDDDDDD  
  8. q__[fwhKFg         7EEEEEEEEEEFFFFFFFFFFGGGGGGGGGGHHHHHHHHHHIIIIIIIIIIJJJJJJJJJJKKKKKKKKKKLLLLLLLL  
  9. ;L+!2rT~hd         8MMMMMMMMMMNNNNNNNNNNOOOOOOOOOOPPPPPPPPPPQQQQQQQQQQRRRRRRRRRRSSSSSSSSSSTTTTTTTT  
  10. M^*dDE;6^<         9UUUUUUUUUUVVVVVVVVVVWWWWWWWWWWXXXXXXXXXXYYYYYYYYYYZZZZZZZZZZAAAAAAAAAABBBBBBBB  



描述一下:每一行,是一条数据。每一条,由2部分组成,前面是一个由10个随即字符组成的key,后面是一个80个字符组成的value。 

排序的任务:按照key的顺序排。 

那么1TB的数据从何而来?答案是用程序随即生成的,用一个只有map,没有reduce的MapReduce job,在整个集群上先随即生成100亿行数据。然后,在这个基础上,再运行排序的MapReduce job,以测试集群排序性能。 


3、排序的原理 

先说明一点,熟悉MapReduce的人都知道:排序是MapReduce的天然特性!在数据达到reducer之前,mapreduce框架已经对这些数据按键排序了。 

所以,在这个排序的job里,不需要特殊的Mapper和Reducer类。用默认的 
IdentityMapper和IdentityReducer即可。 

既然排序是天然特性,那么1TB排序的难点在哪里呢??答:100亿行的数据随即分散在1000多台机器上,mapper和reducer都是Identity的,这个难点就在MapReduce的shuffle阶段!关键在如何取样和怎么写Partitioner。 

好在这个排序的源代码已近包含在hadoop的examples里了,下面我们就来分析一下。 


4、取样和partition的过程 

面对对这么大量的数据,为了partition的更均匀。要先“取样”: 

1) 对Math.min(10, splits.length)个split(输入分片)进行随机取样,对每个split取10000个样,总共10万个样 
2) 10万个样排序,根据reducer的数量(n),取出间隔平均的n-1个样 
3) 将这个n-1个样写入partitionFile(_partition.lst,是一个SequenceFile),key是取的样,值是nullValue 
4) 将partitionFile写入DistributedCache 

接下来,正式开始执行MapReduce job: 
5) 每个map节点: 
          a.根据n-1个样,build一棵类似于B-数的“索引树”: 
                    * 每个非叶子节点,都有256个子节点。 
                    * 不算根节点的非叶子节点有1层,加上根节点和叶子节点,共3层。 
                    * 非叶子节点代表key的“byte path” 
                    * 每个叶子节点代表key的前2个bytes path 
                    * 叶子节点上,保存的是partition number的范围,有多少个reducer就有多少partition number 

          b.前缀相同的key,被分配到同一个叶子节点。 
          c.一个子节点上,可能有多个reducer 
          d.比第i个样小的key,被分配到第i个reducer,剩下的被分配到最后一个reducer。 

6) 针对一个key,partition的过程: 

          a. 首选判断key的第1个byte,找到第1层非叶子节点 
          b. 再根据key的第2个byte,叶子节点 
          c. 每个叶子节点可能对应多个取样(即多个reducer),再逐个和每个样比较,确定分配给哪一个reducer 


5、图解partition的“索引树” 

对上面的文字描述可能比较难理解,etongg 同学建议我画个图。所有才有了下面这些文字。感谢etongg和大家对本帖的关注。 

“索引树”的作用是为了让key快速找到对应的reducer。下图是我画的索引树示意图: 

 


对上面的图做一点解释: 
1、为了简单,我只画了A、B、C三个节点,实际的是有256个节点的。 
2、这个图假设有20个reducer(下标0到19),那么我们最终获得n-1个样,即19个样(下标为18的为最后一个样) 
3、图中的圆圈,代表索引树上的节点,索引树共3层。 
4、叶子节点下面的长方形代表取样数组。红色的数字代表取样的下标。 
5、每个节点都对应取样数组上的一个下标范围(更准备的说,是对应一个partition number的范围,每个partition number代表一个reducer)。这个范围在途中用蓝色的文字标识。 


前面文中有一句话: 
比第i个样小的key,被分配到第i个reducer,剩下的被分配到最后一个reducer 

这里做一个小小的纠正,应该是: 
小于或者等于第i个样的key,被分配到第i个reducer,剩下的被分配到最后一个reducer。 

下面开始partition: 
如果key以"AAA"开头,被分配到第“0”个reducer。 
如果key以"ACA"开头,被分配到第“4”个reducer。 
如果key以"ACD"开头,被分配到第“4”个reducer。 
如果key以"ACF"开头,被分配到第“5”个reducer。 

那么, 
如果key以"ACZ"开头,被分配到第几个reducer?? 
答案是:被分配到第“6”个reducer。 

同理, 
如果key以"CCZ"开头,被分配到第“19”个reducer,也就是最后一个reducer。 


6、为什么不用HashPartitioner? 


还需要再说明的一点: 
上面自定义的Partitinoner的作用除了快速找到key对应的reducer,更重要的一点是:这个Partitioner控制了排序的总体有序! 

上文中提到的“排序是MapReduce的天然特性!”这句话有点迷惑性。更准确的说,这个“天然特性”只保证了:a) 每个map的输出结果是有序的; b) 每个reduce的输入是有序的(参考下面的图)。而1TB的整体有序还需要靠Partitioner的帮助! 


Partitioner控制了相似的key(即前缀相同)落在同一个reducer里,然后mapreduce的“天然特性”再保证每个reducer的输入(在正式执行reduce函数前,有一个排序的动作)是有序的! 


这样就理解了为什么不能用HashPartitioiner了。因为自定义的Partitioner要保证排序的“整体有序”大方向。 


另外,推荐一篇关于partitioner博文:Hadoop Tutorial Series, Issue #2: Getting Started With (Customized) Partitioning 

再贴《Hadoop.The.Definitive.Guide》中一张图,更有利于理解了: 
 

*** THE END *** 

转载www.iteye.com/topic/709986

<think>我们面对的是HadoopStreaming任务失败的问题,具体有两个主要错误:1.PipeMapRed.waitOutputThreads():subprocessfailedwithcode1-说明mapper或reducer进程异常退出(返回非0状态码)2.容器因超出虚拟内存限制被YARN杀死(Containerkilledonrequest.Exitcodeis143)根据日志分析:-Mapper任务尝试3次均失败,错误码1(Python脚本执行异常)-Reducer任务因内存超限被终止(虚拟内存使用2.6GB,超过2.1GB限制)==解决思路==1.修复Python脚本异常(确保mapper/reducer能在集群环境运行)2.调整YARN内存参数(解决容器被杀问题)3.优化Python代码内存使用===详细解决方案===1.**诊断Python脚本错误**Mapper失败的根本原因是Python脚本执行异常。通过以下步骤诊断:-在脚本入口添加异常捕获(确保异常信息输出到stderr)```python#my_mapper_csv.py示例改造importsysimporttracebackdefmain():forlineinsys.stdin:#处理逻辑if__name__=="__main__":try:main()exceptExceptionase:sys.stderr.write(f"ERROR:{str(e)}\n")sys.stderr.write(traceback.format_exc())sys.exit(1)```-本地测试脚本:```bashcatinput.csv|python3my_mapper_csv.py```-常见错误:-Python版本不匹配(集群节点可能缺python3)-未安装必需库(如pandas等)-输入数据格式不符合预期(空行、字段不足等)2.**调整内存配置**原始任务内存参数不足,需在命令行或`mapred-site.xml`中增加:```bashhadoopjar...\-Dmapreduce.map.memory.mb=4096\#容器物理内存上限4GB-Dmapreduce.reduce.memory.mb=8192\-Dmapreduce.map.java.opts="-Xmx3072m"\#JVM堆内存(为容器内存留余量)-Dmapreduce.reduce.java.opts="-Xmx6144m"\-Dyarn.nodemanager.vmem-pmem-ratio=4.5\#虚拟内存/物理内存比例调高(默认2.1易超限)...```**参数解释**:-`mapreduce.[map|reduce].memory.mb`:容器分配的物理内存-`mapreduce.[map|reduce].java.opts`:JVM堆大小(通常设为内存的75%)-`yarn.nodemanager.vmem-pmem-ratio`:虚拟内存扩展系数(可调至4-5)3.**Python代码内存优化**即使调整配置,脚本自身也需控制内存:-**避免全量加载数据**(流式处理):```python#错误示例(数据大时内存爆炸)data=[lineforlineinsys.stdin]#正确做法(逐行流式)forlineinsys.stdin:process(line)```-**减少对象引用**(及时释放):```python#大对象用完后立即删除large_list=[...]result=compute(large_list)dellarge_list#主动释放```-**使用生成器代替列表**(reducer分组时尤其关键):```pythonfromitertoolsimportgroupbyforkey,groupingroupby(sys.stdin,key=key_func):process_group(list(group))#注意:groupby要求输入已排序```===任务重启命令示例===综合调整后,完整任务提交:```bashhadoopjar"$HADOOP_HOME/share/hadoop/tools/lib/hadoop-streaming-3.1.3.jar"\-files"hdfs:///absolute/path/to/my_mapper_csv.py,my_reducer_csv.py"\#分发脚本(HDFS路径或本地)-archives"hdfs:///path/python_env.zip#pyenv"\#携带Python环境(若有第三方库)-Dmapreduce.map.memory.mb=4096\-Dmapreduce.reduce.memory.mb=8192\-Dyarn.nodemanager.vmem-pmem-ratio=5\-input/my_input_csv/*\-output/my_output.csv\-mapper"pyenv/bin/pythonmy_mapper_csv.py"\#使用归档中的Python解释器-reducer"pyenv/bin/pythonmy_reducer_csv.py"```注意:-`-files`替换废弃的`-file`参数[^1]-若用`-archives`,集群所有节点无需预装相同Python环境===相关配置参考===在`mapred-site.xml`中永久设置内存参数(避免每次命令行输入):```xml<configuration><!--内存设置--><property><name>mapreduce.map.memory.mb</name><value>4096</value></property><property><name>mapreduce.reduce.memory.mb</name><value>8192</value></property><property><name>yarn.nodemanager.vmem-pmem-ratio</name><value>5</value></property></configuration>```[^1]:引用配置示例</think> ### Hadoop Streaming任务失败分析与解决方案 #### 问题诊断 1. **PipeMapRed.waitOutputThreads()错误** - 日志显示Mapper进程以错误码1退出:`subprocess failed with code 1` - 常见原因: * Python脚本执行异常(未捕获的异常) * 集群节点缺失Python依赖 * 输入数据格式问题(如CSV字段缺失) 2. **内存超限错误** ```log Container [pid=72527...] is running 537983488B beyond the 'VIRTUAL' memory limit Current usage: 168.3 MB of 1 GB physical memory used; 2.6 GB of 2.1 GB virtual memory used ``` - 虚拟内存(2.6GB)超出限制(2.1GB) - Reducer任务因内存超限被杀死(Exit code 143) --- #### 完整解决方案 ##### 第一步:修复Python脚本问题 1. **添加异常处理机制** 在Python脚本中添加全局异常捕获: ```python # my_mapper_csv.py 修改示例 import sys import traceback def process_line(line): # 原数据处理逻辑 return processed_data if __name__ == "__main__": try: for line in sys.stdin: result = process_line(line) print(result) except Exception as e: sys.stderr.write(f"ERROR: {str(e)}\n") sys.stderr.write(traceback.format_exc()) # 输出堆栈 sys.exit(1) # 必须返回非零退出码 ``` 2. **验证脚本本地执行** ```bash cat sample.csv | python3 my_mapper_csv.py | sort | python3 my_reducer_csv.py ``` 检查是否有错误输出 ##### 第二步:解决内存超限问题 1. **调整YARN内存参数** ```bash hadoop jar hadoop-streaming-3.1.3.jar \ -D mapreduce.map.memory.mb=4096 \ # 容器物理内存 -D mapreduce.reduce.memory.mb=8196 \ # Reducer需要更多内存 -D mapreduce.map.java.opts="-Xmx3072m" \ # JVM堆内存 -D mapreduce.reduce.java.opts="-Xmx6144m" \ -D yarn.nodemanager.vmem-pmem-ratio=5 \ # 虚拟/物理内存比(默认2.1) -files my_mapper_csv.py,my_reducer_csv.py \ # 替换-file参数 -mapper "python3 my_mapper_csv.py" \ -reducer "python3 my_reducer_csv.py" \ -input /my_input_csv/* \ -output /my_output.csv ``` 2. **关键参数说明** | 参数 | 推荐值 | 作用 | |------|--------|------| | `yarn.nodemanager.vmem-pmem-ratio` | 4-5 | 解决虚拟内存超限问题 | | `mapreduce.reduce.memory.mb` | ≥4096 | Reducer物理内存 | | `mapreduce.reduce.java.opts` | 物理内存的75% | JVM堆大小 | ##### 第三步:优化Python脚本内存 1. **避免全量加载数据** ```python # 错误做法(内存爆炸): data = [line for line in sys.stdin] # 正确做法(流式处理): for line in sys.stdin: fields = line.strip().split(",") # 立即处理并输出 ``` 2. **使用分组迭代器** ```python # Reducer优化示例 from itertools import groupby def key_func(line): return line.split(",")[0] # 按首字段分组 sorted_data = sorted(sys.stdin, key=key_func) for key, group in groupby(sorted_data, key=key_func): process_group(list(group)) # 分组处理 ``` ##### 第四步:永久配置优化(mapred-site.xml) ```xml <configuration> <!-- 内存参数优化 --> <property> <name>yarn.nodemanager.vmem-pmem-ratio</name> <value>4.5</value> </property> <property> <name>mapreduce.map.memory.mb</name> <value>4096</value> </property> <property> <name>mapreduce.reduce.memory.mb</name> <value>8192</value> </property> <!-- 历史服务器配置(便于调试) --> <property> <name>mapreduce.jobhistory.address</name> <value>master:10020</value> </property> </configuration> ``` #### 调试技巧 1. **查看完整错误日志** ```bash yarn logs -applicationId application_1744881937486_0004 > debug.log grep "Caused by" debug.log -A 15 ``` 2. **监控内存使用** 在Python脚本中添加: ```python import resource # Mapper/Reducer中定期输出 sys.stderr.write(f"MEM USAGE: {resource.getrusage(resource.RUSAGE_SELF).ru_maxrss} KB\n") ``` --- ### 相关问题 1. 如何为Hadoop Streaming任务分发Python虚拟环境? 2. YARN容器被杀(Exit code 143)有哪些具体原因? 3. 如何动态调整MapReduce任务的Reducer数量?[^4] 4. Hadoop Streaming与原生Java任务性能差异如何优化? 5. 如何监控Hadoop集群中各节点的内存使用情况?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值