Предыстория: физический сервер, около двух лет, диски SATA 7200 об / мин, подключенные к RAID-карте 3Ware, noatime с установленной файловой системой ext3 FS и данные = заказано, без сумасшедшей нагрузки, ядро 2.6.18-92.1.22.el5, время безотказной работы 545 дней , Каталог не содержит никаких подкаталогов, только миллионы маленьких (~ 100 байт) файлов, с некоторыми большими (несколько КБ).
У нас есть сервер, который за последние несколько месяцев немного кукушка, но мы заметили это только на днях, когда он не смог выполнить запись в каталог из-за слишком большого количества файлов. В частности, он начал выдавать эту ошибку в / var / log / messages:
ext3_dx_add_entry: Directory index full!
На рассматриваемом диске осталось много инодов:
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/sda3 60719104 3465660 57253444 6% /
Так что я предполагаю, что это означает, что мы достигли предела того, сколько записей может быть в самом файле каталога. Не знаю, сколько будет файлов, но, как видите, не может быть больше, чем три миллиона или около того. Не то чтобы это хорошо, заметьте! Но это первая часть моего вопроса: что это за верхний предел? Это настраивается? Прежде чем орать на меня - я хочу смягчить это ; этот огромный каталог вызвал всевозможные проблемы.
В любом случае, мы отследили проблему в коде, который генерировал все эти файлы, и исправили его. Теперь я застрял с удалением каталога.
Несколько вариантов здесь:
rm -rf (dir)
Я попробовал это первым. Я сдался и убил его после того, как он проработал полтора дня без какого-либо заметного воздействия.
- unlink (2) для каталога: безусловно, стоит подумать, но вопрос в том, будет ли быстрее удалять файлы внутри каталога через fsck, чем удалять через unlink (2). То есть, так или иначе, я должен пометить эти inode как неиспользованные. Это предполагает, конечно, что я могу сказать fsck не сбрасывать записи в файлы в / lost + found; в противном случае я просто перенес свою проблему. В дополнение ко всем другим проблемам, после прочтения об этом, оказывается, что мне, вероятно, придется вызывать некоторые внутренние функции FS, поскольку ни один из найденных мной вариантов unlink (2) не позволил бы мне просто беспечно удалить каталог с записями в нем. Пух.
while [ true ]; do ls -Uf | head -n 10000 | xargs rm -f 2>/dev/null; done )
Это на самом деле сокращенная версия; Реальный, который я запускаю, который просто добавляет отчеты о прогрессе и чистую остановку, когда у нас заканчиваются файлы для удаления, это:
экспорт я = 0; время (в то время как [верно]; сделать ls -Uf | голова -n 3 | grep -qF '.png' || сломать; ls -Uf | голова -n 10000 | xargs rm -f 2> / dev / null; export i = $ (($ i + 10000)); echo "$ i ..."; сделанный )
Кажется, это работает довольно хорошо. Когда я пишу это, он удалил 260 000 файлов за последние тридцать минут или около того.
- Как упомянуто выше, настраивается ли ограничение на количество записей в каталоге?
- Почему потребовалось «реальные 7m9.561s / пользователь 0m0.001s / sys 0m0.001s», чтобы удалить один файл, который был первым в списке, возвращаемом пользователем
ls -U
, и, возможно, потребовалось десять минут, чтобы удалить первые 10000 записей с команда в # 3, но теперь она довольно счастливо тащится? В этом отношении он удалил 260 000 примерно за тридцать минут, но теперь требуется еще пятнадцать минут, чтобы удалить еще 60 000. Почему огромные колебания в скорости? - Есть ли лучший способ сделать это? Не хранить миллионы файлов в каталоге; Я знаю, что это глупо, и это не случилось бы на моих часах. Погугление проблемы и просмотр SF и SO предлагает множество вариантов
find
, которые не будут значительно быстрее моего подхода по нескольким очевидным причинам. Но есть ли у идеи delete-via-fsck ноги? Или что-то еще целиком? Мне не терпится услышать нестандартное (или не очень известное) мышление.
Окончательный вывод сценария !:
2970000...
2980000...
2990000...
3000000...
3010000...
real 253m59.331s
user 0m6.061s
sys 5m4.019s
Таким образом, три миллиона файлов были удалены за чуть более четырех часов.
rm -rfv | pv -l >/dev/null
, PV должен быть доступен в репозитории EPEL .Ответы:
Опция
data=writeback
монтирования заслуживает того, чтобы ее попытались предотвратить ведение журнала файловой системы. Это должно быть сделано только во время удаления, однако существует риск, если сервер отключается или перезагружается во время операции удаления.Согласно этой странице ,
Опция устанавливается во
fstab
время или в процессе монтирования, заменяяdata=ordered
наdata=writeback
. Файловая система, содержащая файлы для удаления, должна быть перемонтирована.источник
commit
опции : «Это значение по умолчанию (или любое низкое значение) ухудшит производительность, но это хорошо для безопасности данных. Установка его в 0 будет иметь тот же эффект, что и установка по умолчанию (5 секунд). ). Установка очень больших значений улучшит производительность ".data=writeback
все еще журналы метаданных перед записью их в основную файловую систему. Насколько я понимаю, он просто не навязывает порядок между такими вещами, как запись карты экстентов и запись данных в эти экстенты. Может быть, есть и другие ограничения в отношении порядка, которые тоже ослабляют, если вы видели от этого выигрыш в производительности. Конечно, установка без журнала вообще могла бы быть даже более высокой производительностью. (Это может позволить изменениям метаданных просто происходить в ОЗУ, без необходимости иметь что-либо на диске до завершения операции unlink).Хотя основной причиной этой проблемы является производительность ext3 с миллионами файлов, действительная основная причина этой проблемы другая.
Когда каталог должен быть в списке, вызывается readdir () для каталога, который выдает список файлов. readdir - это вызов posix, но используемый здесь системный вызов Linux называется «getdents». Получатели перечисляют записи каталога, заполняя буфер записями.
Проблема в основном связана с тем, что readdir () использует фиксированный размер буфера 32 Кб для извлечения файлов. По мере того, как каталог становится все больше и больше (размер увеличивается с добавлением файлов), ext3 становится все медленнее и медленнее для извлечения записей, а дополнительный размер буфера readdir 32 КБ достаточен только для включения части записей в каталоге. Это заставляет readdir зацикливаться снова и снова и вызывать дорогой системный вызов снова и снова.
Например, в тестовом каталоге, который я создал с более чем 2,6 миллионами файлов внутри, выполнение «ls -1 | wc-l» показывает большой вывод результатов многих системных вызовов getdent.
Кроме того, время, проведенное в этом каталоге, было значительным.
Чтобы сделать этот процесс более эффективным, нужно вызывать getdents вручную с гораздо большим буфером. Это значительно повышает производительность.
Теперь вы не должны сами вызывать getdents вручную, поэтому не существует интерфейса для его нормального использования (проверьте страницу man, чтобы увидеть getdents!), Однако вы можете вызвать его вручную и сделать свой вызов системным вызовом более эффективным.
Это значительно сокращает время загрузки этих файлов. Я написал программу, которая делает это.
Хотя это не решает основную фундаментальную проблему (много файлов в файловой системе, которая плохо работает с ней). Вероятно, это будет намного, намного быстрее, чем многие из опубликованных альтернатив.
В качестве предварительного решения следует удалить поврежденный каталог и переделать его после. Каталоги только увеличиваются в размере и могут работать плохо даже с несколькими файлами внутри из-за размера каталога.
Редактировать: я убрал это совсем немного. Добавлена опция, позволяющая вам удалять из командной строки во время выполнения, и удалялась куча вещей из Treewalk, которые, честно говоря, в лучшем случае сомнительны. Также было показано, чтобы произвести повреждение памяти.
Теперь вы можете сделать
dentls --delete /my/path
Новые результаты. Исходя из каталога с 1,82 миллиона файлов.
Был немного удивлен, что все еще так хорошо работает!
источник
[256]
вероятно[FILENAME_MAX]
, и во-вторых, мой Linux (2.6.18 == CentOS 5.x), по-видимому, не содержит запись d_type в dirent (по крайней мере, согласно getdents (2)).Можно ли было бы сделать резервную копию всех других файлов из этой файловой системы во временное хранилище, переформатировать раздел, а затем восстановить файлы?
источник
В ext3 не существует предельного значения для каждого каталога, только ограничение inode для файловой системы (я думаю, что существует ограничение на количество подкаталогов).
У вас могут остаться проблемы после удаления файлов.
Когда в каталоге содержатся миллионы файлов, сама запись в каталоге становится очень большой. Запись каталога должна проверяться для каждой операции удаления, и для каждого файла требуется различное количество времени, в зависимости от того, где находится его запись. К сожалению, даже после того, как все файлы были удалены, запись каталога сохраняет свой размер. Таким образом, дальнейшие операции, требующие сканирования записи каталога, все равно будут занимать много времени, даже если каталог теперь пуст. Единственный способ решить эту проблему - переименовать каталог, создать новый со старым именем и перенести все оставшиеся файлы в новый. Затем удалите переименованный.
источник
Я не проверял это, но этот парень сделал :
источник
find просто не работает для меня, даже после изменения параметров ext3 fs, как предложено пользователями выше. Потребляется слишком много памяти. Этот скрипт PHP добился цели - быстрое, незначительное использование процессора, незначительное использование памяти:
Я опубликовал отчет об ошибке, связанной с этой проблемой, с find: http://savannah.gnu.org/bugs/?31961
источник
Недавно я столкнулся с подобной проблемой и не смог заставить работать
data=writeback
предложение ring0 (возможно, из-за того, что файлы находятся на моем основном разделе). Исследуя обходные пути, я наткнулся на это:Это полностью отключит ведение журнала, независимо от того, какой
data
параметр датьmount
. Я совместил это с тем,noatime
что громкостьdir_index
установилась, и это, казалось, работало довольно хорошо. Удаление фактически завершилось, и мне не пришлось его убивать, моя система оставалась отзывчивой, и теперь она снова запущена (с включенным ведением журнала) без проблем.источник
Убедитесь, что вы делаете:
что должно немного ускорить процесс.
источник
Очень медленная команда. Пытаться:
источник
strace -r -p <pid of rm>
чтобы присоединиться к уже запущенному процессу rm. Затем вы можете увидеть, как быстроunlink
проходят системные вызовы. (-r
помещает время после предыдущего системного вызова в начале каждой строки.)Является ли
dir_index
установить для файловой системы? (tune2fs -l | grep dir_index
) Если нет, включите его. Обычно для нового RHEL.источник
Пару лет назад я нашел каталог с 16 миллионами файлов XML в
/
файловой системе. Из-за критики сервера мы использовали следующую команду, которая заняла около 30 часов :Это был старый жесткий диск со скоростью 7200 об / мин , и, несмотря на узкое место ввода-вывода и пики ЦП, старый веб-сервер продолжал свою работу.
источник
Мой предпочтительный вариант - уже предложенный подход newfs. Основная проблема заключается в том, что, как уже отмечалось, линейное сканирование для удаления проблематично.
rm -rf
должно быть почти оптимальным для локальной файловой системы (NFS будет другой). Но при миллионах файлов, 36 байтов на имя файла и 4 на индекс (предположение, не проверяя значение для ext3), это 40 * миллионов, которые должны храниться в оперативной памяти только для каталога.По-видимому, вы перебиваете кэш-память метаданных файловой системы в Linux, так что блоки для одной страницы файла каталога удаляются, пока вы все еще используете другую часть, только для того, чтобы снова попасть на эту страницу кэша, когда следующая файл удален Настройка производительности Linux не является моей областью, но / proc / sys / {vm, fs} /, вероятно, содержит что-то важное.
Если вы можете позволить себе простои, вы можете включить функцию dir_index. Он переключает индекс каталога с линейного на нечто гораздо более оптимальное для удаления в больших каталогах (хэшированные b-деревья).
tune2fs -O dir_index ...
затемe2fsck -D
будет работать. Однако, хотя я уверен, что это поможет до возникновения проблем, я не знаю, как выполняется преобразование (e2fsck с-D
) при работе с существующим каталогом v.large. Резервное копирование + сосать-и-посмотреть.источник
/proc/sys/fs/vfs_cache_pressure
может быть полезным значением, но я не знаю, относится ли сам каталог к кешу страниц (потому что это так) или кешу узлов (потому что, несмотря на то, что он не является inode, это метаданные FS, и по этой причине они включены в него). Как я уже сказал, настройка виртуальной машины Linux не моя область. Играй и смотри, что помогает.Очевидно, что от яблок к яблокам здесь нет, но я настроил небольшой тест и сделал следующее:
Создано 100 000 512-байтовых файлов в каталоге (
dd
и/dev/urandom
в цикле); забыл примерить время, но создание этих файлов заняло примерно 15 минут.Выполнить следующее, чтобы удалить указанные файлы:
ls -1 | wc -l && time find . -type f -delete
Это Pentium 4 с частотой 2,8 ГГц (пара сотен гигабайт IDE 7200 об / мин, я думаю; EXT3). Ядро 2.6.27.
источник
rm
это ужасно медленно на большом количестве файлов, отсюда иfind -delete
вариант. С подстановочным знаком на оболочке он будет расширять каждое совпадающее имя файла, и я предполагаю, что для этого существует ограниченный буфер памяти, чтобы вы могли видеть, как это может стать неэффективным.Иногда Perl может творить чудеса в подобных случаях. Вы уже пробовали, если такой маленький сценарий может превзойти bash и основные команды оболочки?
Или другой, возможно, даже более быстрый подход Perl:
РЕДАКТИРОВАТЬ: я только что попробовал мои сценарии Perl. Более многословный делает что-то правильно. В моем случае я попробовал это с виртуальным сервером с 256 МБ ОЗУ и полмиллиона файлов.
time find /test/directory | xargs rm
Результаты:по сравнению с
источник
*(oN)
]Из того, что я помню, удаление inode в файловых системах ext - O (n ^ 2), поэтому чем больше файлов вы удаляете, тем быстрее будут работать остальные.
Был один раз, когда я столкнулся с подобной проблемой (хотя мои оценки оценивали время удаления ~ 7 часов), в конце концов, в первом комментарии был предложен маршрут, предложенный jftuga .
источник
Ну, это не настоящий ответ, но ...
Можно ли будет конвертировать файловую систему в ext4 и посмотреть, что изменится?
источник
Хорошо, это было рассмотрено различными способами в остальной части потока, но я думал, что я добавлю свои два цента. Виновником производительности в вашем случае, вероятно, является readdir. Вы получаете список файлов, которые не обязательно каким-либо образом последовательны на диске, что приводит к доступу к диску повсюду, когда вы отключаете связь. Файлы настолько малы, что операция unlink, вероятно, не слишком быстро обнуляет пространство. Если вы прочитаете dir, а затем отсортируете по возрастанию inode, вы, вероятно, получите лучшую производительность. Так что читайте в RAM (сортировка по inode) -> unlink -> profit.
Я думаю, что Inode - это грубое приближение ... но, исходя из вашего варианта использования, он может быть довольно точным ...
источник
Я бы, вероятно, вытащил компилятор C и сделал бы моральный эквивалент вашего скрипта. То есть используйте
opendir(3)
для получения дескриптора каталога, затем используйтеreaddir(3)
для получения имени файлов, затем подсчитывайте файлы по мере их отсоединения и время от времени печатайте «% d удаленных файлов» (и, возможно, истекшее время или текущую метку времени).Я не ожидаю, что он будет заметно быстрее, чем версия сценария оболочки, просто я привык время от времени вырывать компилятор, потому что нет чистого способа сделать то, что я хочу из оболочки, или потому что в то время как выполнимо в оболочке, это непродуктивно медленно.
источник
Скорее всего, у вас возникли проблемы с перезаписью каталога. Попробуйте сначала удалить самые новые файлы. Посмотрите на параметры монтирования, которые будут откладывать обратную запись на диск.
Для индикатора прогресса попробуйте запустить что-то вроде
rm -rv /mystuff 2>&1 | pv -brtl > /dev/null
источник
Вот как я удаляю миллионы файлов трассировки, которые иногда могут собираться на большом сервере базы данных Oracle:
Я считаю, что это приводит к довольно медленному удалению, которое слабо влияет на производительность сервера, обычно это примерно час на миллион файлов при «типичной» настройке 10000 IOPS.
Для сканирования каталогов, создания исходного списка файлов и удаления первого файла часто требуется несколько минут. Оттуда и далее, а. отображается для каждого удаленного файла.
Задержка, вызванная отражением в терминале, оказалась достаточной задержкой, чтобы предотвратить любую значительную нагрузку во время удаления.
источник
find /u* -maxdepth 3 -mindepth 3 -type d -path '*/app/*' -name diag -print0 | xargs -0I = find = -mindepth 4 -maxdepth 4 -type d -name 'trace' -print0 | xargs -0I = find = -mindepth 1 -maxdepth 1 -name '*.tr'
:? Добавьте-delete
к последнему, чтобы фактически удалить вещи; как написано, он просто перечисляет, что он будет удалять. Обратите внимание, что это оптимизировано для обстоятельств, когда у вас есть много неинтересных вещей в соседних каталогах; если это не так, вы можете значительно упростить логику.rm
произойдет первый ), поэтому при запуске вы получаете относительно эффективный ввод-вывод, за которым следуют болезненные неупорядоченныеrm
операции. это, вероятно, не вызывает большого количества операций ввода-вывода, но требуетscandir
многократного обхода каталога (не вызывает ввода-вывода, поскольку оно уже загружено в кэш блоков; см. такжеvfs_cache_pressure
). Если вы хотите замедлить процесс,ionice
это вариант, но я бы, вероятно, использовал дробные секундыsleep
.find /u*/app/*/diag -path '*/trace/*.tr' -execdir rm {} +
будет запускаться по одномуrm
на каталог, так что вы будете иметь меньше нагрузки на процессор. Думаю, до тех пор, пока у вас есть тонны процессорного времени, дросселирование дискового ввода-вывода путем создания целогоrm
процесса для каждойunlink
работы, но это уродливо. Perl со сном на unlink будет лучше, если сон междуrm
целыми каталогами за раз слишком бурный. (-execdir sh -c ...
возможно)Вы можете использовать функции распараллеливания 'xargs':
источник
источник
на самом деле, это немного лучше, если используемая оболочка выполняет расширение командной строки:
источник