Hadoop中的Terasort算法2<转>

本文详细介绍了使用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亿条数据?都是什么样的数据呢?让我们来看几条: 
  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  
.t^#\|v$2\ 0AAAAAAAAAABBBBBBBBBBCCCCCCCCCCDDDDDDDDDDEEEEEEEEEEFFFFFFFFFFGGGGGGGGGGHHHHHHHH 75@~?'WdUF 1IIIIIIIIIIJJJJJJJJJJKKKKKKKKKKLLLLLLLLLLMMMMMMMMMMNNNNNNNNNNOOOOOOOOOOPPPPPPPP w[o||:N&H, 2QQQQQQQQQQRRRRRRRRRRSSSSSSSSSSTTTTTTTTTTUUUUUUUUUUVVVVVVVVVVWWWWWWWWWWXXXXXXXX ^Eu)<n#kdP 3YYYYYYYYYYZZZZZZZZZZAAAAAAAAAABBBBBBBBBBCCCCCCCCCCDDDDDDDDDDEEEEEEEEEEFFFFFFFF +l-$$OE/ZH 4GGGGGGGGGGHHHHHHHHHHIIIIIIIIIIJJJJJJJJJJKKKKKKKKKKLLLLLLLLLLMMMMMMMMMMNNNNNNNN LsS8)|.ZLD 5OOOOOOOOOOPPPPPPPPPPQQQQQQQQQQRRRRRRRRRRSSSSSSSSSSTTTTTTTTTTUUUUUUUUUUVVVVVVVV le5awB.$sm 6WWWWWWWWWWXXXXXXXXXXYYYYYYYYYYZZZZZZZZZZAAAAAAAAAABBBBBBBBBBCCCCCCCCCCDDDDDDDD q__[fwhKFg 7EEEEEEEEEEFFFFFFFFFFGGGGGGGGGGHHHHHHHHHHIIIIIIIIIIJJJJJJJJJJKKKKKKKKKKLLLLLLLL ;L+!2rT~hd 8MMMMMMMMMMNNNNNNNNNNOOOOOOOOOOPPPPPPPPPPQQQQQQQQQQRRRRRRRRRRSSSSSSSSSSTTTTTTTT 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(输入分片)进行随机取样,总共10万个样,对每个split取10000个(split数目达到10个及以上时候, added by jiwan)。
2) 10万个样排序,根据reducer的数量(n),取出将所有10万个样平均分隔的n-1个样 
3) 将这个n-1个样写入partitionFile(_partition.lst,是一个SequenceFile),key是取的样,值是nullValue 
4) 将partitionFile写入DistributedCache 

