Вопрос или проблема
У меня есть том btrfs, для которого я регулярно создаю снепшоты. Снепшоты ротациируются, самый старый из них – год. В результате, удаление крупных файлов может не освободить место в течение года после удаления.
Примерно год назад я скопировал раздел на больший диск, но старый также оставил.
Теперь новый диск оказался поврежден, и единственный способ извлечь данные – btrfs-restore
. Насколько я знаю, данные с нового диска все еще должны поместиться на старый, меньший диск, и файлы не так сильно изменяются (возможно добавляются новые или удаляются некоторые, но дополнительное место из-за снепшотов за год не должно быть большим). Поэтому я решил восстановить данные на старый диск.
Однако восстановленные данные заполнили старый диск намного быстрее, чем я ожидал. Я подозреваю, это связано с реализацией btrfs:
- Создание большого файла.
- Создание снепшота тома. Использование пространства не изменится, поскольку оба файла (оригинал и файл в снепшоте) ссылаются на один и тот же экстент на диске для своего содержимого. Однако изменение одного из двух файлов увеличит использование пространства из-за принципа copy-on-write в btrfs.
- Перезапись большого файла с идентичным содержимым. Я подозреваю, что использование пространства увеличивается на размер файла, потому что btrfs не распознает, что содержимое не изменилось. В результате, блоки, занимаемые файлом, копируются, и в конечном итоге создаются два одинаковых файла в двух разных наборах блоков.
Предлагает ли btrfs механизм для отмены этого путем нахождения файлов, которые “генетически связаны” (т.е. происходят от одного и того же файла путем его копирования и/или снепшотирования субтома, на котором они находятся), идентичны по содержимому, но хранятся в разных наборах блоков, и превращения их обратно в отражающие ссылки, чтобы освободить пространство?
Кратко: существуют инструменты для этого, но они не являются частью официального набора инструментов и возможно отсутствуют в репозиториях вашей дистрибуции. Вам придется выбрать из нескольких инструментов и, возможно, собрать его самостоятельно. Подробности ниже.
Инструменты
Вики-страница btrfs содержит статью о дедупликации, в которой упоминаются некоторые инструменты.
Существует больше инструментов – я рассматривал один из них, но он, похоже, не поддерживался в течение 6 лет на момент написания, поэтому я решил придерживаться того, что присутствует на вики btrfs.
Ни один из них пока не является частью официального набора инструментов btrfs, и пакеты Ubuntu для них находятся в наборе universe
– возможно, вам придется собрать их самостоятельно или отредактировать свою конфигурацию apt.
dduper
выглядит перспективно – он утверждает, что выполняет дедупликацию на уровне файлов и блоков (т.е. он дедуплицирует как целые файлы, так и блоки, идентичные между двумя или более файлами). Также считается, что он работает быстро, так как полагается на внутренние индексы btrfs. Будучи написан на Python, он не требует сборки перед использованием (требуется пакет prettytable
для Python на вашей машине). Однако, он, похоже, пропускает любые файлы размером ниже 4 КБ, что, по моему мнению, контрпродуктивно, если у вас много маленьких, одинаковых файлов.
Я решил использовать duperemove
, который выполняет дедупликацию только на уровне файлов. Помимо окружения для сборки на C и autotools, вам понадобится пакет libsqlite3-dev
на вашей машине. Скачайте исходники и соберите их, выполнив make
в каталоге с исходниками. duperemove
может быть запущен непосредственно из каталога с исходниками для тех, кто не хочет make install
случайные вещи на своей системе. В качестве альтернативы, установите и запустите из репозиториев.
Запуск duperemove
Документация docs упоминает два способа запуска duperemove
: напрямую или путем запуска fdupes
и передачи его вывода в duperemove
. Первый из них рекомендуется только для небольших наборов данных. Второй, однако, оказался чрезвычайно ресурсоемким для моего набора данных объемом 2–3 ТБ и около 4 миллионов файлов (через день прогресс составил около половины процента, а использование памяти вместе с постоянной свопингом делали систему почти неработоспособной).
Что, по-видимому, работает для меня – это
sudo duperemove -drA /foo --hashfile=/path/to/duperemove.hashfile
Это сделает следующее:
- Запустите весь процесс от имени root, так как у моего аккаунта нет доступа ко всему на диске
- Дедупликация результатов (
-d
), в отличие от простого сбора хэш-сумм и вывода списка дубликатов - Рекурсивное сканирование подкаталогов заданного пути (
-r
) - Откройте файлы в режиме только для чтения для дедупликации (
-A
, необходимо, потому что мои снепшоты только для чтения) - Сохраните хэши в файл, а не в память (
--hashfile
), что дает два преимущества:- Процесс можно прервать в любое время и возобновить позже, пока файл хэшей остается на месте
- Процесс не будет занимать память на вашей системе (в отличие от режима
fdupes
), хотя файл хэша занимает место на диске: по 90 байт на блок и 270 байт на файл.
duperemove
выполняется в нескольких фазах:
- Индексирование содержимого диска (что хранится в файле хэшей)
- Загрузка дублирующих хэшей в память
- Дедупликация файлов
duperemove
можно прервать и возобновить в любой момент, пока остается на месте база данных индекса.
Результаты теста
Я запускал duperemove
на диске с 8 субтомами, 5 из которых регулярно снапшотируются, с сохранением 24 снепшотов (последние 12 ежемесячных, 5 еженедельных и 7 ежедневных). Включая снепшоты, диск содержит 5–6 миллионов файлов, занимающих 3,5 ТБ (до дедупликации, ожидается 1,8 ТБ после дедупликации).
Когда duperemove
начинает работать и отображать прогресс, процент указывает только на фазу индексирования, а не на весь процесс дедупликации. Загрузка дублирующих хэшей может занимать намного меньше или намного больше времени, в зависимости от того, сколько блоков было проверено. Дедупликация снова занимает приблизительно столько же времени, сколько и индексирование. Кроме того, расчет прогресса для фазы индексирования, по-видимому, основан только на количестве файлов, а не блоков, что делает его ненадежным показателем общего времени/пространства, если все ваши большие файлы, как правило, находятся в начале или конце набора.
Использование ресурсов во время фазы индексирования достаточно низкое, чтобы моя система оставалась отзывчивой при использовании файла хэшей, хотя загрузка дублирующих данных может съесть вашу свободную память. Если база данных индекса больше, чем количество свободной памяти на вашей системе, это может вызвать чрезмерный свопинг и замедлить работу системы.
Полное индексирование (с размером блока по умолчанию 128K) заняло около 28 дней и создало файл хэша размером 21 ГБ. У меня закончилась память на 36-м дне, что сделало мою систему неотзывчивой, поэтому я был вынужден прервать работу. Использование памяти процессом duperemove
колебалось в пределах 12–14 ГБ в течение четырех дней, хотя общее использование памяти продолжало увеличиваться, пока система не стала неработоспособной.
Для следующих попыток я решил выполнять дедупликацию по субтомам, с еще одним запуском между отрезками двух субтомов, где я знаю, что перемещал данные. Я начал с размера блока 1024K, хотя это будет пропускать меньшие дублирующие блоки, а также файлы, меньшие, чем размер блока, в обмен на лучшую производительность. Это заняло около 24 часов и освободило около 45 ГБ на моем диске – удовлетворительная производительность, но экономия пространства не то, что я ожидал.
Я прервал еще одну попытку с 4K на субтоме объемом 300G – индексирование заняло примерно в четыре раза больше времени по сравнению с 1024K, но через 3 дня загрузка дублирующих хэшей всё еще не была завершена. Еще одна попытка с 64K завершилась менее чем за 4 часа. Учтите, что для любой последующей проходки после первой дедупликация должна завершиться быстрее, так как остаются только небольшие блоки для дедупликации.
Поэтому мои рекомендации, основанные на практическом опыте:
- Расположение файла хэша важно:
- Убедитесь, что у вас достаточно места на диске (у меня его не было, поэтому пришлось прервать, переместить файл и возобновить).
/tmp
может быть неудачной идеей, так как он может быть очищен при перезагрузке (даже если вы не планируете перезагружаться, может быть стоит подстраховаться в случае сбоя системы или отключения питания).- Если у вас зашифрован домашний каталог, производительность может пострадать, согласно моим наблюдениям. По моему опыту, внешний USB-диск казался работать быстрее, чем зашифрованная домашняя директория на внутреннем жестком диске.
- Данные не могут быть дедуплицированы между двумя (или более) только для чтения субтомами. Если у вас несколько только для чтения снепшотов одного субтома, выберите пути так, чтобы сканировался один из этих снепшотов – обычно самый последний или тот, который вы планируете оставить на дольше всего. Сканирование всех снепшотов займет намного больше времени и не увеличит эффективность – если только некоторые файлы в текущем наборе не совпадают с более ранним снепшотом, а не последним.
- Выполняйте дедупликацию частями – разбейте свою файловую систему на фрагменты, которые, по вашему мнению, не содержат большого количества дублирующихся данных. Это сэкономит место при загрузке дублирующих хэшей.
- Определите оптимальный размер блока, как описано в Choosing the right block size for duperemove. Для моего диска 16K хорошо работало для частями объемом 0.6–1 ТБ (до дедупликации), в то время как при 8K загрузка дублирующих хэшей увеличилась с нескольких часов до более чем 2 дней – это значит, что 16K это оптимальный вариант для меня. С меньшим объемом 4K работало хорошо.
Ответ или решение
Консолидация и дедупликация данных на файловой системе btrfs — это сложный процесс, который требует понимания как работы самой файловой системы, так и инструментов, которые могут помочь эффективно управлять данными. Btrfs, или подвижная файловая система нового поколения, предлагает множество преимуществ, включая поддержку мгновенных снимков (snapshots) и копирования при записи (copy-on-write), что делает её особенно привлекательной для системных администраторов и экспертов по ИТ. Однако эти же характеристики могут стать причиной неожиданного роста использования дискового пространства, особенно при интенсивном использовании снимков.
Теория
Когда используется btrfs, данные часто организуются через использование снимков, что позволяет хранить множество различных состояний файловой системы без значительного увеличения занимаемого пространства. Каждая такая "копия" занимает столько же места, сколько и сама ссылка на неё, так как изменения данных происходят по принципу copy-on-write. Проблемой становится ситуация, когда изменения в самих файлах не отслеживаются на уровне возникающего дублирования данных, то есть фактически одинаковые данные могут занимать различное физическое пространство на диске.
Эта проблема становится более заметной, когда вы работаете с большими объемами данных и регулярно создаёте снимки. В процессе работы файлы могут изменяться, удаляться и создаваться новые, однако старые версии этих файлов продолжают занимать место в системе.
Пример
Рассмотрим пример, где на новом диске btrfs возникла потребность в удалении данных из-за их дублирования:
- Был создан большой файл на дисковой системе.
- После создания файла была выполнена операция снятия снимка всего объема. Поскольку btrfs при создании снимка использует ссылки на оригинальные данные, это не приводит к увеличению использования пространства в данный момент.
- Далее вносились изменения в файл или он был переопределен, в результате чего создавалась копия-блок (или множество копий), которые фактически занимали новое пространство на диске.
В рассматриваемом контексте, несмотря на формальное удаление файлов, из-за наличия множества снимков, "удаленные" файлы сохраняются в этих снимках, и место фактически не освобождается.
Применение
На практике для решения этой задачи требуется процесс дедупликации, именно он позволит освободить занятое местами пространство, которое остаётся захвачено из-за существования нескольких копий одного и того же контента.
Существуют инструменты, которые позволяют провести дедупликацию данных, однако важно иметь в виду, что ни один из них на данный момент не входит в состав официального инструментария btrfs. Вот некоторые из них:
-
dduper: Этот инструмент приглянулся пользователям за счет того, что он выполняет как файловое, так и блочное дедуплицирование. Программа написана на Python и не требует компиляции, но имейте в виду, что она пропускает файлы менее 4 КБ.
-
duperemove: Инструмент, который выполняет файловое дедуплицирование. Требует для работы C-среду сборки и libsqlite3-dev. Вы можете собрать его из исходников и запустить напрямую из каталога исходников.
Запуск duperemove
может быть осуществлен различными способами, один из рекомендуемых для больших наборов данных:
sudo duperemove -drA /foo --hashfile=/path/to/duperemove.hashfile
Этот способ позволяет:
- Выполнить процесс под root, чтобы иметь полный доступ к файлам.
- Сохранить хэш в файле, чтобы избегать использования оперативной памяти, предоставляя возможность прервать и затем продолжить процесс.
При дедупликации необходимо уделить внимание выбору размера блока, также правильному расположению файла хэшей, чтобы избежать недостатка дискового пространства и ненужных перезаписей, которые могут замедлять процесс.
Ключевым моментом успешной дедупликации в btrfs будет стратегический подход — разбиение процесса на этапы и сегменты файловой системы, например, обработка отдельных подсетей вместо всего диска целиком, выбор оптимального размера блока (на основе опыта — например, 16K), а также необходимость исключения непродуктивных сканирований старых снимков, если они не содержат уникальных данных.
В итоге, применение инструментов для дедупликации и внимательное управление снимками может существенно оптимизировать использование места на вашем btrfs-томе, эффективно освобождая значительный объём ранее недоступных ресурсов на диске.