Akemi

百T目录并行删除

2025/08/29

正在美美摸鱼呢,领导来消息了,让我帮他删一下一台云主机上的文件,要求删除所有/mnt下所有archived开头的目录

这是一个超大nfs挂载,使用的是阿里云的NAS,其中有500T数据被占用,有多大呢,连输入ls都会卡住

1
2
df -Th | grep mnt
xxx[.cn-hangzhou.nas.aliyuncs.com:/ nfs 10P 502T 9.6P 5% /mnt2

解决思路

find与xargs

首先肯定是find,因为我们要的是archived开头的目录,它可以很快帮我们找出哪些目录需要删除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
find /mnt2 -maxdepth 1 -type d -name "archived*"
这样就可以列出所有我们需要删除的目录

然后配合xargs
就可以使用这样的命令:
find ./ -maxdepth 1 -type d -name "archived*" -print0 | xargs -0 -n5 sh -c '
echo "已删除以下目录:";
printf "%s\n" "$@";
rm -rf "$@"' --
这条命令会55个删除目录,并且将已经删除的目录打印在控制台,非常好,非常安全

在普通环境中大大的好,但是在这里行不通
因为这条命令甚至会直接卡住,都没有下文了

更重要的是,这条命令是单线程运行的,也就是每五个一批,先输出要删除目录了,然后等待删除完之后
才会进行下五个目录的删除,这显然是不行不够快的

;与+

1
2
3
4
5
6
7
8
9
10
11
12
13
{} \;
{} +
这两种管道符与xargs的使用方式有什么区别呢
第一种是发现一个就删一个
第二种是全部列出后,通过rm全部删除

这两种哪个好呢?都不行

通过grep -c可以查看到到底有多少个目录
ls | grep archived -c
62320

无论是5个一次,还是一次删6w个,都不合适

rsync

1
2
3
4
5
6
rsync是一种清空单个大目录的好方法
将空目录中的数据强制同步到目标目录
rsync -a --delete ~/empty_dir/ /mnt2/tmp/

但很遗憾,它不支持将一个目录同步到多个目录,依然需要使用find,会阻塞
而且会产生大量系统调用来分析目录结构,真不如rm

parallel多线程

这个时候就需要使用多线程工具了,

使用parallel,find会首先找到所有符合条件的目录,将其交给parallel,parallel再根据参数发起对应数量的任务,完美的规避了单线程阻塞的问题

1
2
3
4
find ./ -maxdepth 1 -type d -name "archived*" -print0 | \
parallel -j 12 -0 rm -rf {}

-j 并发线程数

性能瓶颈

CPU瓶颈

那么问题又来了,这个-j到底填多少合适呢?

常识告诉我们并发数应该和CPU线程数一致,也就是填一个12

填完之后越想越觉出不对劲来,点开top看看CPU状态吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
top
- 11:30:57 up 7 days, 21:36, 3 users, load average: 12.11, 8.98, 3.33
Tasks: 235 total, 2 running, 233 sleeping, 0 stopped, 0 zombie
%Cpu0 : 1.3 us, 1.0 sy, 0.0 ni, 97.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu1 : 0.3 us, 0.3 sy, 0.0 ni, 99.3 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu2 : 0.7 us, 1.3 sy, 0.0 ni, 98.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu3 : 0.3 us, 0.3 sy, 0.0 ni, 99.3 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu4 : 1.0 us, 1.0 sy, 0.0 ni, 98.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu5 : 0.3 us, 0.0 sy, 0.0 ni, 99.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu6 : 1.7 us, 1.0 sy, 0.0 ni, 97.3 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu7 : 0.7 us, 0.3 sy, 0.0 ni, 99.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu8 : 1.0 us, 1.0 sy, 0.0 ni, 98.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu9 : 0.7 us, 1.3 sy, 0.0 ni, 98.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu10 : 0.7 us, 0.7 sy, 0.0 ni, 98.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu11 : 0.7 us, 0.7 sy, 0.0 ni, 98.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st

可以看到,虽然load average挺高的
但是所有CPU核心的空闲时间id都在97%以上
用户态使用率us和系统态使用率sy都很低

不是哥们,哥几个表面上很忙,实际上搁这儿摸鱼呢?

这是因为我们执行的删除文件任务并非CPU密集型任务
rm任务只是在队列中等待而已,实际上并不占用CPU
卡住它的另有其人

带宽瓶颈

那我这个存储是NFS挂载的NAS,是不是因为网络带宽或者其他什么的?
(也许在这种场景下没有必要,因为公有云有自己的VPC,云主机和NAS肯定连得贼快,但其实有更普遍的意义)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
yum -y install iftop bind-utils

host xxxx(nfs挂载名称)
解析出对端IP

然后使用iftop
localhost => 172.28.4.39 4.53Mb 4.65Mb 5.02Mb
<= 9.49Mb 8.49Mb 8.16Mb

这还是Mb,换成MB得除以8
现在家用的宽带跑的都比这玩意快

可见网络带宽远远没有达到瓶
问题不是出在带宽不足上,而是出在网络延迟或NFS服务器处理单个请求的延迟上

nfs读写延迟

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
nfsiostat 1
op/s rpc bklog
5427.50 0.00
read: ops/s kB/s kB/op retrans avg RTT (ms) avg exe (ms)
0.000 0.000 0.000 0 (0.0%) 0.000 0.000
write: ops/s kB/s kB/op retrans avg RTT (ms) avg exe (ms)
0.000 0.000 0.000 0 (0.0%) 0.000 0.000

可见
op/s为5000
retrans为0% 丢包率
rpc bklog为0 客户端积压的RPC请求
avg RTT 0 每个操作的平均延迟

下面这三项可能因为内核版本过低,所以没有出来,但实际上avg RTT肯定为非0
那么接下来要做什么呢

根据nfs ops调优并发线程数

调呗,硬调,要通过调整并发线程数,找出一个ops最高的点

1
2
3
4
5
6
7
-j   avg ops
12 300
100 6600
150 7500
160 8000
180 8000
200 7500

命令执行

1
2
3
4
5
6
yum -y install tmux
tmux new -s cleanup
find ./ -maxdepth 1 -type d -name "archived*" -print0 | parallel -j 160 -0 rm -rf {}

快捷键
Ctrl+b 松手d
CATALOG
  1. 1. 解决思路
  2. 2. 性能瓶颈