高性能 Find
如果 JuiceFS 文件系统中已经存有海量文件,需要按照特定条件搜索,那么 Linux 自带的 find 可能性能会比较差,从 v5.3.7 开始,可以使用 juicefs find 命令作为 find 的高性能替代,通常会比 find 快 1~2 个数量级。详细介绍见命令参考。
客户端升级到 v5.3.7 即可使用 juicefs find。如果元数据服务未升级到 v5.3.7,依然可以正常使用,但客户端检索性能会有较大折损。
背景
为什么需要 juicefs find
在大型分布式文件系统上线后,存储治理是一个普遍痛点:
- 大量历史数据长期占用存储空间,但无人知晓哪些文件仍在使用、哪些已经「沉睡」。
- 存储团队需要定期盘点长时间未访问的文件,交给业务部门去确认是否删除。
- 常见的需求表达:「列出超过 X 天未访问的文件列表」、「找出占用空间超过 Y GiB 的大文件」。
现有方案对比
| 方案 | 特点 |
|---|---|
标准 Linux find | 单机遍历,无法利用 JuiceFS 元数据服务分布式扫描能力,大目录极慢 |
juicefs find ✅ | 服务端直接过滤,支持 atime/size/type 等谓词,结果精准返回,零外部依赖 |
juicefs find 直接在 JuiceFS 内部实现按条件递归查找文件,结合两阶段过滤(服务端元数据侧预筛选 + 客户端挂载侧最终求值),兼顾性能与灵活性。
核心价值
juicefs find 为存储治理提供了完整的「发现 → 确认 → 执行」闭环:
快速定位「冷数据」→ 业务部门确认 → 创建删除任务 → 释放存储空间
- 定位:通过
-atime、-size、-user等谓词组合,在海量文件中快速筛选出符合条件的「冷数据」。 - 确认:先用
-print将匹配结果导出为清单,交给业务部门审核哪些确实可以清理。 - 执行:确认无误后用
-delete批量删除,或配合-exec调用外部工具(如归档、压缩),释放存储空间、降低存储成本。
功能概述
juicefs find 命令用于在 JuiceFS 挂载点中按条件递归查找文件或目录,并支持执行动作(如打印、删除、执行外部命令)。
该实现采用两阶段过滤:
- 服务端过滤(元数据侧):基于表达式 AST 在元数据树上做快速筛选,减少回传数据量。
- 客户端过滤与动作执行(挂载侧):对服务端返回条目做最终表达式求值并执行动作。默认动作为
-print。
命令格式
juicefs find [-p N] [PATH ...] [EXPRESSION]
简单示例:
juicefs find /mnt/jfs -name "*.log"
juicefs find /mnt/jfs -type f -size +10M -print
juicefs find /mnt/jfs -name "*.tmp" -a -delete
juicefs find /mnt/jfs -type f -exec ls -l {} \;
高级用法示范:
# 使用 8 个并发线程扫描
juicefs find -p 8 /mnt/jfs -name "*.log"
# 查找大文件
juicefs find /mnt/jfs -type f -size +1G
# 查找最近 1 天内修改的日志
juicefs find /mnt/jfs -name "*.log" -mtime -1
# 忽略大小写查找路径
juicefs find /mnt/jfs -ipath "*backup*"
# 组合条件
juicefs find /mnt/jfs -type f -a \( -name "*.go" -o -name "*.md" \)
# 按时间比较:修改时间晚于指定日期
juicefs find /mnt/jfs -newermt "2024-01-01"
# 按时间比较:访问时间晚于参考文件
juicefs find /mnt/jfs -neweram /mnt/jfs/reference.txt
# 执行动作
juicefs find /mnt/jfs -type f -name "*.tmp" -delete
juicefs find /mnt/jfs -type f -name "*.log" -exec gzip {} \;
选项与语法
遍历选项
-p, --threads N并发线程数(默认 10),提升大目录扫描速度。该参数必须放在路径之前。-maxdepth N最大遍历深度。可以放在表达式中的任意位置。-mindepth N最小匹配深度。可以放在表达式中的任意位置。
逻辑运算
- 与:
-a, -and(也支持隐式与)。 - 或:
-o, -or。 - 非:
!, -not。 - 分组:
(...)。
表达式遵循常见优先级:
- 括号
- 非
- 与
- 或
支持的谓词
| 谓词 | 说明 | 生命周期管理用途 |
|---|---|---|
-atime [+-]N | 文件最后访问时间。+N 匹配 N 天之前,-N 匹配最近 N 天内(均不含第 N 天) | 识别冷数据的核心谓词 |
-mtime [+-]N | 文件内容最后修改时间。+N/-N 同上 | 识别长期未更新的文件 |
-ctime [+-]N | 文件状态最后变更时间(含内容修改、权限/所有者变更等)。+N/-N 同上 | 识别状态变更时间范围内的文件 |
-size [+-]N[KMGT] | 指定文件大小(+ 为大于,- 为小于;K/M/G/T 采用 1024 进制) | 识别占用空间多的大文件 |
-type f/d/l | 文件类型(文件/目录/软链接) | 区分文件与目录分别治理 |
-name/-iname | 文件名(支持通配符) | 按扩展名筛选(如 .log、.tmp) |
-path/-ipath | 路径匹配(支持通配符,* 匹配 /) | 按目录筛选 |
-user/-group | 按文件所属用户/组筛选,同时支持名称和数字 ID(如 UID、GID) | 按部门维度统计冷数据 |
-empty | 空文件或空目录 | 清理无效残留文件 |
-perm MODE | 按权限筛选。-perm 644 权限恰好为 644;-perm -644 权限至少为 644;-perm /644 权限包含 644 中的任一位 | 排查权限异常文件 |
-links | 按硬链接数筛选 | 识别无引用的文件 |
-inum [+-]N | 按 inode 号筛选,+ 为大于,- 为小于 | 定位特定 inode 关联的文件 |
-regex/-iregex | 正则匹配完整路径,采用 RE2 语法,自动全路径锚定;-iregex 不区分大小写 | 复杂路径模式匹配 |
-newerXY FILE | 比参考文件/日期更新。X 为检查的时间字段(a 访问时间 / m 修改时间 / c 变更时间),Y 为参考类型(a/c/m 为文件引用,t 为日期字符串 )。-newer 等价于 -newermm | 以某文件或日期为基准比较 |
-mmin/-amin/-cmin | 分钟级时间筛选 | 精细化时间控制 |
-nouser/-nogroup | 无对应用户/组的文件 | 排查孤儿文件 |
-newerXY 的 t 模式支持以下日期格式,按优先级依次尝试:YYYY-MM-DD HH:MM:SS、RFC 3339 格式(带时区)、ISO 8601 格式(不带时区)、YYYY-MM-DD 纯日期,以及系统 date --date 所能解析的其他格式(如 1 year ago、last Monday)。
支持的动作
| 动作 | 说明 |
|---|---|
-print | 打印路径(默认动作) |
-print0 | 打印路径,以 null 字符分隔(适合 xargs -0) |
-printf FORMAT | 按指定格式打印,支持 %p(完整路径)、%f(文件名)、%s(文件大小)、%M(权限字符串)、%u/%g(用户/组名)、%t/%a/%c(时间)等格式化占位符 |
-exec CMD {} \; | 对每个匹配项执行命令(暂不支持 -exec ... + 形式) |
-delete | 删除匹配的文件 |
生命周期管理典型场景
以下场景均可用 juicefs find 组合条件实现:
场景一:生成月度存储治理报告
# 找出所有超过 90 天未访问的文件,导出到清单
juicefs find /mnt/jfs -type f -atime +90 > /tmp/cold_files_90d.txt
# 进一步按大小区间分类统计
juicefs find /mnt/jfs -type f -atime +90 -size +1G > /tmp/cold_large_1g.txt
juicefs find /mnt/jfs -type f -atime +90 -size +100M > /tmp/cold_large_100m.txt
识别「冷数据」,按大小分级导出为清单,交给业务部门确认是否可删除。
场景二:查找超过 30 天未访问的大文件(> 1 GiB)
juicefs find /mnt/jfs -type f -atime +30 -size +1G -print
大文件即使数量少,也占用大量空间,这类文件优先治理。
场景三:查找 7 天内修改过的日志文件
juicefs find /mnt/jfs -name "*.log" -mtime -7 -print
确认近期还在写入的日志,避免误删正在使用的日志。
场景四:查找空文件或空目录
juicefs find /mnt/jfs -empty -print
发现已删除内容但残留的空文件/目录,释放 inode 资源。
场景五:按部门统计冷数据
# 按部门目录维度分别统计超过 180 天未访问的文件
juicefs find /mnt/jfs/rd -type f -atime +180 -print
juicefs find /mnt/jfs/marketing -type f -atime +180 -print
# 也可以按文件所属用户筛选
juicefs find /mnt/jfs -type f -atime +180 -user sales -print
按部门/用户维度统计冷数据,责任到人,便于业务部门统一治理。
场景六:批量清理老旧临时文件
# 第一步:预览要删除的临时文件,确认清单
juicefs find /mnt/jfs -type f -name "*.tmp" -atime +7 -print
# 第二步:确认无误后执 行删除
juicefs find /mnt/jfs -type f -name "*.tmp" -atime +7 -delete
务必先 -print 预览,确认无误后再执行 -delete。
场景七:与外部系统集成
# 导出为 null 分隔格式,避免文件名含空格导致的问题
juicefs find /mnt/jfs -type f -atime +365 -print0 > /tmp/cold_files-1y.txt
# 结合 xargs 批量压缩一年前的冷数据(节省存储成本)
cat /tmp/cold_files-1y.txt | xargs -0 gzip
将查找结果以 -print0 格式导出,无缝衔接 xargs 等外部工具做后续处理。
注意事项
- 请在挂载点路径上使用该命令,避免对非 JuiceFS 路径执行。
- 使用
-delete前务必先用同条件执行-print预览。 - 对大目录建议设置
-maxdepth以控制扫描范围。 - 谓词解析错误会直接返回错误,建议先从简单表达式逐步叠加。
- 对于低版本的元数据服务或者不支持的谓词,可以回退到客户端做过滤。
- 对于没有访问权限的目录会自动跳过(静默)。
- 遍历过程中或者执行出错,会打印相关日志。
- atime 相关功能需要元数据服务开启记录 atime 功能(默认关闭更新 atime)。
- 大规模扫描时建议使用独立 Token 并设置 CPU 使用率上限(v5.3.7+,仅需升级元数据服务),避免影响正常业务。
故障排查
| 问题 | 可能原因 |
|---|---|
| 无输出 | 检查路径是否正确、是否命中条件、atime 是否被记录(可咨询 JuiceFS 团队确认) |
-exec 执行失败 | 检查外部命令是否存在、参数是否正确、权限是否足够 |
| 执行后返回错误 | 常见于 delete/exec 行为失败;日志会显示具体失败条目 |
性能表现与负载
- 实际使用中,受各分区元数据分布的均衡性、过滤 条件的复杂度、扫描返回的数据量、元数据服务的负载等因素影响,处理性能会有较大波动,从单分区几万 inodes/s 到几十万 inodes/s 均有可能。
- 可以通过配置并发数(
-p/--threads)来加速扫描,但需要注意增大并发也会增加元数据服务的负载,建议避免过度调高并发而影响正常业务请求处理。 - 遍历过程中,归档的目录需要解压缩后才能扫描。从 v5.3.12 版本开始(仅需升级元数据服务,不依赖客户端升级),扫描完成的目录节点会被及时重新压缩,大幅缩减解压缩数据在内存中的滞留时间,从而显著降低内存峰值并消除间歇性 GC 导致的 CPU 满载问题。升级后内存仍会有小幅增长,增长量与
-p/--threads设置的并发度成正比:并发越高,同时处于解压缩状态的目录节点越多。在未升级到此版本的元数据服务上,大规模juicefs find扫描可能引发内存大幅增长和间歇性全核 CPU 占用。 - 解压缩在请求处理线程中同步执行,受限于所属 Token 的 CPU 配额;重新压缩在后台完成,不占用 Token CPU 配额,且单次开销很小,不会产生额外负载。
使用独立 Token 控制负载
在大规模文件系统上执行 juicefs find 扫描时,建议为执行扫描的挂载点创建独立的客户端 Token,并限制其元数据服务 CPU 使用率(该功能从 v5.3.7 开始支持,仅需升级元数据服务,不依赖客户端升级),配合 -p/--threads 控制并发数,避免扫描影响正常业务请求处理。


