linux删除数据慢,Linux下快速删除大量小文件的方法探索

一、前言

Linux服务器磁盘中存在大量小文件,需要进行删除(文件小于1K,数量50w~100w),发现rm删除速度奇慢无比,甚至出现“argument list too long”的错误。网上资料一搜索基本都是建议rsync方法,所以本文对几种常见的方法进行试验对比。

二、相关知识

2.1 文件系统的元数据(metadata)

1)superblock:记录文件系统的整体信息,包含inode/block的大小、总量、使用量、剩余量,以及文件系统的格式,文件系统挂载时间,最近一次数据写入时间,最近一次校验磁盘的时间等。

2)inode:表示文件系统的对象,具备唯一标识符(一个文件占用一个inode),inode记录了文件的元信息,具体来说包含的信息为:文件所有者、访问权限(读、写、执行)、类型(是文件还是目录)、内容修改时间、inode修改时间、上次访问时间、对应的文件系统存储块(Block)的地址;

3)block:实际记录文件的内容,若文件太大,则会占用多个block,通常的block大小有1K,2K,4K三种,这里内核记录block信息的数据结构是Bitmap。

2.2 文件系统工作原理(ext2、ext3)

ext2 为索引式文件系统,新增一个文件的流程如下:

1)确定目录是否有写权限/打开权限(w/x),没有则返回失败;

2)根据inode-bitmap分配新的inode,将新文件权限/属性记入;

3)根据block-bitmap分配新的block,将文件数据写入block中,更新inode的block指向数据;

4)将新的inode、block数据同步到inode-bitmap、block-bitmap中,并更新superblock内容;

ext3 为日志式文件系统,对ext2的缺点进行改进(系统故障重启,修复元数据信息耗时长),多出一块日志记录区块来保证可靠性:

1)当系统准备写入一个文件时,先在日志记录区块记录文件要写入的信息;

2)写入文件权限/属性,写入文件数据,更新元数据内容(同ext2);

3)完成数据与元数据更新后,在日志记录区块完成文件的记录;

在出现故障需要恢复时,可根据日志追踪之前提交到主文件系统的更改,大大减少了磁盘的扫描时间,实现丢失数据的快速重建,比传统的索引式文件系统更安全[3]。Linux下的集中日志式文件系统有XFS(目前是CentOS7的默认文件系统),ReiserFS,Ext3,Ext4。

三、实验

3.1 实验环境

CPU:4核,Intel(R) Core(TM) i3-3120ME CPU @ 2.40GHz

内存:8G

硬盘:1T(ST1000NM0055)

操作系统:Linux-3.10.25

文件系统:ext3

软件版本:find、rm(busybox1.24)、rsync3.1、bash3.2、perl5.8

3.2 实验步骤

1)创建文件:创建文件夹test,生成$num个8字节的文件,为防止文件名有规律,文件名给定随机的前缀,并且每次生成完成后都清空内存cache(echo 3 >/proc/sys/vm/drop_caches)

function genfiles()

{

mkdir test

for ((ix=0; ix

local filename=$RANDOM

echo -n "01234567" >test/$filename-$ix

echo "Genfile($ix/$num): $filename"

done

}

2)删除文件:根据文章[1]给出的方法,实验各种删除方法,并使用time给出统计时间

time rm -rf test

time find test -type f -delete;

time perl -e 'for(){((stat)[9]

time ./rsync -a --delete $PWD/tmp/ $PWD/test/

3)生成100w个文件,测试结果如下:

测试方法

耗时

rm -rf test

11m12.703s

find test -type f -delete;

13m18.632s

cd test && perl -e 'for(){((stat)[9]

19m17.510s

./rsync -a --delete $PWD/tmp/ $PWD/test/

17m54.199s

结果并没有跟文章[1]描述的情况一致,rsync快速提升?!

四、编程改进

根据文件系统元数据的思路,考虑我生成100w小文件的时候,名字为随机命名的,但是inode号为正向顺序增长的(猜测blocks块按照一定规律写入,机械硬盘的磁头使用自身高效的方式进行写入),如果删除文件时按照inode号顺序进行排序效率会怎么样?思路如下:

1)遍历目录,获取文件列表,按照inode号大小排序;

2)按照文件列表删除文件;

3)两种方案进行速度比较:inode排序(-DORDER_BY_INODE)、文件名排序;

遍历目录,将文件插入到红黑树regtree中,目录插到vector中:

static int __dir_scan(const char *pname, REGTREE &regtree, vector &dirlist)

{

int cnt = 0, ret = 0;

DIR *dir = NULL;

struct dirent *entry = NULL;

dir = opendir(pname);

assert(dir);

printf("dir: %s\n", pname);

while ((entry = readdir(dir)) != NULL) {

if (0 == strcmp(entry->d_name, ".") ||

0 == strcmp(entry->d_name, "..")) {

continue;

}

string fname = pname;

fname.append("/");

fname.append(entry->d_name);

if (entry->d_type == DT_DIR) {

ret = __dir_scan(fname.c_str(), regtree, dirlist);

if (ret <= 0) {

cnt = 0;

}

else {

cnt += ret + 1;

}

dirlist.push_back(fname);

}

else {

#ifdef ORDER_BY_INODE

regtree.insert(pair(entry->d_ino, fname));

#else

regtree.insert(pair(fname, fname));

#endif

cnt++;

}

if (regtree.size() >= BATCH_SIZE) {

break;

}

}

closedir(dir);

return cnt;

}

根据dirlist删除目录、regtree删除文件:

static int __dir_remove(vector &dirlist)

{

int cnt = dirlist.size();

for (size_t ix = 0; ix < dirlist.size(); ix++) {

LOGN("rmdir: %s\n", dirlist[ix].c_str());

rmdir(dirlist[ix].c_str());

}

// dirlist.clear();

return cnt;

}

static int __reg_remove(REGTREE &regtree)

{

int cnt = regtree.size();

for (REGTREE::iterator iter = regtree.begin();

iter != regtree.end(); iter++) {

LOGD("unlink: %llu %s\n", iter->first, iter->second.c_str());

unlink(iter->second.c_str());

}

regtree.clear();

LOGN("Files: %d\n", cnt);

return cnt;

}

为了防止内存占用过大,100w文件列表的内存使用超过1Gb,期间对regtree.size()进行限制,当扫描文件列表达到10w后,先退出扫描,先把文件删除一部分后再继续

#include

#include

#include

#include

#include

#include

#define BATCH_SIZE 100000

#ifdef ORDER_BY_INODE

# define REGTREE map

#else

# define REGTREE map

#endif

using namespace std;

int main(int argc, char *argv[])

{

int ret = FAILURE, cnt = 0;

REGTREE regtree;

vector dirlist;

while (1) {

if (__dir_scan(argv[1], regtree, dirlist) == 0) {

cnt += __dir_remove(dirlist);

break;

}

else {

cnt += __reg_remove(regtree);

}

}

ret = rmdir(argv[1]);

exit(ret ? EXIT_FAILURE : EXIT_SUCCESS);

}

上面测试程序写的比较糙,vector没有对dirlist进行去重,多次扫描还会多次rmdir同一个目录;中间使用了assert,实际过程可能还会出现文件删除不掉,文件夹陆续还有新文件出现的情况;

测试数据一看还是蛮不错的,相比rsync、rm、find方法快了许多:

测试方法

耗时

./rm_by_inode test

0m53.135s

./rm_by_name test

9m55.988s

五、结论

这本先是根据文章[1]进行了实验,后来发现实验结果完全与文章不一致,又查看了一些文件系统的资料,后来自己写了代码实验了一下,最终发现ext3文件系统下,inode号与磁头处理效率是挂钩的,特此将步骤写下,后续工程中再继续实践~

参考文章:

[1] https://www.slashroot.in/which-is-the-fastest-method-to-delete-files-in-linux

[2] https://www.ibm.com/developerworks/cn/linux/l-linux-filesystem/

[3] https://zhuanlan.zhihu.com/p/22976640

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值