Debian.pro

Блог для пользователей и администраторов Debian


Самый быстрый и надежный способ удалить все файлы в каталоге — rsync

Наверняка все сталкивались с ситуацией, когда есть каталог с несколькими миллиардами файлов, место/inodes кончилось, всё не работает, в каталоге нет ничего важного (по крайней мере настолько важного, чтобы нельзя было удалить это ради того, чтобы сервер начал работать).. Жмем rm — arguments list too long. Или segfault. Или rm падает по памяти. А ещё веселее, если сам каталог удалять нельзя. А ещё веселее, что нужно сейчас быстро-срочно подчистить хоть сколько-нибудь.
Гугл вам будет советовать find с xargs rm или -delete, perl и ещё какую-нибудь неведомую фигню. Не верьте ему. Решение этой задачи — rsync.
- rsync в данном примере сразу начинает удалять файлы (то есть через несколько секунд вы получите хоть сколько-то inodes пустых, а через пару минут — уже приличное их количество, достаточное для нормальной работы), а не строить список файлов, как некоторые
- rsync просто идет по файлам и удаляет их нафиг без всяких размышлений. Что быстро.

А суть простая — нужно rsync-ом положить пустой каталог в тот, который нужно почистить, с опцией —delete. Тогда rsync будет просто удалять каждый файл. А если в каталоге ветвистая структура и вы знаете про parallels из прошлой статьи…
Создаём пустой каталог:

root@server:~# mkdir /tmp/empty

Если всё плохо и на дисках создать каталог уже нельзя, то нас всегда спасёт /dev/shm:

root@server:~# mkdir /dev/shm/empty

Вешаем на каталог те же права, которые сейчас висят на том каталоге, который мы собираемся чистить (иначе каталог станет принадлежать руту и туда никто потом не сможет писать), например:

root@server:~# chown www-data:www-data /dev/shm/empty; chmod 755 /dev/shm/empty

Начинаем всё удалять:

root@server:~# rsync -a --delete /dev/shm/empty/ /path/to/big/dir/

(не протеряйте слеши в концах каталогов).
Идём пить чай, потом рестартим всё, что попадало. К завтрашнему утру rsync сможет удалить несколько десятков миллиардов файлов. А на ssd — сотен миллиардов.


