4. 分布式存储HDFS:读写流程、Shell命令操作、Java API编程、数据块与副本机制
好,咱们今天聊聊HDFS。说实话,这是整个大数据生态的基石。你想想看,没有分布式存储,后面那些Spark、Flink跑什么呢?我当年刚接触大数据时,第一个被震撼到的就是HDFS——原来文件还能这么存。
4.1 数据块与副本机制:HDFS的命根子
先讲最核心的概念:数据块和副本。这是HDFS设计的灵魂。
传统文件系统里,一个文件就是一个整体。但HDFS不一样。它会把文件切成一块一块的,默认每块128MB。为什么是128MB?嗯,早期Hadoop版本是64MB,后来磁盘和网络都快了,就翻倍了。我个人习惯在生产环境里保持默认值,除非有特殊场景。
每个数据块默认存3份副本。这个3是怎么来的?其实是个权衡。少了怕丢数据,多了浪费存储。我在项目中遇到过,有个同事为了省空间把副本数改成2,结果一台机器挂了,数据恢复时差点出问题。所以啊,除非你特别清楚自己在做什么,否则别动这个3。
核心要点:
- 数据块大小:默认128MB,可配置
- 副本因子:默认3,保证数据可靠性
- 副本放置策略:机架感知,保证高可用
副本怎么放?这里有个讲究。HDFS会尽量把副本放在不同的机架上。比如第一个副本放在本地节点,第二个放在同机架另一个节点,第三个放在不同机架。这样就算整个机架断电,数据还在。说白了,就是用空间换安全。
4.2 HDFS读写流程:数据是怎么流动的
理解了数据块,咱们看看读写流程。我画了个图,帮你理清思路。
写流程:
- 客户端向NameNode发起写请求
- NameNode检查权限和空间,返回可用的DataNode列表
- 客户端将数据块依次写入第一个DataNode
- 第一个DataNode自动将数据复制到第二个,第二个再复制到第三个
- 所有副本写完后,客户端通知NameNode完成
这里有个坑。我曾经遇到过网络抖动导致写超时的情况。客户端写数据时,如果某个DataNode挂了,它会自动重试下一个。但如果你网络不稳定,建议调大超时参数。具体怎么调?后面讲配置时再说。
读流程:
- 客户端问NameNode:我要读这个文件,数据在哪?
- NameNode返回数据块的位置信息(哪个DataNode上有哪些块)
- 客户端直接连接最近的DataNode读取数据
- 如果某个DataNode挂了,自动换一个
你发现没?读流程比写流程简单多了。而且客户端是直接从DataNode读数据,NameNode不参与数据传输。这样设计就是为了避免NameNode成为瓶颈。
4.3 Shell命令操作:日常必备技能
好了,理论讲完了,咱们上手实操。HDFS的Shell命令跟Linux命令很像,但前面要加个hdfs dfs。我刚开始用的时候老记混,后来总结了个规律:把hdfs dfs当成Linux的ls、cp的前缀就行。
我的习惯: 把常用的HDFS命令写了个别名,比如 alias hls='hdfs dfs -ls',省事不少。
常用命令一览:
| 操作 | 命令 | 说明 |
|---|---|---|
| 查看目录 | hdfs dfs -ls /path |
类似Linux的ls |
| 创建目录 | hdfs dfs -mkdir /path |
支持-p递归创建 |
| 上传文件 | hdfs dfs -put localfile /hdfspath |
从本地上传到HDFS |
| 下载文件 | hdfs dfs -get /hdfspath localfile |
从HDFS下载到本地 |
| 查看文件内容 | hdfs dfs -cat /path/file |
直接输出到终端 |
| 删除文件 | hdfs dfs -rm /path/file |
支持-r递归删除 |
| 查看块信息 | hdfs fsck /path/file -files -blocks |
查看文件的数据块分布 |
举个例子,上传一个日志文件:
# 先看看本地文件
ls -lh access.log
# 输出:-rw-r--r-- 1 user user 256M Jan 15 10:00 access.log
# 上传到HDFS
hdfs dfs -put access.log /user/logs/
# 验证一下
hdfs dfs -ls /user/logs/
# 输出:Found 1 items
# -rw-r--r-- 3 user supergroup 256M 2024-01-15 10:01 /user/logs/access.log
# 看看这个文件占了多少数据块
hdfs fsck /user/logs/access.log -files -blocks
注意: 上传大文件时,HDFS会自动分块。你看到的是256M的文件,实际上它被分成了2个128M的数据块。用fsck命令就能看到每个块分布在哪些DataNode上。
4.4 Java API编程:用代码操作HDFS
Shell命令适合日常运维,但真正的数据处理还得靠代码。Java API是操作HDFS最正统的方式。我当年写第一个HDFS程序时,踩了个坑——忘记关流,结果文件一直显示写入中。后来养成了习惯,所有流操作都用try-with-resources。
先加依赖:
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>3.3.4</version>
</dependency>
核心代码示例:
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.*;
import java.io.*;
public class HdfsDemo {
public static void main(String[] args) throws Exception {
// 创建配置对象
Configuration conf = new Configuration();
// 设置HDFS地址
conf.set("fs.defaultFS", "hdfs://localhost:9000");
// 获取文件系统对象
try (FileSystem fs = FileSystem.get(conf)) {
// 1. 创建目录
Path dir = new Path("/user/demo");
if (!fs.exists(dir)) {
fs.mkdirs(dir);
System.out.println("目录创建成功");
}
// 2. 上传文件
Path localFile = new Path("local.txt");
Path hdfsFile = new Path("/user/demo/remote.txt");
fs.copyFromLocalFile(localFile, hdfsFile);
System.out.println("文件上传成功");
// 3. 读取文件内容
try (FSDataInputStream in = fs.open(hdfsFile)) {
BufferedReader reader = new BufferedReader(
new InputStreamReader(in));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
}
// 4. 查看文件块信息
FileStatus status = fs.getFileStatus(hdfsFile);
BlockLocation[] blocks = fs.getFileBlockLocations(
status, 0, status.getLen());
for (BlockLocation block : blocks) {
System.out.println("块偏移: " + block.getOffset() +
", 长度: " + block.getLength() +
", 所在节点: " + String.join(",", block.getHosts()));
}
}
}
}
这段代码涵盖了最常用的操作:创建目录、上传文件、读取内容、查看块信息。你想想看,实际项目中无非就是这些操作的组合。
避坑指南: 我曾经在生产环境遇到过一个问题——客户端和集群的Hadoop版本不一致,导致RPC通信失败。所以啊,hadoop-client的版本一定要和集群版本保持一致。
4.5 实战经验总结
最后,分享几个我在项目中积累的经验:
- 小文件问题: HDFS不适合存大量小文件。每个文件都要占用NameNode内存,几百万个小文件能把NameNode撑爆。我一般建议用SequenceFile或合并工具把小文件合并成大文件。
- 副本数调整: 临时数据可以设成2副本,重要数据设成3副本。但别设成1,除非你做好了数据丢失的心理准备。
- 数据均衡: 新加节点后,用
hdfs balancer做数据均衡。我习惯在凌晨跑,因为比较耗资源。 - 安全模式: NameNode启动时会进入安全模式,此时不能写数据。等它检查完数据块完整性后自动退出。如果一直卡在安全模式,多半是有数据块损坏了。
好了,HDFS的核心内容就这些。记住一句话:HDFS是为大文件、流式读取设计的,别拿它当普通文件系统用。理解了这一点,后面学Hive、HBase时就会轻松很多。
公众号:蓝海资料掘金营,微信deep3321