Почему перемещение некоторых файлов в папке занимает больше времени, чем перемещение всей папки?

21

У меня есть миллионы изображений на моем облачном сервере Ubuntu. Когда я перемещаю всю папку, содержащую 12 миллионов изображений, используя mvкоманду, это происходит почти мгновенно. Однако, когда я mvтолько изображения (не папки), это занимает некоторое время. Есть ли способ перемещать все изображения так же быстро, как папки?

Вот что происходит:

  1. В папке src 12 миллионов изображений, и я перемещаю это в папку dst, используя

    $ mv  src ../dst
    

    Происходит сразу

  2. Внутри папки src я делаю это для перемещения:

    find -maxdepth 1 -name '*.jpg' -exec mv -t ../../dst/ {} +
    

    Это займет некоторое время.

Есть ли способ ускорить второй процесс?

sankit
источник
1
Не решение, но чтобы уточнить: cmd2 должен быть медленнее, чем cmd1, так как он использует find, а затем выполняет перемещение для результата. Это никогда не может быть таким же быстрым, как прямое движение без предварительного поиска.
Dufte
возможно dstнаходится в разделе, тогда как ../../dstнаходится на другом.
phuclv
Как написано, это даже не выглядит как действительный вызов find. Не хватает {}аргумента, где имя файла (ов) будет расширен.
R ..
Я представил правку, которая изменяет заголовок, удаляя ссылку на «изображения» и заменяя ее суть вопроса - это перемещение отдельных файлов вместо перемещения всей папки. Я надеюсь, что это принято кем-то с представителем, чтобы сделать это.
Монти Хардер
1
Это не действительный вызов find. find ... -exec mv -t ../../dst/ {} \;будет вызывать mvодин раз за файл; find ... -exec mv -t ../../dest {} +было бы намного быстрее, копируя столько файлов за вызов, сколько возможно, но все же не так быстро, как перемещение самой директории, как объясняет dadexix86 .
chepner

Ответы:

50

TL; DR : нет

Для меньшего количества файлов вам это не понадобится, findно даже в этом упрощенном и меньшем случае, если вы просто

mv *.jpg ../../dst/

это займет больше времени, чем перемещение всего каталога за один раз.


Зачем? Дело в том, чтобы понять, что mvделает.

Вкратце, mvперемещает число (которое идентифицирует каталог или файл) из inode (каталог, содержащий его) в другой, и эти индексы обновляются в журнале файловой системы или в FAT (если файловая система). реализован таким образом).

Если источник и пункт назначения находятся в одной файловой системе, фактическое перемещение данных отсутствует, оно просто меняет положение, точку, к которой они присоединены.

Таким образом, когда вы mv один каталог, вы делаете эту операцию один раз .

Но когда вы перемещаете 1 миллион файлов, вы выполняете эту операцию 1 миллион раз .

Чтобы дать вам практический пример, у вас есть дерево с множеством ветвей. В частности, есть один узел, к которому прикреплено 1 миллион веток.
Чтобы вырезать эти ветви и переместить их в другое место, вы можете вырезать каждый из них, чтобы сделать 1 миллион разрезов, или вырезать непосредственно перед узлом, таким образом, делая только один разрез (в этом разница между перемещением файлов и каталог).

dadexix86
источник
4
Вы должны указать, что в mvтой же файловой системе это просто перезапись записи TOC.
Видеонавт
Я не уверен, что понимаю, что вы подразумеваете под TOC. Насколько я знаю, в файловых системах ext, NTFS, btrfs и так далее нет таблиц. FAT имеет таблицу (из которой он берет имя), но, например, ext хранит имена и блоки, а также родителей, детей и другую информацию в inode. Если вы можете указать мне ссылку, где объясняется, где у ext FS есть оглавление и для чего она используется, я с удовольствием прочитаю и
обновлю
10
Um. mv *.jpgвероятно, потерпит неудачу для 12 миллионов файлов, поэтому он использует find. Я полагаю, что большинство Unix, включая Linux (если кто-то не изменял его за последние 5-10 лет), имеют ограниченную максимальную длину командной строки. Я думаю, что это было 64K для Linux в течение длительного времени. То же самое ограничение применяется к переменным среды, я почти уверен.
Zan Lynx
1
Перемещение файла больше о перемещении его имени . Unix-подобные записи каталога содержат имя файла и номер индекса, который в основном является указателем на остальную часть метаданных. Каталог - это просто особый вид файла. Сам инод не содержит фактических данных файла, только указатели на него, поэтому немного ошибочно утверждать, что что-либо перемещено из инода. С другой стороны, журналы файловой системы обычно ссылаются на тип журнала метаданных, который в основном используется для защиты от сбоев.
ilkkachu
1
Конечно, терминология здесь не главное. Важным моментом является именно то, что вы сказали: внутри файловой системы перемещение должно касаться только метаданных. Из одной файловой системы в другую ярлык отсутствует, и все файлы необходимо перемещать (создавать заново) по одному, включая их содержимое. В этом случае не имеет значения, перемещаете ли вы весь каталог или только файлы внутри, это будет примерно так же медленно.
ilkkachu
13

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

Тем не менее, вы можете ускорить его с того, что у вас есть сейчас.

Ваша команда find запускает exec один раз для каждого файла. Таким образом, он запускает mvкоманду 12 миллионов раз для 12 миллионов файлов. Это можно улучшить двумя способами.

  • Добавьте плюс в конце:
    find -maxdepth 1 -name '*.jpg' -exec mv -t ../../dst/ +
    проверьте страницу руководства, чтобы убедиться, что она поддерживается в вашей версии find. Эффект должен состоять в том, чтобы запустить серию mvкоманд с таким количеством имен файлов, которое поместится в каждой командной строке.

  • Используйте findи xargsвместе.
    find -maxdepth 1 -name '*.jpg' -print0 | xargs -0 mv -t ../../dst/
    Для -print0разделения имен файлов будет использоваться NUL, то есть нулевые байты. Этот плюс xargs -0устраняет любые проблемы, xargsкоторые в противном случае имели бы пробелы в именах файлов. Команда xargsпрочитает список имен файлов из findкоманды и запустит mvкоманду с тем количеством имен файлов, которое подходит.

Зан Рысь
источник
7

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

Когда вы «перемещаете» файлы внутри файловой системы, реальные файлы никуда не уходят. Скорее, списки внутри каталогов обновляются для отражения изменений.

mv src ../dstперемещает одну запись списка из каталога .в каталог ../dst, так что это быстро.

find -maxdepth 1 -name '*.jpg' -exec mv -t ../../dst/приходится перемещать миллионы записей, поэтому это медленнее. Это может быть ускорено, если вы вызываете mvтолько один раз, а не один раз для файла, и сама mvкоманда может быть оптимизирована для перемещения нескольких записей каталога за один шаг, но нет способа сделать это так же быстро, как при перемещении одного каталога ,

Дмитрий Григорьев
источник
4

Упрощенный ответ

перемещение файла выполняется за 3 шага:

  • добавить () ссылку на файл в список индексов папки назначения
  • проверить, была ли ссылка успешно добавлена
  • удалите () ссылку из списка inode исходной папки, если проверка выше прошла успешно.

этот процесс одинаков для файла или папки.
и очевидно, что сделать это для 1 файла на 100 быстрее, чем для 100 файлов.

man link is add ()
man unlinkэто удаление ()
mvпросто использует эти две команды выше и добавляет промежуточную проверку для предотвращения потери данных.


источник
1
Ну, есть также переименовать ().
ilkkachu