☰
Hadoop实战:从综述到伪分布式搭建与HBase操作
2026/10/5 5:14:45 网站建设 项目流程

简介:这份Hadoop综述PPT面向大数据入门学习者与分布式系统初学者,系统梳理Hadoop核心知识框架,帮助读者建立对HDFS、MapReduce与HBase三大组件的整体认知。内容围绕HDFS分布式文件系统展开,涵盖设计目标、数据块机制、NameNode与DataNode主从模型、命名空间映像与修改日志等要点,并延伸至MapReduce基础、数据流与工作原理,以及HBase简介、数据模型与行列时间戳API。资源包共1个文件,为ppt格式,大小约1.03MB,适合课堂讲解、自学梳理或面试前快速回顾。目前已有32人学习浏览,可作为大数据技术栈的入门索引,帮助读者在较短时间内把握Hadoop各模块的定位与关联,为后续深入实践打下概念基础。

1. 从一份 Hadoop综述.ppt 说起:为什么很多人看完还是搭不起集群

你可能也遇到过这种场景:接手一份Hadoop综述.ppt,翻完几十页,HDFS、MapReduce、HBase 的名词都认识了,架构图也能画出来,可真让你在一台机器上把伪分布式跑起来、往 HBase 里写一条数据,手还是生的。这份综述类材料最大的价值不是讲原理,而是给你一张「知识地图」——它告诉你 Hadoop 生态里 HDFS 管存储、MapReduce 管计算、HBase 管随机读写,但不会告诉你core-site.xml里fs.defaultFS该填什么、hbase-site.xml的hbase.rootdir为什么要指向 HDFS。

这篇笔记就顺着这张地图往下走:先把 HDFS 读写流程和 MapReduce 执行模型讲透,再落到伪分布式搭建、HDFS 常用命令、MapReduce 编程实例、HBase 安装与 Java 操作这几件具体的事上。适合两类人:一是刚学完理论、准备做课程设计或综合实训的学生;二是需要快速把 Hadoop 环境跑起来验证想法的后端或数据开发。目标很明确——读完你能自己搭一套能用的环境,知道每个参数为什么这么设,踩坑时知道去哪查。

2. HDFS 与 MapReduce:综述里没讲透的两个执行流程

2.1 HDFS 读写流程:NameNode、DataNode 和 Client 到底怎么配合

综述 PPT 通常只画一张「一个 NameNode 加多个 DataNode」的图,但真正决定你调优和排错的是读写时的数据流。先看写流程:客户端调用FileSystem.create()时,NameNode 并不接收文件内容,它只做两件事——检查路径是否存在、是否有权限,然后在内存的命名空间里创建一个 INode,返回一个包含若干 DataNode 地址的LocatedBlock列表。真正写数据的是客户端和 DataNode 之间的DataStreamer,数据以 packet(默认 64KB)为单位,沿着一条 pipeline 逐级传递,最后一个 DataNode 写完才逐级回 ack。

这里有个容易被忽略的点:pipeline 是链式的,不是并行的。假设副本数设为 3,客户端只连第一个 DataNode,第一个连第二个,第二个连第三个。任何一个节点挂了,客户端会收到异常,然后从 pipeline 里移除故障节点、重新申请一个新节点补上,继续写。这就是为什么dfs.replication设成 3 时,写入延迟对最慢的那个 DataNode 很敏感。

读流程相对简单:客户端拿到的LocatedBlock里,DataNode 是按网络拓扑距离排序的,优先读同机架的副本。如果读失败,客户端会标记该副本并转向下一个,同时向 NameNode 报告坏块。理解这一点,你就能明白为什么hdfs dfs -cat大文件时偶尔会卡一下——它在做副本切换。

2.2 MapReduce 执行模型:从 InputSplit 到 Reduce 的完整链路

MapReduce 的综述往往停在「Map 阶段和 Reduce 阶段」这句话上,但你要写一个能跑的作业,必须知道中间发生了什么。作业提交后,JobClient把 jar 和配置上传到 HDFS,ResourceManager 分配一个 ApplicationMaster,AM 再向 RM 申请 Container 来跑 Map 和 Reduce 任务。

Map 任务的输入是 InputSplit,默认一个 block(128MB)对应一个 split。RecordReader把 split 解析成 key-value 对喂给 map 函数,map 输出先写到环形缓冲区(默认 100MB,阈值 80%),溢写时按 partition 排序。每个 Map 任务最终会生成一个按分区排好序的文件,Reduce 任务通过 HTTP 拉取属于自己的那些分区数据,做归并排序后再喂给 reduce 函数。

