MyTetra Share
Делитесь знаниями!
О, смотри-ка какое хорошее место. Дайте два!
Эффективное удаление большого количества файлов в одной директории, разные способы
08.11.2012
22:40
Текстовые метки: linux, удаление файлов, директория, большое количество
Раздел: Компьютер - Linux - Bash - Команды и скрипты

Удаление через rm -r


$ rm -r /mnt/test_dir/


Под strace несколько раз подряд (!!!) вызывает getdents(), затем очень много вызывает unlinkat() и так в цикле. Занял 30Мб RAM, не растет. Удаляет содержимое успешно.


iotop

7664 be/4 seriy 72.70 M/s 0.00 B/s 0.00 % 93.15 % rm -r /mnt/test_dir/

5919 be/0 root 80.77 M/s 16.48 M/s 0.00 % 80.68 % [loop0]


Т.е. удалять переполненные директории с помощью rm -r /путь/до/директории вполне нормально.



Удаление через rm ./*


Команда:


$ rm /mnt/test_dir/*


Запускает дочерний процесс шелла, который дорос до 600Мб, прибил по ^C. Ничего не удалил.


Очевидно, что glob по звёздочке обрабатывается самим шеллом, накапливается в памяти и передается команде rm после того как считается директория целиком.



Удаление через find -exec


Команда:


$ find /mnt/test_dir/ -type f -exec rm -v {} \;


Под strace вызывает только getdents(). процесс find вырос до 600Мб, прибил по ^C. Ничего не удалил.

find действует так же, как и * в шелле — сперва строит полный список в памяти.



Удаление через find -delete


Команда:


$ find /mnt/test_dir/ -type f -delete


Процесс в памяти вырос до 600Мб, прибил по ^C. Ничего не удалил.

Аналогично предыдущей команде. И это крайне удивительно! На эту команду я возлагал надежду изначально.



Удаление через ls -f и xargs


Команда:


$ cd /mnt/test_dir/ ; ls -f . | xargs -n 100 rm


Здесь параметр -f говорит, что не нужно сортировать список файлов. Создает такую иерархию процессов:


| - ls 212Кб

| - xargs 108Кб

| - rm 130Кб # pid у rm постоянно меняется


Удаляет успешно.


iotop # сильно скачет

5919 be/0 root 5.87 M/s 6.28 M/s 0.00 % 89.15 % [loop0]


ls -f в данной ситуации ведет себя адекватнее, чем find и не накапливает список файлов в памяти без необходимости. ls без параметров (как и find) — считывает список файлов в память целиком. Очевидно, для сортировки. Но этот способ плох тем, что постоянно вызывает rm, чем создается дополнительный оверхед.


Из этого вытекает ещё один способ — можно вывод ls -f перенаправить в файл и затем удалить содержимое директории по этому списку.



Удаление через perl readdir


Команда:


$ perl -e 'chdir "/mnt/test_dir/" or die; opendir D, "."; while ($n = readdir D) { unlink $n }'


Под strace один раз вызывает getdents(), потом много раз unlink() и так в цикле. Занял 380Кб памяти, не растет. Удаляет успешно.


iotop

7591 be/4 seriy 13.74 M/s 0.00 B/s 0.00 % 98.95 % perl -e chdi...

5919 be/0 root 11.18 M/s 1438.88 K/s 0.00 % 93.85 % [loop0]


Получается, что использование readdir вполне возможно?



Удаление через программу на C readdir + unlink


Код программы на С (файл cleandir.c):


#include <dirent.h>

#include <sys/types.h>

#include <unistd.h>


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

struct dirent *entry;

DIR *dp;

chdir("/mnt/test_dir");

dp = opendir(".");

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

if ( strcmp(entry->d_name, ".") && strcmp(entry->d_name, "..") ){

unlink(entry->d_name); // maybe unlinkat ?

}

}

}


Компилирование и запуск:


$ gcc -o cleandir cleandir.c

$ ./cleandir


Под strace один раз вызывает getdents(), потом много раз unlink() и так в цикле. Занял 128Кб памяти, не растет. Удаляет успешно.


iotop:

7565 be/4 seriy 11.70 M/s 0.00 B/s 0.00 % 98.88 % ./cleandir

5919 be/0 root 12.97 M/s 1079.23 K/s 0.00 % 92.42 % [loop0]


Опять — же, убеждаемся, что использовать readdir — вполне нормально, если не накапливать результаты в памяти, а удалять файлы сразу.



Выводы


Использовать комбинацию функций readdir() + unlink() для удаления директорий, содержащих миллионы файлов, можно.

На практике лучше использовать rm -r /my/dir/, т.к. он поступает более умно — сперва строит относительно небольшой список файлов в памяти, вызывая несколько раз readdir(), а затем удаляет файлы по этому списку. Это позволяет более плавно чередовать нагрузку на чтение и запись, чем повышает скорость удаления.


Для снижения нагрузки на систему использовать в комбинации с nice или ionice. Либо использовать скриптовые языки и вставлять небольшие sleep() в циклах. Либо генерировать список файлов через ls -l и пропускать его через замедляющий пайп.


И самое главное: не верить всему, что пишут в интернетах, конечно же! В различных блогах часто обсуждают эту проблему, и регулярно подсказывают неработающие решения.


P.S.: К сожалению, не нашел в Python функции для итеративного чтения директории, чему крайне удивлён; os.listdir() и os.walk() читают директорию целиком. Даже в PHP есть readdir.

Так же в этом разделе:
 
MyTetra Share v.0.44
Яндекс индекс цитирования