Комментарии (14):

  1. JTProg :

    Спасибо большое за ценную инфу! Взял на вооружение!

  2. Да не за что.

  3. Dmitry :

    Забавно:)

  4. skeletor :

    На продакшин серверах вы поставите раком свой сервер таким методом. IO будет с настолько большим overhead’ом, что лучше вручную удалить хотя бы пару десятков файлов. Рекомендую ознакомиться с http://blog.endpoint.com/2010/07/efficiency-of-find-exec-vs-find-xargs.html .
    Так что всё-таки идеальный вариант = find+xargs.

  5. Ну херню не неси.
    IO оно поставит раком только в том случае, если с IO всё и так плохо (например, в сервере стоят sata-2 диски убитые).
    Удаление файлов последовательное, никакого параллелизма там нет, занята одна IO-очередь. Соответственно, всё это прекрасно управляется через ionice (да и просто не мешает другим процессам, если сервер хорошо себя чувствует, а не убит в потолок по IO без всяких удалений файлов).

    А вот с find может быть проблема, когда файлов миллиарды — он строит в памяти список, если памяти не хватает, то его прибивает OOM. Так что на виртуалке с 512 памяти find-ом удалить миллиард файлов вовсе не получится. Собственно, у меня была виртуалка с 4гб памяти и жалкие 200 млн. файлов в mod_tmp/ — тогда мне и пришлось придумывать нечто работающее.

    Есть второй работающий способ с перлом (просто брать и удалять файлы, а не думать), но его запомнить намного сложнее. А вот вариант с rsync — золотая середина. И удаляет эффективно, и запомнить легко.

  6. ZS :

    Позвольте полюбопытствовать, как вы поставите rsync, если inodes кончились?
    С совершенно обычного сервера:
    root@2:~# which rsync
    root@2:~# which find
    /usr/bin/find
    root@2:~# which rm
    /bin/rm
    А еще можно примерно так:
    ls -1 | while read F; do rm -f «$F» ; done
    Наверняка можно еще пару способов найти которые и память не едят и юзеют только ширпотребные команды.

  7. > А еще можно примерно так:
    ls на 200 миллионах файлов тоже падает по памяти.

    > можно еще пару способов найти которые и память не едят и юзеют только ширпотребные команды.
    Можно найти миллиард способов. Этот удаляет файлы быстрее всего (ну кроме перлового однострочника, который из широких штанин не достанешь в панике)

    Тем не менее, название статьи — «Самый быстрый и надежный способ удалить все файлы в каталоге», а не «самый распространенный», «самый простой» или какое-то подобное.

    Бинарь rsync можно тоже в /dev/shm положить

  8. ZS :

    > ls на 200 миллионах файлов тоже падает по памяти.
    Даже если отключить всякие ненужные сортировки?
    Попробовать негде — 200 млн. файлов нигде не завалялись. Максимум 11 млн. есть. И это (ls -1f) занимает меньше минуты на md raid5 из 4х sata-2 дисков. И памяти не сказать чтоб много заюзалось.
    Но раз вы утверждаете, что это «Самый быстрый и надежный способ удалить все файлы в каталоге», то наверняка уже так пробовали. Насколько rsync быстрее получается?
    Но мне вот интересно почему rsync не падает по памяти. Он же сравнивает каталоги прежде чем синхронизировать…

  9. > Даже если отключить всякие ненужные сортировки?
    На 130 файлах:
    0.00user 0.00system 0:00.00elapsed 0%CPU (0avgtext+0avgdata 1112maxresident)k
    0inputs+0outputs (0major+338minor)pagefaults 0swaps

    На 200к файлов:
    0.65user 0.19system 0:02.96elapsed 28%CPU (0avgtext+0avgdata 52660maxresident)k
    118008inputs+0outputs (0major+13218minor)pagefaults 0swaps

    50 мегабайт памяти с гаком во втором случае (оба запуска на ssd).

    > И это (ls -1f) занимает меньше минуты на md raid5 из 4х sata-2 дисков.
    Да, а ещё вы 11 млн. раз сделали бы тяжелый exec на бинарник rm вместо простого сисколла unlink

    > Насколько rsync быстрее получается?
    Насколько именно быстрее выяснить не получилось. Все остальные способы банально падали по памяти.
    Я просто по-strac-ил и не придумал, что можно выкинуть. Он там делает getdents, потом lstat на файл, потом unlink без переключений контекстов.
    Соответственно, ему не нужно нигде хранить выхлоп lstat (а ls кладет в память результаты lstat на каждый файл в память, при этом, собственно, сначала делает lstat на каждый файл).
    Ну а выхлоп getdents можно читать без всасывания всего массива в память. Да и там, емнип, словарь/массив из номеров inodes файлов, так что оно и так сильно меньше занимает.

    > Он же сравнивает каталоги прежде чем синхронизировать…
    Нет, не совсем так. Механику я выше описал.
    Он строит «индекс» только для первого каталога (пустого в нашем случае) в памяти. Потом (ну при условии -a —delete, в остальных случаях может быть по другому) он работает пофайлово. Видит следующий файл в выхлопе getdents, смотрит на него, смотрит, что такого же файла (с тем же именем) нет в исходном каталоге, делает unlink, берет следующий файл из getdents

    Опции —delete-before и —delete-after вот уже могут всасывать в память индексы двух каталогов, потому что в теории они там пригодились бы. Или, по крайней мере, rsync хранил бы в памяти инфу о том, какие файлы нужно удалить (если —after использовать), чтобы потом этот массив использовать. Но —delete точно ничего в память не кладет.

  10. Собственно, если getdents не влазит в память — то такой каталог удалить уже в принципе ничем нельзя будет, кроме mkfs. Но там система уже упрется в 64-битность в каком-нибудь дурацком месте, а не в память, скорее.

  11. skeletor :

    Чувак ты сам херню несёшь. find+xargs не строит список, а лишь выплёвывает по каждому найденному файлу дальше (параметр print0, например) в xargs. Xargs же, на основе этого составляет свой список по 10-100 файлов (хз, сколько за раз) и просто удаляет. Если ты не умеешь работать с find — твои проблемы.
    дополнительно запускать ionice для того, что бы следить за удалением файлов — о да, это круто!
    а касательно раков: на высоко-нагруженных серверах любое лишнее увеличение IO это всегда плохо, любой overhead (в виде запуска ionice) вносит свои коррективы. И тут дело не в sata-дисках, а в том, что на таких серверах выполняются миллионы операций, и на каждую операцию много своих syscall’ов, lock’ов.
    Тебе конечно пофиг, если база начала отвечать на 10 мс дольше, или даже на 100 мс, поэтому ты и админишь localhost
    А теперь касательно ionice, который лишний десяток раз будет дёргать scheduler по типу «а можно ли выполнить это сейчас? а никому ли я не наврежу? и т.д.». Надо ли это на высоконагруженных серверах? Нет.

  12. ZS :

    В целом точка зрения на быстрое удаление понятно. Но для информации:
    6.36user 17.28system 1:57.59elapsed 20%CPU (0avgtext+0avgdata 22788maxresident)k
    11726752inputs+0outputs (0major+11768minor)pagefaults 0swaps
    Или мне кажется или ls -1f использует всего ничего памяти. Т.е. выпадение по памяти здесь весьма сомнительно.

  13. > Xargs же, на основе этого составляет свой список по 10-100 файлов (хз, сколько за раз) и просто удаляет. Если ты не умеешь работать с find — твои проблемы.
    Интересно, с каких пор утилита для распараллеливания научилась сама файлы удалять?
    Удалять умеет find, но делает он это per-entry.

    > Тебе конечно пофиг, если база начала отвечать на 10 мс дольше, или даже на 100 мс, поэтому ты и админишь localhost
    да, пофиг, потому что у меня нет баз, которые нельзя отключать. Либо на них похуй всем (читай — девелопмент), либо есть ещё пара-тройка-десять таких баз (и на одну, опять же, всем насрать).

    > если база начала отвечать на 10 мс дольше, или даже на 100 мс
    Впрочем, если у тебя базы начинают отвечать на 10-100 мс дольше из-за того, что кто-то рядом елозит по дискам — i have bad news for you.

    > А теперь касательно ionice, который лишний десяток раз будет дёргать scheduler
    Ты точно понимаешь, как работает дефолтно ведущий себя дисковый шедуллинг в linux? Намекну — ionice это всего-лишь user-space утилита.

    > Надо ли это на высоконагруженных серверах? Нет.
    Поэтому у тебя на этих серверах FIFO-шедуллер уже включен?

    > поэтому ты и админишь localhost
    Да. localhost с желтой стрелкой на морде.

    Anyway, не хочешь удалять быстро — не удаляй быстро. Только не пиши неправду, тебя гугл индексирует как бэ.

  14. > Или мне кажется или ls -1f использует всего ничего памяти. Т.е. выпадение по памяти здесь весьма сомнительно.

    Да, ls -1f действительно живет. Впрочем, что с этим выхлопом потом делать — непонятно. Если в xargs rm передавать, то это много-много тяжелых exec() на бинарник (пусть и бинарник этот в памяти). А хвостом будут всё те же stat() + unlink()

Написать комментарий