接下来,正式开始执行MapReduce job: 
5)  每个map节点 : 
a.根据n-1个样,build一棵类似于B-数的“索引树”: 
* 每个非叶子节点,都有 256个子节点(应为2^8=256, added by jiwan)。 
* 不算根节点的非叶子节点有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》中一张图,更有利于理解了: 
一、存储 PB 级环境监测数据的性能优化方案 技术工具:Hadoop 3.x 功能:分布式存储与资源管理 1.数据存储架构优化 为高效存储 PB 级环境监测数据,在原有 Hadoop 集群架构基础上,采用分层存储策略。将高频访问的实时监测数据和近期预测数据存储于 HDFS 的高速存储层,选用 SSD 硬盘作为存储介质,利用其快速读写特性,满足实时监测与短期预测对数据读取速度的严格要求。对于历史监测数据等低频访问数据,则迁移至 HDFS 的大容量存储层,使用 HDD 硬盘降低存储成本。同时,通过Hadoop的异构存储管理功能,实现不同存储层之间的数据自动分层与迁移,在保证性能的前提下,优化存储资源利用率。 (1)异构存储配置实现 Hadoop 3.x 支持异构存储策略,通过配Storage Policy可自动管理数据分层。在hdfs-site.xml中添加以下配置: <property> <name>dfs.storage.policy.enabled</name> <value>true</value> </property> <property> <name>dfs.storage.policy.names</name> <value>LAZY_PERSIST, ALL_SSD, ONE_SSD, HOT, WARM, COLD, ALL_DISK</value> </property> 创建存储策略映射表,将不同类型数据分配到对应存储层: # 为实时数据设置SSD存储策略 hdfs storagepolicies -setStoragePolicy -path /air_quality/real_time -policy ALL_SSD # 为历史数据设置HDD存储策略 hdfs storagepolicies -setStoragePolicy -path /air_quality/historical -policy COLD (2)自动分层迁移脚本 编写定期执行的数据分层迁移脚本,将超过 30 天的历史数据自动迁移至冷存储层: #!/usr/bin/env python3 import subprocess import datetime import logging logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(message)s') def migrate_old_data(days_threshold=30): cutoff_date = (datetime.datetime.now() - datetime.timedelta(days=days_threshold)).strftime('%Y-%m-%d') # 获取所有超过阈值的文件 cmd = f"hdfs dfs -find /air_quality/data -type f -mtime +{days_threshold}" files = subprocess.check_output(cmd, shell=True).decode().strip().split('\n') for file in files: # 检查文件当前存储策略 policy = subprocess.check_output(f"hdfs storagepolicies -getStoragePolicy -path {file}", shell=True).decode().strip() # 如果不是冷存储策略,则迁移 if "COLD" not in policy: logging.info(f"Migrating {file} to COLD storage") subprocess.run(f"hdfs storagepolicies -setStoragePolicy -path {file} -policy COLD", shell=True) if __name__ == "__main__": migrate_old_data() 2.数据压缩与编码 在数据写入 HDFS 前,应用高效的数据压缩算法与编码技术。针对空气质量监测数据的特点,采用 Snappy 压缩算法,该算法在提供较高压缩比的同时,具备极快的压缩和解压缩速度,能够显著减少数据存储空间,降低数据传输过程中的网络带宽消耗。此外,对数值型的污染物浓度等数据,运用列式存储与 Run - Length Encoding(行程长度编码)相结合的方式,进一步提升数据压缩效果,加速数据分析与查询操作,为 PB 级数据的高效处理提供支持。 (1)压缩算法配置 在 Hadoop 集群中配置 Snappy 压缩算法,修改mapred-site.xml: <property> <name>mapreduce.map.output.compress</name> <value>true</value> </property> <property> <name>mapreduce.map.output.compress.codec</name> <value>org.apache.hadoop.io.compress.SnappyCodec</value> </property> <property> <name>mapreduce.output.fileoutputformat.compress</name> <value>true</value> </property> <property> <name>mapreduce.output.fileoutputformat.compress.codec</name> <value>org.apache.hadoop.io.compress.SnappyCodec</value> </property> (2)列式存储与编码实现 使用 Parquet 列式存储格式处理数值型监测数据,配置示例: from pyspark.sql import SparkSession spark = SparkSession.builder \ .appName("AirQualityDataProcessing") \ .getOrCreate() # 读取原始数据 df = spark.read.csv("/air_quality/raw_data/*.csv", header=True) # 应用RLE编码并存储为Parquet格式 df.write \ .option("compression", "snappy") \ .option("parquet.enable.dictionary", "true") \ .option("parquet.page.size", 1048576) \ .parquet("/air_quality/processed_data") 3.集群扩展与负载均衡 随着环境监测数据量的持续增长,制定动态集群扩展策略。当集群存储利用率达到预设阈值(如 80%)时,自动添加新的 DataNode 节点。通过 Hadoop 的自动发现机制,新节点能够快速加入集群,并自动同步元数据数据块,实现无缝扩展。同时,优化 YARN 资源管理器的负载均衡算法,根据各节点的 CPU、内存、磁盘 I/O 等资源使用情况,动态分配任务,避免部分节点负载过高而影响整体性能,确保集群在处理 PB 级数据时始终保持高效稳定运行。 (1)动态扩缩容脚本 实现基于存储利用率的自动扩缩容机制: #!/usr/bin/env python3 import subprocess import json import logging import requests logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(message)s') # 阈值配置 STORAGE_THRESHOLD = 0.8 # 80%使用率触发扩容 INSTANCE_TEMPLATE = "hadoop-datanode-template" MAX_NODES = 50 def get_cluster_usage(): # 获取HDFS集群使用情况 cmd = "hdfs dfsadmin -report" output = subprocess.check_output(cmd, shell=True).decode() # 解析输出获取使用百分比 for line in output.split('\n'): if "DFS Used%" in line: usage = float(line.split(':')[1].strip().replace('%', '')) / 100 return usage return 0.0 def scale_cluster(): usage = get_cluster_usage() logging.info(f"Current cluster usage: {usage:.2%}") # 获取当前节点数 cmd = "hdfs dfsadmin -report | grep 'Live datanodes' | awk '{print $3}'" current_nodes = int(subprocess.check_output(cmd, shell=True).decode().strip()) if usage > STORAGE_THRESHOLD and current_nodes < MAX_NODES: # 计算需要添加的节点数 nodes_to_add = min(5, MAX_NODES - current_nodes) logging.info(f"Adding {nodes_to_add} new datanodes...") # 使用GCP Compute Engine API添加节点 cmd = f"gcloud compute instance-groups managed resize hadoop-datanodes --size={current_nodes + nodes_to_add}" subprocess.run(cmd, shell=True) # 等待节点加入集群 logging.info("Waiting for new nodes to join the cluster...") # 实际应用中应添加等待节点就绪的逻辑 if __name__ == "__main__": scale_cluster() (2)资源调度优化配置 在yarn-site.xml中优化资源调度配置: <property> <name>yarn.resourcemanager.scheduler.class</name> <value>org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacityScheduler</value> </property> <property> <name>yarn.scheduler.capacity.root.queues</name> <value>air_quality, batch, default</value> </property> <property> <name>yarn.scheduler.capacity.root.air_quality.capacity</name> <value>60</value> </property> <property> <name>yarn.scheduler.capacity.root.air_quality.maximum-capacity</name> <value>80</value> </property> <property> <name>yarn.scheduler.capacity.resource-calculator</name> <value>org.apache.hadoop.yarn.util.resource.DominantResourceCalculator</value> </property> 三、方案实施效果评估与总结 1.性能评估 通过模拟 PB 级环境监测数据的写入、存储和查询操作,对存储方案的性能进行全面评估。在数据写入方面,测试不同数据规模下的写入速度与吞吐量,验证数据压缩和集群扩展策略对写入性能的提升效果;在数据查询环节,针对常见的空气质量指标查询、历史数据统计分析等场景,测量查询响应时间,评估数据编码和负载均衡优化对查询性能的影响。将评估结果与预设的性能指标进行对比,分析方案在处理 PB 级数据时的优势与不足,为后续优化提供依据。 (1)基准测试脚本 使用 Terasort 基准测试评估 HDFS 性能: #!/bin/bash # 配置测试参数 SIZE=10000000000 # 10GB数据 NUM_MAPS=20 NUM_REDS=5 INPUT_DIR=/benchmarks/terasort/input OUTPUT_DIR=/benchmarks/terasort/output RESULT_FILE=~/terasort_results.txt # 生成测试数据 echo "Generating test data..." hadoop jar $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-*.jar teragen -Dmapred.map.tasks=$NUM_MAPS $SIZE $INPUT_DIR # 执行排序测试 echo "Running terasort benchmark..." START_TIME=$(date +%s) hadoop jar $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-*.jar terasort -Dmapred.map.tasks=$NUM_MAPS -Dmapred.reduce.tasks=$NUM_REDS $INPUT_DIR $OUTPUT_DIR END_TIME=$(date +%s) # 计算并保存结果 ELAPSED_TIME=$((END_TIME - START_TIME)) echo "Terasort completed in $ELAPSED_TIME seconds" > $RESULT_FILE # 验证结果 echo "Validating results..." hadoop jar $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-*.jar teravalidate $OUTPUT_DIR $OUTPUT_DIR/validate # 清理测试数据 echo "Cleaning up..." hdfs dfs -rm -r -f $INPUT_DIR $OUTPUT_DIR (2)性能监控配置 配置 Ganglia 监控 Hadoop 集群性能: # gmetad.conf配置 data_source "hadoop_cluster" namenode1:8651 datanode1:8651 datanode2:8651 # gmond.conf配置示例 cluster { name = "hadoop_cluster" owner = "AirQualitySystem" latlong = "unknown" url = "unknown"} udp_send_channel { host = namenode1 port = 8649 ttl = 1} udp_recv_channel { port = 8649} tcp_accept_channel { port = 8651 } 2.总结 性能优化成果:通过实施上述优化方案,系统性能提升显著 存储效率:通过分层存储和压缩技术,存储成本降低 40%,存储容量利用率提升至 85% 处理速度:实时数据查询响应时间从平均 2.5 秒降至 0.8 秒,提升 68% 扩展性:动态扩缩容机制使集群可在 15 分钟内完成 5 节点扩展,满足突发数据增长需求 生成关于以上内容的代码运行截图。
最新发布
06-12
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值