关键参数有三个:mapreduce.task.io.sort.mb控制环形缓冲区大小,mapreduce.map.sort.spill.percent控制溢写阈值,mapreduce.reduce.shuffle.parallelcopies控制 Reduce 拉取的并发数。数据倾斜时,Reduce 卡在 shuffle 阶段,多半是某个 key 的数据量远超其他 key,这时候要么加 Combiner,要么在 map 输出时对 key 做加盐处理。

2.3 伪分布式搭建:从零到能跑 WordCount 的最小步骤

理论讲完,落到动手。伪分布式是单机模拟多节点的最小环境,适合开发和验证。下面这套步骤在 Linux 上通用,JDK 用 8 或 11 都行。

第一步,准备环境和 SSH 免密。Hadoop 的启动脚本依赖 SSH,即使是伪分布式也要配本机免密。

# 安装 JDK 并配置 JAVA_HOME sudo apt install openjdk-8-jdk -y echo 'export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64' >> ~/.bashrc source ~/.bashrc # 配置本机 SSH 免密,否则 start-dfs.sh 会反复要密码 ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys chmod 600 ~/.ssh/authorized_keys ssh localhost # 验证能免密登录

第二步,解压 Hadoop 并配置四个核心文件。core-site.xml指定默认文件系统,hdfs-site.xml设副本数,mapred-site.xml指定用 YARN 跑 MapReduce,yarn-site.xml配 ResourceManager。

tar -xzf hadoop-3.3.6.tar.gz -C /opt echo 'export HADOOP_HOME=/opt/hadoop-3.3.6' >> ~/.bashrc echo 'export PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin' >> ~/.bashrc source ~/.bashrc
<!-- core-site.xml:fs.defaultFS 是客户端默认访问的地址 --> <configuration> <property> <name>fs.defaultFS</name> <value>hdfs://localhost:9000</value> </property> <property> <name>hadoop.tmp.dir</name> <value>/opt/hadoop-3.3.6/tmp</value> </property> </configuration>
<!-- hdfs-site.xml:伪分布式副本数只能设 1,设 3 会一直报副本不足 --> <configuration> <property> <name>dfs.replication</name> <value>1</value> </property> <property> <name>dfs.namenode.name.dir</name> <value>/opt/hadoop-3.3.6/data/namenode</value> </property> <property> <name>dfs.datanode.data.dir</name> <value>/opt/hadoop-3.3.6/data/datanode</value> </property> </configuration>
<!-- mapred-site.xml:告诉 MapReduce 用 YARN 作为调度框架 --> <configuration> <property> <name>mapreduce.framework.name</name> <value>yarn</value> </property> </configuration>
<!-- yarn-site.xml:aux-service 必须配,否则 MapReduce 的 shuffle 会失败 --> <configuration> <property> <name>yarn.nodemanager.aux-services</name> <value>mapreduce_shuffle</value> </property> </configuration>

第三步,格式化并启动。格式化只能做一次,重复格式化会导致 NameNode 和 DataNode 的 clusterID 不一致,DataNode 起不来。

hdfs namenode -format # 只执行一次 start-dfs.sh # 启动 NameNode、DataNode、SecondaryNameNode start-yarn.sh # 启动 ResourceManager、NodeManager jps # 应看到 NameNode、DataNode、ResourceManager、NodeManager

参数说明:fs.defaultFS的端口 9000 是 NameNode RPC 端口,Web UI 在 9870;hadoop.tmp.dir建议单独设,默认在 /tmp 下重启会丢;dfs.replication伪分布式必须为 1。验证方式很简单,跑一个自带例子:

hdfs dfs -mkdir -p /user/root/input hdfs dfs -put $HADOOP_HOME/etc/hadoop/*.xml /user/root/input hadoop jar $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.3.6.jar \ wordcount /user/root/input /user/root/output hdfs dfs -cat /user/root/output/part-r-00000 | head

能出词频结果,说明 HDFS 和 MapReduce 都通了。

3. HDFS 常用命令与 MapReduce 编程实例:把综述里的名词变成能跑的任务

3.1 HDFS 常用命令:别只会 put 和 cat

HDFS 命令是日常操作最频繁的部分,但很多人只会put、ls、cat。下面这张表把高频命令和易错点列清楚,建议收藏。

命令作用易错点
hdfs dfs -ls -R /path递归列出路径不存在时报No such file or directory,先-ls /确认
hdfs dfs -put local hdfs上传目标已存在会失败,加-f覆盖
hdfs dfs -get hdfs local下载本地同名文件会被覆盖,无提示
hdfs dfs -rm -r /path递归删除不加-r删目录会报错,且不进回收站
hdfs dfs -du -h /path查看大小-h人类可读,不加显示字节
hdfs dfs -count /path统计目录数和文件数输出四列:目录数、文件数、总大小、路径
hdfs dfs -setrep -w 2 /file改副本数-w等待完成,大文件会阻塞
hdfs dfsadmin -report集群状态看 DataNode 是否全部注册,容量是否正常

一个血泪经验:-rm默认不进回收站,删错了没有后悔药。如果开了回收站(fs.trash.interval大于 0),删除的文件会进/user/<user>/.Trash,可以用hdfs dfs -mv从回收站移回来。生产环境建议把fs.trash.interval设成 1440(分钟),给自己留一天缓冲。

3.2 MapReduce 编程实例:招聘数据清洗的完整代码

综合实训里常见的题目是「招聘数据清洗」,输入是招聘网站的原始 CSV,字段有公司名、职位、薪资、地点等,要求清洗掉空行、字段数不对的行,并统计各城市职位数。下面用 Java 写一个能直接跑的版本。

// 招聘数据清洗:过滤字段数不为 5 的行,并按城市统计职位数 public class JobClean { // Mapper:解析 CSV,字段数不对直接丢弃 public static class CleanMapper extends Mapper<LongWritable, Text, Text, IntWritable> { private Text city = new Text(); private final static IntWritable ONE = new IntWritable(1); @Override protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { String line = value.toString().trim(); if (line.isEmpty()) return; // 跳过空行 String[] fields = line.split(",", -1); // -1 保留尾部空字段 if (fields.length != 5) return; // 字段数不对,丢弃 String cityName = fields[3].trim(); if (cityName.isEmpty()) return; // 城市为空,丢弃 city.set(cityName); context.write(city, ONE); } } // Reducer:累加每个城市的职位数 public static class SumReducer extends Reducer<Text, IntWritable, Text, IntWritable> { private IntWritable result = new IntWritable(); @Override protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException { int sum = 0; for (IntWritable val : values) sum += val.get(); result.set(sum); context.write(key, result); } } public static void main(String[] args) throws Exception { Configuration conf = new Configuration(); Job job = Job.getInstance(conf, "job clean"); job.setJarByClass(JobClean.class); job.setMapperClass(CleanMapper.class); job.setReducerClass(SumReducer.class); job.setOutputKeyClass(Text.class); job.setOutputValueClass(IntWritable.class); FileInputFormat.addInputPath(job, new Path(args[0])); FileOutputFormat.setOutputPath(job, new Path(args[1])); System.exit(job.waitForCompletion(true) ? 0 : 1); } }

逻辑说明:Mapper 里用split(",", -1)而不是split(","),是因为后者会丢弃行尾的空字段,导致字段数判断出错。fields.length != 5是清洗的核心规则,实际数据里字段数可能更多,按你的 CSV 调整。Reducer 做的是标准求和,没有用 Combiner,因为这里逻辑简单,加不加 Combiner 结果一样,但数据量大时加 Combiner 能显著减少 shuffle 数据量。

参数说明:job.setJarByClass指定主类,打包时要确保 manifest 正确;输入输出路径通过args传入,输出目录必须不存在,否则FileAlreadyExistsException。编译打包:

# 编译并打包成 jar,注意 Hadoop 的 classpath javac -classpath $(hadoop classpath) -d classes JobClean.java jar -cvf jobclean.jar -C classes . # 提交作业 hadoop jar jobclean.jar JobClean /user/root/recruit /user/root/recruit_out

如果作业卡在 map 100% reduce 0%,多半是 shuffle 阶段拉取失败,检查yarn-site.xml的aux-services是否配了mapreduce_shuffle。

3.3 用 Python 写 MapReduce:hadoop streaming 的取巧方式

不想写 Java 的话,Hadoop Streaming 允许用任意可执行程序做 map 和 reduce,Python 是最常用的。下面是一个词频统计的 mapper 和 reducer。

# mapper.py:从标准输入读,按行拆分单词,输出 word\t1 import sys for line in sys.stdin: line = line.strip() if not line: continue for word in line.split(): print(f"{word}\t1")
# reducer.py:按 key 聚合,输出 word\tcount import sys current_word = None current_count = 0 for line in sys.stdin: word, count = line.strip().split("\t", 1) count = int(count) if word == current_word: current_count += count else: if current_word: print(f"{current_word}\t{current_count}") current_word = word current_count = count if current_word: print(f"{current_word}\t{current_count}")

提交命令:

hadoop jar $HADOOP_HOME/share/hadoop/tools/lib/hadoop-streaming-3.3.6.jar \ -files mapper.py,reducer.py \ -mapper "python3 mapper.py" \ -reducer "python3 reducer.py" \ -input /user/root/input -output /user/root/pyout

注意 reducer 里假设输入是按 key 排好序的,这是 MapReduce 框架保证的,但前提是你在 reducer 里不能打乱顺序。另外-files会把脚本分发到各个节点,节点上必须有 python3,否则报command not found。

4. HBase 安装与 Java 操作:从综述里的「列式存储」到能读写的表

4.1 HBase 安装与配置:伪分布式下必须对齐的三个参数

HBase 依赖 HDFS 做底层存储,所以先确保 HDFS 正常。下载解压后,核心是改hbase-site.xml和hbase-env.sh。

<!-- hbase-site.xml:伪分布式最小配置 --> <configuration> <property> <name>hbase.rootdir</name> <value>hdfs://localhost:9000/hbase</value> </property> <property> <name>hbase.cluster.distributed</name> <value>true</value> </property> <property> <name>hbase.zookeeper.quorum</name> <value>localhost</value> </property> <property> <name>hbase.zookeeper.property.dataDir</name> <value>/opt/hbase-2.5.5/zookeeper</value> </property> </configuration>
# hbase-env.sh:关掉自带 ZooKeeper,用外部的;指定 JDK export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64 export HBASE_MANAGES_ZK=false

三个参数必须对齐:hbase.rootdir的地址要和core-site.xml的fs.defaultFS一致,端口写错会连不上 HDFS;hbase.cluster.distributed设 true 才是分布式模式,伪分布式也要设 true;HBASE_MANAGES_ZK设 false 表示用外部 ZooKeeper,如果你没装 ZooKeeper,设 true 让 HBase 自己管也行,但生产环境一般用外部。启动:

start-hbase.sh jps # 应看到 HMaster、HRegionServer、HQuorumPeer(如果自带 ZK)

HBase 的端口清单:HMaster Web UI 16010,RegionServer Web UI 16030,ZooKeeper 2181,HMaster RPC 16000,RegionServer RPC 16020。连不上时先telnet localhost 2181确认 ZK 在。

4.2 HBase 表设计与数据操作:rowkey 设计决定查询性能

HBase 的表设计和关系型数据库完全不同,核心是 rowkey。rowkey 按字典序排序,所以设计时要考虑查询模式。比如要查某个用户最近的订单,rowkey 可以设计成userId + 反转时间戳,这样同一用户的记录连续存储,且最新的在前面。

建表和插入用 HBase Shell:

hbase shell # 建表:表名 student,列族 info create 'student', 'info' # 插入数据:rowkey 为 001,列族 info,列 name put 'student', '001', 'info:name', 'zhangsan' put 'student', '001', 'info:age', '20' # 查询单行 get 'student', '001' # 扫描全表 scan 'student' # 按 rowkey 范围扫描 scan 'student', {STARTROW => '001', STOPROW => '002'}

注意scan不加限制会全表扫描,大表上很危险,一定要带LIMIT或STARTROW/STOPROW。列族不宜过多,官方建议 1 到 3 个,因为列族多了会影响 flush 和 compaction 效率。

4.3 用 Java 操作 HBase:Put、Get、Scan 的最小示例

课程设计里常要求用 Java API 操作 HBase。下面是一个完整的增查示例。

// HBase Java API:建表、插入、查询 public class HBaseDemo { public static void main(String[] args) throws IOException { Configuration conf = HBaseConfiguration.create(); conf.set("hbase.zookeeper.quorum", "localhost"); conf.set("hbase.zookeeper.property.clientPort", "2181"); try (Connection conn = ConnectionFactory.createConnection(conf); Admin admin = conn.getAdmin()) { TableName tableName = TableName.valueOf("student"); // 建表:如果不存在才建 if (!admin.tableExists(tableName)) { TableDescriptor desc = TableDescriptorBuilder.newBuilder(tableName) .setColumnFamily(ColumnFamilyDescriptorBuilder.of("info")) .build(); admin.createTable(desc); } // 插入数据 try (Table table = conn.getTable(tableName)) { Put put = new Put(Bytes.toBytes("002")); put.addColumn(Bytes.toBytes("info"), Bytes.toBytes("name"), Bytes.toBytes("lisi")); table.put(put); // 查询 Get get = new Get(Bytes.toBytes("002")); Result result = table.get(get); byte[] value = result.getValue(Bytes.toBytes("info"), Bytes.toBytes("name")); System.out.println("name = " + Bytes.toString(value)); } } } }

逻辑说明:Connection是重量级对象,应该复用而不是每次新建,示例里用 try-with-resources 保证关闭。Admin负责建表删表,Table负责读写。Put的 rowkey 是字节数组,实际项目里通常用Bytes.toBytes转换。参数说明:hbase.zookeeper.quorum必须和hbase-site.xml一致,否则客户端找不到集群;hbase.zookeeper.property.clientPort默认 2181,改过端口要同步。

编译时需要把 HBase 的 jar 加进 classpath:

javac -classpath $(hbase classpath) -d classes HBaseDemo.java java -classpath $(hbase classpath):classes HBaseDemo

5. 避坑与排查:伪分布式环境里最容易翻车的 5 个点

5.1 格式化两次导致 DataNode 起不来

现象:start-dfs.sh后jps只有 NameNode,没有 DataNode,日志里报Incompatible clusterIDs。原因:重复执行hdfs namenode -format,NameNode 的 clusterID 变了,DataNode 还记着旧的。解决:停掉所有进程,删除dfs.namenode.name.dir和dfs.datanode.data.dir下的所有数据,重新格式化一次,再启动。记住格式化只做一次。

5.2 副本数设成 3 导致文件一直处于 under-replicated

现象:hdfs dfsadmin -report显示大量 under-replicated blocks,写入慢。原因:伪分布式只有一个 DataNode,dfs.replication设成 3 时永远凑不齐副本。解决:把dfs.replication改成 1,或者用hdfs dfs -setrep -w 1 /path对已有文件降副本。

5.3 MapReduce 作业卡在 shuffle 阶段

现象:map 100%,reduce 一直 0%,日志里Connection refused或Too many fetch failures。原因:yarn-site.xml的yarn.nodemanager.aux-services没配mapreduce_shuffle,或者 NodeManager 没起来。解决:检查yarn-site.xml,确认aux-services配了,重启 YARN;再看jps有没有 NodeManager。

5.4 HBase 启动后 HMaster 秒退

现象:start-hbase.sh后jps里 HMaster 一闪而过,日志报Connection refused连 ZooKeeper。原因:hbase.zookeeper.quorum配的地址不对,或者 ZooKeeper 没启动。解决:先确认 ZooKeeper 在 2181 端口监听,HBASE_MANAGES_ZK设 false 时必须有外部 ZK;如果不想装 ZK,设 true 让 HBase 自带。

5.5 Java API 连 HBase 报找不到 ZooKeeper

现象:本地 IDE 里跑 Java 程序,报KeeperErrorCode = ConnectionLoss。原因:客户端用的hbase-site.xml和集群不一致,或者没把hbase-site.xml放进 classpath。解决:把集群的hbase-site.xml复制到项目的resources目录,或者代码里显式conf.set所有关键参数。另外确认本机 hosts 里localhost解析正常。

6. 进阶技巧:用 distcp 做集群间数据迁移和验证

环境跑通之后,真正体现功力的是数据迁移。Hadoop 自带的distcp是最可靠的工具,它底层用 MapReduce 做并行拷贝,比hdfs dfs -cp快得多,而且能跨集群。基本用法:

# 同集群内拷贝 hadoop distcp hdfs://src-cluster:9000/data/input hdfs://dst-cluster:9000/data/input # 跨集群拷贝,指定带宽和并发 hadoop distcp -m 20 -bandwidth 100 \ hdfs://src-cluster:9000/data/big hdfs://dst-cluster:9000/data/big

参数说明:-m指定 map 数,也就是并发拷贝的线程数,默认 20,小文件多时可以调大;-bandwidth限制每个 map 的带宽(MB/s),避免打满网络;-update只拷贝源和目标不一致的文件,适合增量同步;-delete删除目标端多余的文件,让目标和源完全一致,用之前一定要确认,删错了没有后悔药。

一个我踩过的坑:跨集群 distcp 时,两个集群的 Hadoop 版本不一致,报Protocol mismatch。解决办法是用目标集群的 distcp 去拉源集群的数据,或者用-Dmapreduce.job.hdfs-servers指定兼容的协议。另外 distcp 默认不保留 block 大小和副本数,需要加-p保留权限、-pb保留 block 大小。

验证迁移是否完整,别只看命令返回 0。我的习惯是迁移后跑一次hdfs dfs -count对比源和目标的文件数和总大小,再用hdfs dfs -checksum抽查几个大文件的校验和。如果文件数对不上,多半是有文件在迁移过程中被写入,这时候用-update再跑一次补齐。

最后说个习惯:每次改完*-site.xml,我都会先hdfs dfsadmin -refreshNodes或重启对应服务,再用jps和 Web UI 双重确认,不靠猜。Hadoop 这套东西,玄学问题多半是配置没对齐,日志里其实都写着,只是你没翻到那一行。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询