- Удаляем файлы определенного размера и дубликаты
- Так как же удалить миллионы файлов из одной папки?
- Подготовка
- Тесты
- Удаление через rm -r
- Удаление через rm ./*
- Удаление через find -exec
- Удаление через find -delete
- Удаление через ls -f и xargs
- Удаление через perl readdir
- Удаление через программу на C readdir + unlink
- Выводы
- Как быстро удалить Очень Много файлов?
- Re: Как быстро удалить Очень Много файлов?
- Каждую неделю вопрос на ЛОРе.
- Use ionice, Luke!
- Re: Как быстро удалить Очень Много файлов?
Удаляем файлы определенного размера и дубликаты
К написанию этой статьи меня подвигли постоянные восстановления данных с «флешек» или карт памяти. После спасения данных (изображений, музыки, документов и т.д.) программами Photores и Foremost, в каталоге с найденными файлами появляется много картинок маленького размера и много дубликатов. Поэтому раньше очень много времени я тратил на сортировку файлов по размеру и удалял файлы небольшого размера. Но Linux потому и Linux, что может решить множество нужных задач при помощи нескольких команд в Терминале. Быстро и эффективно!
Как правило программы для восстановления запускаются от супер пользователя, поэтому просто так удалить восстановленные файлы мы не сможем. Для этого я изменяю владельца для этих каталогов и становлюсь их хозяином. После этого нужные файлы и каталоги удаляются от обычного пользователя без проблем. Итак, меняем владельца каталога:
sudo chown -R user:user ~/Recovery
user — Ваш логин в системе
~/Recovery — каталог с восстановленными файлами. В Вашем случае он скорее всего будет другой.
После этого нам нужно удалить картинки маленького размера. Размер напишите свой, потому что в одном случае нужное изображение может быть от 2 Мб и выше, а в другом случае размер может быть от 300Кб, если фото делалось в телефоне среднего качества. Поэтому я установлю размер удаляемых картинок меньше 60 кб:
find ~/Recovery -type f -size -60k -exec rm <> \;
В данном случае производится поиск в каталоге ~/Recovery. Параметр -type f указывает программе, что нужно искать именно файлы, а не каталоги, а команда rm удалит все файлы, которые ниже размера, указанного в параметре -size -60k . Если нужно в будущем удалять и каталоги, то добавьте параметр -type d .
После этого мне нужно было удалить и дубликаты, которые появились после восстановления. Это делается при помощи утилиты fdupes. Если не установлена, то выполним команду в Терминале:
sudo apt-get install fdupes
А теперь удалим ненужные дубликаты командой:
-r — рекурсивный поиск в каталоге;
-d — параметр для удаления;
-N — данный параметр поможет удалить каждый файл без постоянного переспрашивания и ожидания Вашей реакции;
~/Recovery — каталог с дубликатами.
После этих команд в каталоге остались практически все нужные фотографии и другие файлы.
p.s. Все больше и больше влюбляюсь в этот безумный и обалденный LINUX!
Так как же удалить миллионы файлов из одной папки?
Феерическая расстановка точек над i в вопросе удаления файлов из переполненной директории.
Прочитал статью Необычное переполнение жесткого диска или как удалить миллионы файлов из одной папки и очень удивился. Неужели в стандартном инструментарии Linux нет простых средств для работы с переполненными директориями и необходимо прибегать к столь низкоуровневым способам, как вызов getdents() напрямую.
Для тех, кто не в курсе проблемы, краткое описание: если вы случайно создали в одной директории огромное количество файлов без иерархии — т.е. от 5 млн файлов, лежащих в одной единственной плоской директории, то быстро удалить их не получится. Кроме того, не все утилиты в linux могут это сделать в принципе — либо будут сильно нагружать процессор/HDD, либо займут очень много памяти.
Так что я выделил время, организовал тестовый полигон и попробовал различные средства, как предложенные в комментариях, так и найденные в различных статьях и свои собственные.
Подготовка
Так как создавать переполненную директорию на своём HDD рабочего компьютера, потом мучиться с её удалением ну никак не хочется, создадим виртуальную ФС в отдельном файле и примонтируем её через loop-устройство. К счастью, в Linux с этим всё просто.
Создаём пустой файл размером 200Гб
#!python f = open("sparse", "w") f.seek(1024 * 1024 * 1024 * 200) f.write("\0")
Многие советуют использовать для этого утилиту dd, например dd if=/dev/zero of=disk-image bs=1M count=1M , но это работает несравнимо медленнее, а результат, как я понимаю, одинаковый.
mkfs -t ext4 -q sparse # TODO: less FS size, but change -N option sudo mount sparse /mnt mkdir /mnt/test_dir
К сожалению, я узнал об опции -N команды mkfs.ext4 уже после экспериментов. Она позволяет увеличить лимит на количество inode на FS, не увеличивая размер файла образа. Но, с другой стороны, стандартные настройки — ближе к реальным условиям.
#!python for i in xrange(0, 13107300): f = open("/mnt/test_dir/___".format(i), "w") f.close() if i % 10000 == 0: print i
Кстати, если в начале файлы создавались достаточно быстро, то последующие добавлялись всё медленнее и медленнее, появлялись рандомные паузы, росло использование памяти ядром. Так что хранение большого числа файлов в плоской директории само по себе плохая идея.
$ df -i /dev/loop0 13107200 13107200 38517 100% /mnt
$ ls -lh /mnt/ drwxrwxr-x 2 seriy seriy 358M нояб. 1 03:11 test_dir
Теперь попробуем удалить эту директорию со всем её содержимым различными способами.
Тесты
После каждого теста сбрасываем кеш файловой системы
sudo sh -c ‘sync && echo 1 > /proc/sys/vm/drop_caches’
для того чтобы не занять быстро всю память и сравнивать скорость удаления в одинаковых условиях.
Удаление через 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
//file: cleandir.c #include #include #include 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 и пропускать его через замедляющий пайп.
- Не верить всему, что пишут в интернетах, конечно же! В различных блогах часто обсуждают эту проблему, и регулярно подсказывают неработающие решения.
Как быстро удалить Очень Много файлов?
Т.е. только мета-инфа весит 330 Мб (верхняя планка, значит, 1,3-1,4 млн. файлов). Списка имён файлов нет.
Вопрос: как их быстро удалить, с минимальной утилизацией винтов (продакшн-сервер) и без потери остальных данных на этом разделе?
Re: Как быстро удалить Очень Много файлов?
rm -dfr /var/www или че там у тебя
Каждую неделю вопрос на ЛОРе.
Этого вопроса ещё нет в FAQ? (Мне лень смотреть)
find . -maxdepth 1 -type f | xargs -0 ls
Use ionice, Luke!
> Т.е. только мета-инфа весит 330 Мб
Неверно, но к делу отношения не имеет.
> Вопрос: как их быстро удалить,
> с минимальной утилизацией винтов (продакшн-сервер)
echo cfq > /sys/block/[диск, где находится ФС]/queue/scheduler
ionice -c3 rm -rf /path/to/dir
P.S. Непонятно, чем они так помешали, что понадобилось их так срочно удалять.
Re: Как быстро удалить Очень Много файлов?
До этого пробовал:
#find ./ -maxdepth 1 -type f -mtime +7 -exec rm -f <> \;
(не хотелось удалять всё), выглядело так:
——————
#strace -s 360 -p 25670
Process 25670 attached — interrupt to quit
getdents64(4, /* 73 entries */, 4096) = 4088
getdents64(4, /* 73 entries */, 4096) = 4088
getdents64(4, /* 73 entries */, 4096) = 4088
getdents64(4, /* 73 entries */, 4096) = 4088
getdents64(4, /* 73 entries */, 4096) = 4088
etdents64(4,
Process 25670 detached
——————
, т.е. видимо строило файл-лист и делало это больше недели, с обычным rm было так же (надо было кстати использование памяти замерить).
А с ionice -c3 rm -dfr dir так:
# strace -s 360 -p 933
——————
Process 933 attached — interrupt to quit
unlink(«4e2a0e62921574187660e5ea2155af7e») = 0
unlink(«6094ad144713c973537c162b9e52b5d5») = 0
unlink(«86287687383af5b9c880cd6d4dbaca1c») = 0
unlink(«a8c6a06559e542e396c01a28d6110bbc») = 0
unlink(«ce40f028183e656e1fb5039b8a06dc92») = 0
unlink(«6c66c8ac651afbcd9c3b6dd45a79d506») = 0
unlink(«0753b1f1fcadc9262f0705c897350846») = 0
unlink(«a76ec71296a85087c5e76c413c92e23b») = 0
unlink(«66d65eba60a15d3e44b4058c763dbe74») = 0
— SIGINT (Interrupt) @ 0 (0) —
Process 933 detached
——————