尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

HDFS常用操作与读写流程:从Shell命令到Java API实战

HDFS常用操作与读写流程:从Shell命令到Java API实战 简介实验二“熟悉常用的HDFS操作”配套学习资料包面向正在学习大数据技术原理与应用课程的学生特别是进入Hadoop生态入门阶段、需要动手掌握分布式文件系统操作的读者。资源内容围绕HDFS在Hadoop体系中的角色展开完整覆盖实验目的、实验平台配置要求并通过Shell命令与Java API两条主线展示文件上传、存在性检查、追加与覆盖等常用操作适合作为实验报告参考、作业模板或课后复习提纲。资料包共1个docx文档压缩包大小约3.4MB文档结构清晰、可直接编辑便于按本机环境调整后复用。已有10211人学习下载学习热度较高。这份文档以完成度较高的实验报告为例给出了Ubuntu/Hadoop/JDK/Eclipse等环境说明、关键Java代码如CopyFromLocalFile类和Shell命令的对应实现可帮助读者快速梳理HDFS操作的完整流程对照自身实验查漏补缺也为后续MapReduce、HBase等Hadoop生态组件学习打下基础。1. 熟悉常用的HDFS操作到底在练什么从“能跑命令”到“懂读写流程”如果你刚接触大数据打开实验指导书看到“实验二熟悉常用的HDFS操作”多半觉得这只是一堆命令的堆砌ls、mkdir、put、get、rm……其实这个实验是在为后面所有分布式计算打地基。HDFS是Hadoop生态的存储底座MapReduce、Spark、Flink读写数据都绕不开它。做这个实验你练的不只是“敲几条命令”而是理解文件在分布式系统里怎么存、怎么读、怎么保证可靠以及为什么有些操作会失败。适合正在做大数据课程实验、或者准备HDFS和MapReduce综合实训的人。把这层逻辑看清楚后面调Yarn、查任务、处理数据倾斜都会顺很多。2. HDFS常用Shell命令从文件增删改查到目录配额的一线用法2.1 先理解HDFS的文件视图目录、块和副本HDFS对外暴露的是一个树形文件系统路径格式和Linux很像比如/user/hadoop/data。但内部存储完全不同一个文件会被切分成若干块block每个块默认128MB再复制成多份放到不同节点上。所以你执行hadoop fs -ls看到的目录只是“逻辑视图”真正物理上块分布在哪需要另查。这个实验里最该先建立的认知是HDFS不适合存大量小文件也不适合频繁修改。因为每个文件、目录、块都要由NameNode记录元数据小文件多了会压垮内存而修改文件通常要先删除再写没有随机的写操作。理解了这一点你就明白为什么很多实验里第一步都是mkdir建目录而不是直接往根目录塞一堆文件。接着要熟悉命令前缀。HDFS命令通常有三种写法hadoop fs、hdfs dfs、hdfs dfsadmin。hadoop fs和hdfs dfs在大多数发行版里等价都是面向普通用户的文件操作dfsadmin是管理命令用来更新元数据、查看安全模式、滚动日志等。做常用操作时建议统一用hdfs dfs -命令因为hdfs dfs在HDFS HA环境下少一层代理出问题时日志也直接。2.2 高频命令ls、mkdir、put、get、rm、cat与参数解释最常用的一套命令我直接在实验环境里跑给你看# 查看根目录下的文件与目录-R可以递归列出子目录 hdfs dfs -ls / hdfs dfs -ls -R /user # 创建目录-p类似Linux的mkdir -p能自动创建父目录 hdfs dfs -mkdir /user/hadoop hdfs dfs -mkdir -p /user/hadoop/input # 把本地文件上传到HDFS hdfs dfs -put /etc/hosts /user/hadoop/input/hosts.txt hdfs dfs -moveFromLocal /tmp/import.log /user/hadoop/input/import.log # 下载到本地-f表示如果本地已存在则覆盖 hdfs dfs -get /user/hadoop/input/hosts.txt /tmp/hosts_backup.txt hdfs dfs -get -f /user/hadoop/input/hosts.txt /tmp/hosts_backup.txt # 查看文件内容适合小文本大文件别直接用cat hdfs dfs -cat /user/hadoop/input/hosts.txt hdfs dfs -tail /user/hadoop/input/hosts.txt # 删除文件-r删除目录-skipTrash会直接清空而不进回收站 hdfs dfs -rm /user/hadoop/input/hosts.txt hdfs dfs -rm -r /user/hadoop/input hdfs dfs -rm -r -skipTrash /user/hadoop/tmp这些命令对应hdfs常用命令的核心集合。-put和-get是最容易出错的-put会把本地文件原样传到HDFS不会自动合并如果目标路径已经存在默认会生成副本比如-put ./a.txt /user/hadoop/input/会在input下再建一个a.txt而-put ./a.txt /user/hadoop/input/a.txt则直接覆盖同名文件。-get同理最好显式写目标文件名。-cat只适合读小文件如果文件有几个GB输出会淹没终端还会占用大量客户端内存。要看大文件就配合-tail看尾部或者用后面的Java API写一个“读前N行”的小工具。另一个容易被忽略的选项是-D它可以在命令执行时临时指定参数比如hdfs dfs -Ddfs.replication1 -put bigdata.tar.gz /user/hadoop/data/这条命令只对这个文件生效副本数为1适合临时上传一次性测试数据不修改集群配置。2.3 管理类命令fsck、du、setrep、quota与使用场景实验要求“熟悉常用操作”很多同学做完ls、put就认为结束了。但真正的落地操作还包括检查文件健康度、调整副本、管理配额。这几条命令在后续HDFS和MapReduce综合实训里很有用因为任务失败常和副本不足、目录空间超限有关。# 查看文件块分布与副本健康状态输出每个块的所在节点和副本数 hdfs fsck /user/hadoop/input/hosts.txt -files -blocks -locations # 统计目录里每个文件的大小-h显示人类可读单位 hdfs dfs -du -h /user/hadoop # 查看整个目录的占用类似Linux的du -sh hdfs dfs -du -s -h /user/hadoop # 调整文件副本数为2-R表示递归调整目录下所有文件 hdfs dfs -setrep -R 2 /user/hadoop/input # 给目录设置文件数配额和空间配额单位是字节 hdfs dfsadmin -setQuota 100000 /user/hadoop/input hdfs dfsadmin -setSpaceQuota 10g /user/hadoop/input # 清除配额 hdfs dfsadmin -clrQuota /user/hadoop/input hdfs dfsadmin -clrSpaceQuota /user/hadoop/inputfsck是我排查问题的第一工具。当实验报告里要求“验证副本数是否达到3”不要只信-setrep的输出要用fsck看实际状态。注意fsck输出会把块路径和DataNode地址打出来网上很多抄来的实验报告就是从这里截图但截图时记得打码本机IP。-setrep只是提交了一个副本调整请求NameNode会异步让DataNode执行复制或删除。刚执行完立刻fsck可能看到副本数还没到位这不叫翻车是异步机制在起作用。等待几十秒再查。配额是实验里很少考、但线上必用的功能。setQuota限制目录下文件/目录总个数setSpaceQuota限制字节数。给用户目录设置配额能防止某个人写满磁盘把整个集群拖挂。注意空间配额是“字节数逻辑占用”包含副本占用。比如实际文件1GB、副本3份配额会显示为3GB。3. 用Java API操作HDFS从环境变量到文件上传下载的完整实现3.1 引入依赖与初始化FileSystemShell命令能完成日常操作但很多业务场景需要在程序里读写HDFS。实验二通常要求“编程实现文件操作”绕不开Java API。这块的坑主要在依赖版本和客户端配置上。我用Maven工程为例Hadoop版本选和集群一致的版本。这里以Hadoop 3.3.x为例dependency groupIdorg.apache.hadoop/groupId artifactIdhadoop-client/artifactId version3.3.6/version /dependency如果你的集群是CDH或HDP发行版建议用对应发行版的客户端包比如hadoop-client-api和hadoop-client-runtime。别混用不同大版本的客户端和服务端否则会报协议不兼容。初始化FileSystem是第一步。常见的做法有两种import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; import java.net.URI; public class HdfsClient { public static void main(String[] args) throws Exception { Configuration conf new Configuration(); // 显式指定NameNode地址如果在集群内跑可以用默认core-site.xml conf.set(fs.defaultFS, hdfs://namenode:8020); // 指定客户端用户身份HDFS会按这个身份做权限检查 System.setProperty(HADOOP_USER_NAME, hadoop); // 优先走HADOOP_CONF_DIR里的配置让客户端知道网络拓扑 conf.addResource(new Path(/etc/hadoop/core-site.xml)); conf.addResource(new Path(/etc/hadoop/hdfs-site.xml)); FileSystem fs FileSystem.get(URI.create(hdfs://namenode:8020), conf); System.out.println(Connected: fs.getUri()); fs.close(); } }这里有两个非常关键的参数说明。HADOOP_USER_NAME是指定“以谁的身份去访问HDFS”不设置的话客户端会用本机系统用户名比如你在自己电脑上跑用户名是admin就会因为HDFS上没有该用户而报Permission denied。conf.addResource是加载集群的配置让客户端知道副本数、块大小、短路读开关等参数如果不加默认值可能和集群不一致比如本地默认块大小128MB集群设了256MB上传会产生额外分块。3.2 实现文件的创建、上传、下载和删除下面是完整的上传、下载、删除操作我把注释写在关键位置import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FSDataInputStream; import org.apache.hadoop.fs.FSDataOutputStream; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; public class HdfsFileOps { public static void main(String[] args) throws Exception { Configuration conf new Configuration(); conf.set(fs.defaultFS, hdfs://namenode:8020); System.setProperty(HADOOP_USER_NAME, hadoop); FileSystem fs FileSystem.get(conf); // 1. 创建文件并写入内容overwrite参数为true表示覆盖已存在文件 Path writePath new Path(/user/hadoop/api_demo.txt); FSDataOutputStream out fs.create(writePath, true); out.writeUTF(hello hdfs from java api\n); out.flush(); out.close(); // 2. 上传本地文件到HDFS使用标准文件流拷贝 Path localPath new Path(/tmp/local_data.csv); Path remotePath new Path(/user/hadoop/upload/data.csv); fs.copyFromLocalFile(false, true, localPath, remotePath); // 参数一delSrcfalse保留本地文件参数二overwritetrue覆盖远端 // 3. 下载HDFS文件到本地 Path remoteFile new Path(/user/hadoop/api_demo.txt); Path localFile new Path(/tmp/api_demo_download.txt); fs.copyToLocalFile(false, remoteFile, localFile); // 4. 删除 boolean deleted fs.delete(new Path(/user/hadoop/api_demo.txt), false); System.out.println(deleted: deleted); // 5. 判断路径是否存在 boolean exists fs.exists(new Path(/user/hadoop)); System.out.println(exists: exists); fs.close(); } }逻辑说明fs.create返回FSDataOutputStream它是带缓冲的流写完必须flushclose否则最后一个块可能没落盘。copyFromLocalFile是上层封装内部通过FileUtil.copy实现适合整文件拷贝如果想边读边处理可以用IOUtils。delete的第二个参数是是否递归删除非空目录必须传true。参数说明fs.defaultFS决定客户端连哪个NameNodeHADOOP_USER_NAME是权限伪装copyFromLocalFile的四个参数重载里第一个delSrc是否删本地源文件第二个overwrite是否覆盖远端同名文件。要注意如果你传入的remotePath是个已存在的目录文件会被放到目录内部并保留原文件名这个行为和Shell的-put一致。3.3 用IOUtils简化流拷贝与性能参数调整上面代码用FSDataOutputStream写死字符串没问题但处理大文件时建议直接使用Hadoop自带的IOUtils.copyBytes它内部用8KB缓冲区循环读写比你自己写while循环更安全还自动关了流import org.apache.hadoop.fs.FSDataInputStream; import org.apache.hadoop.fs.FSDataOutputStream; import org.apache.hadoop.io.IOUtils; FSDataInputStream in fs.open(new Path(/user/hadoop/bigfile.bin)); FSDataOutputStream out fs.create(new Path(/user/hadoop/bigfile_copy.bin), true); // 第三个参数4096是缓冲区字节数第四个参数true表示结束是否关闭流 IOUtils.copyBytes(in, out, 4096, true);这里有个常见误解IOUtils.copyBytes的缓冲区不是HDFS写缓冲区只是网络流的拷贝缓冲区。真正影响写入性能的是fs.create时传入的bufferSize和replication。更精细的写法是FSDataOutputStream out fs.create(remotePath, new FsPermission(755), true, // overwrite 4096, // bufferSize建议与io.file.buffer.size一致 (short) 2, // 副本数 128L * 1024 * 1024, // 块大小128MB null); // 校验和后台线程参数说明FsPermission(755)指定文件权限是rwxr-xr-x不写默认按umask生成bufferSize是客户端写缓冲太小会频繁发包太大浪费客户端内存线上常用128KB或256KBblockSize只在创建新文件时生效已有文件不能修改。这些对应HDFS编程实践里“自定义参数上传”这类要求。4. HDFS读写流程与关键参数搞懂数据在节点间怎么流动4.1 写入流程客户端、NameNode、DataNode的协作很多同学命令敲得溜但一问“put一个文件到HDFS中间发生了什么”就卡住。这个实验的价值就在这里把读写流程看懂后面写MapReduce时才能理解数据本地性和网络开销。写入流程分三阶段客户端调用create向NameNode发起RPC请求。NameNode检查权限和目录是否存在然后返回一个可写入的DataNode列表。客户端把文件按块大小切分得到第一个块后向NameNode申请块的目标节点。NameNode按机架感知策略返回一组DataNode通常是2个同机架、1个不同机架用来平衡容错与带宽。客户端以pipeline方式把数据发给第一个DataNode第一个再转发给第二个第二个转发给第三个。每写一个chunk默认512字节会带校验和下游节点返回ack后继续写下一段。所有块写完后客户端调用closeNameNode收到完成通知并更新元数据。这个流程解释了为什么写HDFS比写本地文件慢每个写操作要等最慢的那个副本确认。如果你的实验环境只有1个DataNode那么日志里看不到pipeline尾部信息但你会看到同步开销依然存在。4.2 读取流程就近读与短路读读取比写入简单客户端请求NameNode文件对应哪些块、块在哪些DataNode然后直接连DataNode去读。HDFS 3.x默认开启短路读short-circuit read即客户端和DataNode在同一台机器时绕过TCP和DataNode直接读本地文件。这个特性对MapReduce的Map任务读HDFS文件非常有效因为Map任务通常调度到数据所在节点。短路读在hdfs-site.xml里的配置由两部分组成property namedfs.client.read.shortcircuit/name valuetrue/value /property property namedfs.domain.socket.path/name value/var/lib/hadoop-hdfs/dn_socket/value /property注意dfs.domain.socket.path在每台DataNode上必须存在且有写权限。如果这个路径配错启动时日志里能看到“Failed to open local socket”之类的记录但不会导致DataNode退出只是短路读失效。很多课程集群默认没开短路读你不用改也没关系但如果你到了HDFS和MapReduce综合实训阶段发现任务处理小文件慢先检查这个开关。4.3 必调参数块大小、副本数、缓冲区与校验做这个实验时你会看到配置文件里一堆dfs.开头的参数。我不建议背但以下这几个一定要亲手调一次并观察效果。参数默认值调整场景注意事项dfs.blocksize128MB大文件调大至256MB减少NameNode元数据小文件多则不调只对新建文件生效dfs.replication3测试环境调2可省空间但容错下降可在命令或API里临时覆盖dfs.client.block.write.buffer由客户端指定写入大文件时调大减少TCP小包与io.file.buffer.size配合dfs.namenode.handler.count10NameNode并发高时调大但别超过64改完要重启NameNodedfs.datanode.data.dir无多磁盘时配置多目录分散写入压力目录权限必须是属主drwxr-x---一个常见误区是dfs.replication设置为1是不是等于没冗错对实验来说是的但生产环境不可能允许。另一个坑是dfs.blocksize设为64MB反而会让小文件更多NameNode内存占用反而上升。我一般会在实验报告里补一段验证命令上传一个200MB的随机文件然后用hdfs fsck看分块情况dd if/dev/urandom of/tmp/test_200m.bin bs1M count200 hdfs dfs -Ddfs.block.size134217728 -put /tmp/test_200m.bin /user/hadoop/test_200m.bin hdfs fsck /user/hadoop/test_200m.bin -files -blocks你会看到这个文件被分成2个块而不是在物理上连续存储。这就是分布式存储的基本形态。5. HDFS操作避坑权限、写入失败、小文件过载的排查记录5.1 现象Permission denied——权限传递与HDFS用户映射实验里最常见的就是put时报Permission denied。原因是在你本地执行命令的用户名例如root或admin在HDFS上不存在或没有写权限。HDFS不像Linux有su命令它信任客户端提供的身份默认取操作系统用户名。你把HADOOP_USER_NAME设成集群里的用户或者用hdfs dfs时加-D来模拟hdfs dfs -Dfs.defaultFS... -put file /user/hadoop/解决方式是三步第一用klist确认是否已用Kerberos认证非安全集群不用这一步。第二whoami看当前系统用户。第三hdfs dfs -mkdir /user/当前用户名并授权。如果只是做实验最简单的是在代码或命令前加export HADOOP_USER_NAMEhadoop。5.2 现象写入超时或块副本不足——网络与DataNode下线原因我遇到过这样的翻车-put一个2GB文件进度条到70%卡住最后抛SocketTimeoutException。查日志发现DataNode之间带宽只有百兆而副本数设了3流式转发在第二个DataNode上超时。解决方法是临时降低副本数hdfs dfs -Ddfs.replication1 -put /tmp/bigfile .bak /如果文件已经传了一部分先删除残留块再调整副本数重传。另外注意hdfs dfsadmin -report查看DataNode状态如果某个DataNode显示Dead副本数就一直满足不了。这种情况先检查该节点的磁盘空间和dfs.datanode.data.dir目录权限然后重启DataNode服务。5.3 现象NameNode进入安全模式——什么时候该等什么时候该干预刚启动集群执行文件操作时报Name node is in safe mode。这通常是NameNode在加载元数据并等待DataNode上报数据块正常会自己退出。你要做的是等待不是立刻敲hdfs dfsadmin -safemode leave。只有在集群长时间卡在安全模式并且确认所有DataNode都活着时才手动离开。强制离开后的第一个动作是hdfs fsck检查是否丢块否则可能只读到残缺数据。# 查看当前安全模式状态 hdfs dfsadmin -safemode get # 等待安全模式退出有数字提示还剩多少块 hdfs dfsadmin -safemode wait # 只在异常时手动退出 hdfs dfsadmin -safemode leave安全模式里只能执行读操作不能写入。如果你的实验脚本里有批量写入最好加一个循环等待安全模式退出的逻辑而不是死等。5.4 现象集群明明有空间但写不进去——小文件与NameNode内存另一种常见失败是put一个几千个文件的小目录报org.apache.hadoop.hdfs.protocol.FSConstants$DatanodeNotExistException或No space left on device但hdfs dfs -df显示空间剩很多。这大概率是NameNode内存不足无法为更多文件分配元数据。HDFS中每个文件、目录和块大约占用NameNode 150字节堆内内存加额外对象开销。10万个文件就可能吃掉几百MB。解决方向是合并小文件用hdfs fsck找出小文件然后写脚本合并成SequenceFile或ORC。如果一定要保留小文件调大dfs.namenode.handler.count只是治标真正的问题是存储模式不适合小文件场景。6. 把实验做出价值感用脚本封装日常检查顺便验证读写流程做“熟悉常用HDFS操作”实验最后一步不应该是交一份截图而是把手动敲的命令沉淀成可复用的检查脚本。我给自己写了一套极简巡检脚本每次部署完新集群先跑一遍#!/bin/bash # HDFS基础巡检状态、节点、安全模式、块健康、剩余空间 echo NameNode 状态 hdfs dfsadmin -report | head -n 20 echo 安全模式 hdfs dfsadmin -safemode get echo 根目录大小 hdfs dfs -du -s -h / echo 检查所有块健康 hdfs fsck / -files -blocks 21 | tail -n 5这套脚本的好处是能暴露大多数“基础没搭好”的问题。如果fsck最后一行出现“Number of under-replicated blocks 0”说明副本全部健康出现非0数值就要等待复制完成或检查节点。另一个值得做的进阶验证用time监控一次上传来观察读写流程的耗时特征/usr/bin/time -v hdfs dfs -Ddfs.blocksize67108864 -put /tmp/test_64m.bin /user/hadoop/test_64m.bin你会看到用户CPU时间很低但系统时间和等待I/O占比明显这是因为HDFS客户端在等待DataNode的ack。把块大小调成64MB跑一次再调成128MB跑一次对比文件切块数量和耗时就能直接体会“块大小不是越大越好”。最后补一招验证短路读是否生效。在开启短路读的集群上从DataNode本机用Java API连续读一个大文件两次第一次会消耗一秒左右建连接第二次明显变快如果从另一台客户端读耗时差距没那么明显。你也可以在DataNode的日志里搜shortcircuit如果看到Successfully opened file就说明生效。我在自己搭的实验环境里把这段脚本存成了check_hdfs.sh每次跑综合实训前先执行能省下不少排查时间。个人习惯是把hdfs dfsadmin -safemode wait放进所有需要自动写入的脚本开头因为不这么做你会在凌晨提交作业时收到一条“Name node is in safe mode”的教训。实验不只是为了过报告把这一套常用的HDFS操作固化成肌肉记忆去面对真实的分布式任务时才不会虚。希望帮到你。本文还有配套的精品资源点击获取
返回列表