1. 为什么要单独写一篇HDFS Shell命令:这东西到底解决什么问题
如果你刚接触Hadoop,大概率会对着两套命令发懵:一套是hadoop fs,一套是hdfs dfs,输入help出来的参数看着都差不多,网上的博客各写各的,一会儿用这个前缀一会儿用那个前缀,明明贴了同样的ls、put,在有的机器上却能跑通、有的直接报错。
先说结论:HDFS Shell命令是操作Hadoop分布式文件系统最重要、也最容易被低估的入口。你可以在Java API里写一堆代码去操作文件,可以在Web UI上点点鼠标看文件目录,但真正到了排障、迁移、批处理、日常运维的场景,所有人都会回到命令行——因为它足够直接、足够快,也足够强大。一个hadoop fs -put就能把本地文件推到分布式集群,一条hdfs dfs -setrep就能调整副本因子,一条hdfs dfsadmin -report就能看清整个集群的健康状态。这些操作如果换做API实现,少的几十行代码,多的能写一个模块。
这篇文章要做的,就是不绕弯子地把HDFS Shell命令的常用部分讲透。我默认你手里已经有一个能跑的Hadoop环境——不管是单机伪分布式、三节点小集群,还是公司里几百台机器的大集群,命令本身是通用的。内容会分成几个层面:命令的定位和体系、文件操作、目录查询、权限控制、维护类命令,以及一些文档里通常不会写、但实际工作中一定会踩的坑。适合的人群很明确:刚入门大数据、想系统梳理HDFS命令的开发者;日常需要做数据巡检和文件维护的运维同学;以及那些准备面试、想搞清楚命令底层逻辑的人。这篇不是速查表式的罗列,而是把"为什么这样用""底层发生了什么"一并讲清楚,这样你换到任何发行版、任何版本,都不至于慌。
2. 先把命令体系理清:hadoop fs、hdfs dfs、hdfs dfsadmin 别再用混
2.1 三个前缀的来龙去脉和使用边界
我第一次接触Hadoop的时候,把hadoop fs -ls /和hdfs dfs -ls /都执行了一遍,输出结果一样,就以为两者完全等价。后来在旧版本的CDH集群上排查问题,发现有的机器居然提示hdfs dfs找不到,查了之后才算把这块搞清楚。
从Hadoop的演进历史来看,hadoop fs是一个更通用的文件系统入口,它背后调用的是FileSystem这个抽象类,这意味着它不光能操作HDFS,还可以操作本地文件系统、以及Hadoop支持的其他文件系统。而hdfs dfs是专门对接HDFS的入口,调用的是DistributedFileSystem,所以你问两者的关系,最准确的说法是:hdfs dfs是hadoop fs的子集,做HDFS操作时两者基本等价,但hadoop fs的设计面更宽。
企业生产环境里,大多数人不会刻意区分这两个前缀,但我的建议是:如果你只操作HDFS,统一使用hdfs dfs更语义化,看着也清楚;如果脚本里可能涉及本地文件系统与HDFS的混合操作,用hadoop fs更保险。至于那些以-开头的子命令,比如-ls、-mkdir、-cat,本质上是一个大而全的操作集合,没必要背,用的时候看一眼help就行。
2.2 dfsadmin、fsck、balancer 这类命令不属于日常文件操作
很多初学者会以为hdfs dfsadmin也是"文件操作"类命令,其实它们是管理命令的范畴,作用对象不是具体的文件,而是集群本身。hdfs dfsadmin -report查看DataNode的在线状态、存储容量、块信息;hdfs dfsadmin -safemode enter手动进入安全模式做维护;hdfs balancer触发数据均衡。这些命令在日常巡检中非常重要,但它们和hdfs dfs是两条线,参数的风格、风险等级完全不一样。
我在带团队的时候,会明确画一条线:所有涉及单个文件、目录、权限、配额的操作走hdfs dfs;涉及集群节点、副本、均衡、安全模式的操作走dfsadmin或专门的工具命令。这样脚本的职责边界清晰,出了问题也好排查是谁动了集群的状态。
顺手验证命令可用性:输入
hdfs dfs -help,能列出全部子命令就说明基础环境没问题;再输入hdfs dfsadmin -help,检查管理命令是否可用。两个都通了,后面所有示例你都可以直接上手。
2.3 Linux命令与HDFS命令的联想记忆法
HDFS Shell命令最大的特点就是"像Linux命令,但不完全是"。比如ls、cat、mkdir、rm、mv、cp、chmod、chown这些名字和Linux完全一致,但参数略有不同,而且路径必须是HDFS上的路径(默认是hdfs://协议)。这个设计的初衷就是为了降低使用门槛,让有Linux基础的人能快速上手。
不过有两处要注意:一是HDFS没有"当前工作目录"的概念(虽然有cd命令,但不是真正的CD),所以几乎所有路径都建议写绝对路径;二是通配符的处理有时和Linux不一样,比如*在某些场景下需要转义或会被HDFS的路径解析器做额外处理,后面我会专门用一个章节讲这个坑。理解了这个"似Linux非Linux"的定位,你再看任何一条命令,第一反应就不会是死记参数,而是去想"它在Linux里的同名命令是怎么工作的,在HDFS里又改了什么"。
3. 文件操作三板斧:put、get、rm 的生产级用法
3.1 上传文件:-put 的完整语义和常用参数
hdfs dfs -put应该是所有命令里使用频率最高的一个,作用是把本地文件或目录上传到HDFS。基本用法很简单:
hdfs dfs -put /local/data.csv /user/hive/warehouse/这行命令把本地的data.csv放到HDFS的/user/hive/warehouse/目录下。看起来平平无奇,但你需要注意它背后发生了什么:命令执行时,客户端会把文件按块大小(默认128MB,可配置)切分,然后向NameNode发起请求,获得每个块要写入的DataNode列表,再以流式管道的方式把数据写进去。这个过程中,客户端机器本身参与数据传输的调度,所以执行 -put 的节点与集群之间的网络带宽,直接决定了上传速度。
如果你要覆盖上传,注意-f参数:
hdfs dfs -put -f /local/data.csv /user/hive/warehouse/data.csv不带-f时,如果目标路径已存在同名文件,命令会直接报错,这在生产环境里反而是一种保护——避免脚本里误覆盖了重要数据。我见过不少刚入行的同学以为-put会像Linuxcp一样自动覆盖,结果脚本每次跑都报File already exists,然后到处查日志,其实就是少了这个参数。另外,上传目录时,-put会递归把整个目录结构拷进去,行为符合直觉。
3.2 下载文件:-get 与 -copyToLocal 的等价与差异
下载方向对应的是-get,把HDFS上的文件拉回本地:
hdfs dfs -get /user/hive/warehouse/data.csv /local/backup/如果要设置下载时的副本数或忽略某些校验,可以加-p保留文件属性(权限、时间戳等),但日常用得少。大多数时候-get和-copyToLocal可以直接替换,内部实现基本一致,唯一的边际差异在于命令名不同、个别版本里-copyToLocal会有更严格的校验。我的习惯是脚本里统一用-get,因为短,而且所有发行版都支持。
这里有个容易忽略的点:下载大文件时,客户端内存和磁盘都会承压。因为数据同样经过客户端节点流动,如果你在一个磁盘空间很小的跳板机上执行-get一个100GB的文件,机器可能会直接写满。所以生产上建议在目标盘剩余空间足够的节点上执行,或者用-getmerge先把分散的小文件合并再拉取。
3.3 删除操作:-rm、-rmdir、-expunge 千万别乱用
删除命令是生产事故高发区,但HDFS的删除机制其实比Linux更"仁慈"一点。-rm删除文件后,数据会先进入回收站(如果配置了fs.trash.interval的话),而不是立即物理擦除。默认配置下回收站可能没开启,但很多企业会设置一个保留周期,比如24小时,超过这个时间才会彻底清除。所以如果你的集群开启了回收站,删错文件还有后悔药。
# 删除单个文件 hdfs dfs -rm /tmp/test.txt # 递归删除目录 hdfs dfs -rm -r /tmp/dir # 跳过回收站,直接删除(慎用) hdfs dfs -rm -skipTrash /tmp/test.txt-rmdir只能删空目录,适合清理建错的目录结构;-expunge是手动触发清空回收站,物理释放空间。我特别想强调的是:-rm -r -skipTrash这个组合,在运维脚本里尽量不要出现。一旦出现,再配合路径参数的变量为空,后果就是删根目录级别的灾难。这是HDFS领域最典型的事故模式,后面讲避坑时会再展开。
3.4 生产场景举例:日志文件的归档搬迁
把上面几个操作串起来看一个真实场景:每天早上需要把前一天的Nginx日志文件从本地服务器搬迁到HDFS上的/data/logs/2024/目录,同时保留本地7天内的日志用于排查问题。脚本大致长这样:
DATE=$(date +%Y%m%d --date="yesterday") hdfs dfs -mkdir -p /data/logs/$DATE hdfs dfs -put /var/log/nginx/access.$DATE.log /data/logs/$DATE/ # 验证上传后的文件大小与本地一致 LOCAL_SIZE=$(stat -c%s /var/log/nginx/access.$DATE.log) HDFS_SIZE=$(hdfs dfs -stat -size /data/logs/$DATE/access.$DATE.log | sed 's/[^0-9]//g') if [ "$LOCAL_SIZE" -eq "$HDFS_SIZE" ]; then echo "upload ok" fi这里用mkdir -p创建多级目录、用stat -size校验文件大小,全程只用到put、mkdir、stat三个子命令。你会发现实际工作中的大部分操作,组合起来也就是这些基础命令,把每个命令的参数吃透远比记一堆高级命令有用。
4. 目录与信息查询:日常巡检最先用到的命令组合
4.1 ls / du / df:从"有多少文件"到"集群还有多少空间"
查询类命令是运维巡检的主力。ls不光能列目录,加-R能递归列出所有子目录与文件,加-h能人性化显示文件大小,这是我最常用的组合:
hdfs dfs -ls -R -h /data它会输出类似下面这样的排列:
drwxr-xr-x - hdfs hadoop 0 2024-01-15 10:00 /data/logs -rw-r--r-- 2 hdfs hadoop 135.2 M 2024-01-15 10:00 /data/logs/access.log第二列的2表示副本数,默认是3如果集群配置改过就是配置值。如果想看集群存储的整体水位,用hdfs dfs -df -h,它会显示整个HDFS文件系统的容量、已使用量、剩余空间,相比登录每台机器去看磁盘要效率高得多。
4.2 count、stat、du:精确到文件级别的容量分析
当你想统计一个目录下有多少个文件、总大小是多少,count比ls更好用:
hdfs dfs -count /data输出包含四列:目录数、文件数、文件总大小(字节)、路径。这在做数据治理时非常有用,比如你想知道Hive表在HDFS上到底占地多大、碎片文件有多少个,一条count直接给出答案。
stat是查看单个文件详细属性的命令,能输出块大小、权限、修改时间、副本数等。需要注意参数格式比较老派:
hdfs dfs -stat -size -blocksize -replication /data/logs/access.log-du则是显示目录下每个子项的磁盘占用,注意它统计的默认是不含副本的原始大小,加-s显示汇总,想包含副本占用的真实物理空间,需要看-du -h的输出或配合-s理解。这一块非常容易被误解:很多人看到-du的结果比HDFS总容量小,就以为算错了,其实HDFS命令行大多统计的是"逻辑大小",物理占用要乘以副本数(或参考纠删码的奇偶校验比例)。
4.3 配额设置:给目录设定容量硬边界
查询类命令延伸出去,就是配额管理。HDFS允许对目录设置两种配额:文件数量配额(namespace quota)和空间配额(space quota)。适用场景很典型:多租户环境下,每个部门的数据目录必须有上限,否则一个跑飞的Spark任务能把整个集群磁盘塞满。
# 设置 /data/department_a 的文件数量上限为100万个 hdfs dfs -setquota 1000000 /data/department_a # 设置空间配额为2TB hdfs dfs -setSpaceQuota 2T /data/department_a # 查看配额 hdfs dfs -count -q /data/department_acount -q输出里会带上配额信息和当前使用量,如果CURRENT列接近MAX列,就要注意清理数据了。配额这块很多人只在文档里见过、生产里没配过,等真出了“磁盘写满导致整个集群Hive查询全部失败”的事故,再回头配就晚了。我在帮某团队Review集群配置时,第一件事就是检查关键目录有没有配额,没有的立刻补上。
5. 权限控制相关命令:告别Permission denied的方法论
5.1 chmod、chown、chgrp 的基本操作与HDFS特有逻辑
HDFS的权限模型基本沿用POSIX风格,有owner、group、other三个角色,读(r)、写(w)、执行(x)三种权限,用法和Linux的chmod、chown几乎一致:
# 修改权限 hdfs dfs -chmod 750 /data/secure_dir # 递归修改目录下所有文件的属主和属组 hdfs dfs -chown -R hdfs:hadoop /data/secure_dir # 只改属组 hdfs dfs -chgrp -R analytics /data/project但你必须注意一个HDFS独有的特点:执行(x)权限在没有ACL的情况下对文件没有实际意义,但对目录有意义——它决定了你是否能遍历这个目录。这就是为什么有时候你对一个文件有读权限,但ls它的父目录却报Permission denied,通常是父目录缺了x权限。排查这种问题,一条一条ls -d往下看权限就行。
5.2 超级用户、超级组的语义
HDFS的超级用户是可以绕过所有权限检查的存在,它由NameNode配置项dfs.permissions.superusergroup决定,默认是supergroup。注意这里不是指Linux的root,而是指启动NameNode的那个用户,以及supergroup组里的用户。当你用root用户执行hdfs dfs -ls时,实际生效的身份取决于你的Kerberos凭据(如果开了安全认证)或HADOOP_USER_NAME环境变量。
一个常见需求是:在非安全集群里,以root身份操作HDFS时想伪装成hdfs用户,可以用:
HADOOP_USER_NAME=hdfs hdfs dfs -ls /tmp这样可以不切换系统用户就完成高权限操作,但生产环境请谨慎使用,权限这东西越绕越容易出问题。
5.3 ACL与默认ACL:当基本权限不够用时的扩展方案
HDFS从2.x版本开始支持POSIX ACL,允许给某个文件或目录额外授权给多个指定用户或组,而不仅仅是owner/group/other三组。常用命令:
# 给用户 alice 添加对 /data/project 目录的读权限 hdfs dfs -setfacl -m user:alice:r-x /data/project # 给组 data_reader 添加读权限 hdfs dfs -setfacl -m group:data_reader:r-x /data/project # 查看ACL hdfs dfs -getfacl /data/project # 给目录设置默认ACL,让新建的子文件和子目录自动继承 hdfs dfs -setfacl -d -m group:data_reader:r-x /data/projectACL是个双刃剑:配置灵活了,但排障也会变复杂。我之前排查过一个诡异的问题:用户明明被加进了某个组,HDFS上的目录也显示有权限,但他就是访问不了。最后getfacl一看,发现该目录曾设置过默认ACL,新建文件默认带上了一条奇怪的mask,把他的权限给遮掉了。HDFS的ACL mask机制和Linux一样会过滤掉"名为授权、实为越权"的条目,遇到这类问题记得用getfacl看mask那一行,而不是只看列表里的显式条目。
5.4 权限排查标准流程:一条命令定位问题
遇到Permission denied,不要盲试。我通常按下面这个顺序查:
- 用
hdfs dfs -ls -d <路径>看目标路径的属主、属组、权限位,确认当前用户是否属于能访问的角色。 - 用
hdfs dfs -getfacl <路径>看是否有ACL条目或mask影响。 - 逐级检查父目录的权限,因为HDFS的路径访问跟Linux一样,中间任何一级目录不可遍历就全盘失败。
- 确认当前用户身份,用
whoami和HADOOP_USER_NAME排查是否被环境变量干扰。
这套流程大概率能定位80%以上的权限问题。剩下20%可能是安全认证(Kerberos票据过期)或Ranger/Sentry这类外部授权组件在拦截,那就是另一个层面的排查了。
6. 运维管理命令:fsck、快照、均衡器的实际使用
6.1 fsck:检查文件健康度与块缺失问题
hdfs fsck不是Shell文件操作命令,但它是HDFS运维最常被用到的检车工具,用于检查文件和块的完整性。基本用法:
hdfs fsck /path/to/dir -files -blocks这个命令会扫描目录下所有文件的块列表,检查是否有丢失的块、副本数不足的块、损坏的块。输出里那几个关键指标很有意思:
Status: HEALTHY Replicated Blocks: 120 Erasure Coded Block Groups: 0 Missing Blocks: 0看到Status: HEALTHY基本可以放心。如果出现CORRUPT或Missing Blocks: 1,说明有文件块损坏或DataNode宕机导致副本丢失。此时先判断是哪个块丢了,再看对应DataNode是否在线。
用-racks参数可以查看多个副本是否分布在不同的机架上,这对了解集群的高可用状态非常直观:
hdfs fsck /data/important -files -blocks -racks6.2 快照:给重要目录拍免费"照片"
HDFS快照几乎是所有运维事故中最能救命的机制。它允许你对某个目录创建一个秒级完成、不占额外空间(只有增量变化时才占用)的快照。启用命令:
# 允许对 /data/important 创建快照 hdfs dfsadmin -allowSnapshot /data/important # 创建快照 hdfs dfs -createSnapshot /data/important snapshot_20240115 # 查看快照列表 hdfs dfs -ls /data/important/.snapshot # 删除快照 hdfs dfs -deleteSnapshot /data/important snapshot_20240115快照一个很实用的点:误删了文件,可以直接从.snapshot目录里拉回来,不需要备份系统恢复。如果误删目录,配上快照,恢复时间就是一条cp的事。我在实际工作中,对所有重要数据目录都会开启快照功能,并设置定时任务每小时打一个快照,保留最近24个。成本极低,但事故恢复时的收益巨大。
6.3 Balancer:集群数据倾斜时的平衡之手
集群运行久了,新加节点磁盘占用远低于老节点是常态。数据倾斜会影响MapReduce和Spark任务的本地性,拖慢整体性能。hdfs balancer就是用来自动迁移数据块的命令:
# 以默认阈值启动均衡 hdfs balancer # 指定10%的平衡阈值 hdfs balancer -threshold 10 # 只均衡指定的机架 hdfs balancer -includeRack rack1但要注意:生产环境在业务高峰跑Balancer非常不推荐。数据迁移会占用网络带宽和DataNode的IO,可能拖慢正在运行的任务。我的经验是专门挑工作日凌晨的低峰期,或者维护窗口执行,同时把阈值调得相对保守(比如10%),让它慢慢挪而不是疯狂迁移。Balancer的日志默认输出到终端,生产环境最好重定向到文件:
nohup hdfs balancer -threshold 10 > /var/log/hdfs_balancer.log 2>&1 &然后过几小时tail日志看结果,不要一直在前台等。
6.4 安全模式与其他维护开关
安全模式是HDFS启动后自动进入的一个特殊状态,此时文件系统只读,不允许写操作。NameNode在加载元数据后需要等待DataNode上报块信息,确认块副本数达到要求,才会自动退出安全模式。运维需要手动操作时:
hdfs dfsadmin -safemode enter hdfs dfsadmin -safemode leave hdfs dfsadmin -safemode get生产环境有一种典型情况:某个机架整体宕机,导致大量块的副本数低于配置值,NameNode会一直处于安全模式不退出。这时不要盲目leave,否则写操作可能使用不健康的块。正确做法是先看dfsadmin -report,确认缺失块的DataNode是否恢复在线,恢复后再退出安全模式。这块需要一点耐心,不能只想着"把它踢出去"。
还有一个常用开关是DataNode的服役和退役:通过hdfs dfsadmin -refreshNodes刷新包含dfs.hosts/dfs.hosts.exclude的配置文件,实现优雅下线。这个操作规划不好最容易造成数据丢失,建议先在exclude文件中加入要退役的节点,等数据迁移完成(块复制到其他节点),再真正关闭进程。
7. 那些文档没写、但生产一定会踩的坑与扩展技巧
7.1 通配符在Shell命令里的表现差异
在Linux里,hdfs dfs -cat /data/*.log这种写法通常能展开成所有匹配文件,但HDFS的路径解析有自己的规则。问题常出在*被本地Shell提前展开,而不是由HDFS处理。比如你在本地目录/data下有a.log和b.log,在HDFS的/data目录下也有同名的文件,然后执行:
hdfs dfs -cat /data/*.log如果本地Shell把这个*展开成/data/a.log /data/b.log,那HDFS解析的路径就变成了本地的两个文件路径(在HDFS上根本不存在),结果就是报File does not exist。解决方法是加引号,把通配符留给HDFS的路径解析器:
hdfs dfs -cat '/data/*.log'这个细节坑了很多人。凡是命令参数里带有通配符的,在Shell脚本中一律加引号,能省去大量莫名奇妙的报错排查时间。
7.2 小文件问题:命令行禁不住的坏习惯
使用命令行操作HDFS时,最伤集群的不是命令本身,而是操作方式诱发的小文件问题。比如用-put一次上传几万个几十KB的文件,每个文件独立成一个块,NameNode就要维护几万个块的元数据,内存开销直接拉满。几万个小文件拖垮集群的例子,我见过不止一次。所以生产环境我定过一条铁律:每天定时合并小文件,或者写入时尽量用大文件。
合并的通用命令是hdfs dfs -getmerge把HDFS上的小文件先合并到本地,再转成大文件put回去,或者直接在Hive/Spark作业里用CONCAT类操作处理。如果只是想快速在命令行看文件内容,-cat不会造成小文件问题,但要注意它会把二进制文件的内容打到终端,遇到压缩文件或Parquet文件,终端会直接花屏,此时用hdfs dfs -text或-get更合适。
7.3 误删恢复:快照和回收站是最后防线
前面讲权限时提过快照和回收站,这里再多说一句实战经验。误删文件后,如果你的集群开启了回收站,执行:
hdfs dfs -ls /user/<user>/.Trash/Current然后从回收站里mv回原位置就行。如果没有开启回收站、也没有快照,那么误删的文件基本就宣告无法恢复了——HDFS到目前为止没有完整的"被删文件恢复"功能。现实做法是在所有生产集群上强制开启fs.trash.interval=1440(分钟),再加上关键目录的自动快照。很多公司把这一条写进了交付基线,我很赞同。别等真删了重要分区表的数据才后知后觉。
7.4 批量操作与脚本化:封装命令的进阶思路
最后,分享一个我常用的脚本封装思路。HDFS命令本身很基础,但生产脚本往往需要处理多级目录、异常重试、日志记录。我习惯写一个薄薄的封装函数,比如:
function hdfs_put_retry() { local src=$1 local dst=$2 for i in 1 2 3; do hdfs dfs -put "$src" "$dst" && return 0 echo "[WARN] hdfs put failed, retry $i" sleep 5 done echo "[ERROR] hdfs put failed after 3 retries" return 1 }这个封装解决的问题是:网络抖动或NameNode短暂切换时,命令可能偶发失败,直接重试就能恢复。类似的思路还可以用在-get、-mkdir上。把所有HDFS操作统一走一层带日志和重试的脚本封装,生产任务至少能少一半的报警。
另外提醒一句:脚本里hdfs dfs -put的源路径千万不能带尾斜杠的目录相对路径写错,否则上传目录结构会和你预期的完全不同。我见过有人把put /data/logs/ /target写成put /data/logs /target,最后目录结构多了一层,导致下游读取路径全部错位。这种事情用-ls -R检查一遍就能发现,但往往要等下游任务报了"目录不存在"才意识到。
8. 最后想说的:命令行只是入口,理解原理才是核心
我自己用了很多年HDFS命令行,最大的体会是:命令本身从来不是难点,难的是你能否在输入一行命令之前,判断出它背后到底会发生什么——数据块怎么流动、NameNode会做什么决策、哪个环节可能出问题。-put是按块写入的,所以大文件比海量小文件更友好;-chmod要检查父目录权限,所以一条命令失败不一定是它自己有问题;-rm -skipTrash是物理删除,所以永远不要在自动化脚本里随手带-skipTrash。
如果你是想深入学习,建议按这个路径走:先在日常操作里把所有常用命令反复敲熟,再用fsck和dfsadmin -report去观察文件在集群里的真实分布,最后尝试自己写一个包含上传、校验、失败重试的脚本。到这个阶段,HDFS在你眼里就不再是一个"黑盒文件系统",而是一套有血有肉、能被你掌控的分布式存储设施。
我看过太多教程把HDFS命令做成了API字典的搬运工,但实际生产中,真正值钱的往往是"这条命令在这个环境下会踩哪些坑、配合什么机制使用才安全"。这篇里写的每一个坑都是我或身边同事真实遇到过、排查过的,希望你在自己的集群上实操时,能比我们少走一些弯路。命令行只是入口,你对分布式文件系统原理的理解深度,才是决定上限的东西。