Предыстория

При проектировании программного обеспечения порой возникают неожиданные вопросы. Мой коллега задал один такой вопрос, когда решал, как устроить работу с файлами в веб-приложении. Методы взаимодействия с файлами давно отработаны, и существуют две основные стратегии: хранить файлы прямо в базе данных или записывать в базу данных только ссылки, а сам файл оставить в файловой системе сервера.
Коллега решил рассмотреть третий вариант (который впоследствии был аргументированно отвергнут). Один и тот же файл по логике приложения нужно использовать в нескольких местах (во многих местах), для этого он предложил просто задавать имя по шаблону, а для множественного использования создавать ссылки (символьные или жёсткие в файловой системе, не веб ссылки типа a href=...).
Символьные ссылки знакомы большинству разработчиков и системных администраторов и не вызывают вопросов. Однако hard link — вещь менее очевидная, особенно для разработчиков на скриптовых языках (PHP, JavaScript, Python). Желая заполнить этот пробел, я и пишу эту статью. Рассматривать буду на примере Windows, т. к. пользователи Linux, как правило, лучше разбираются в этой теме, по крайней мере иногда сталкиваются. А вот Windows разработчики (всё на тех же скриптовых языках) и системные администраторы могут десятилетиями успешно работать, не касаясь этой темы.

Чуть-чуть теории (всего один абзац!)

Файлы в операционной системе — это не более, чем ссылки на определённые области памяти жёсткого диска. Другими словами, данные хранятся на накопителе в виде последовательностей нулей и единиц, например "01001001". Чтобы получить доступ к этим данным, операционная система создаёт ссылку (link), которую пользователь видит как "файл". Назовём его "number.txt".

Эту структуру можно сравнить с веб-ссылкой: к примеру, habr.com указывает на IP-адрес 178.248.233.33, по которому мы получаем доступ к содержимому сайта. Подобно этому, файл number.txt на рабочем столе, на самом деле, — это только ссылка, указывающая на область в памяти диска допустим 0x7ffd3b56a4d0, где находятся данные «01001001». Всё, что видит пользователь, — это интерфейс, «ярлык», который позволяет обращаться к данным по нужному адресу. Но не надо путать это с тем, что сам Windows называет ярлык, об этой сущности в данной статье вообще не говорим.

Демонстрация работы hard links

Чтобы понять, как работают hard link, проведём небольшой эксперимент на Windows. Для этого потребуется лишь командная строка.

  1. Сначала посмотрите, сколько свободного места на жёстком диске

  2. Откройте командную строку Win + R, затем введите cmd

  3. Перейдите на рабочий стол командой cd Desktop

  4. Создайте файл, например, размером 10 ГБ, используя команду: fsutil file createnew bigfile.txt 10737418240. После создания файла на диске уменьшится количество свободного места на 10 ГБ

  5. Попробуйте скопировать файл. После создания копии свободное место уменьшится ещё на 10 ГБ, так что в итоге будет занято 20 ГБ. Логичное поведение, удалим копию.

Теперь, воспользовавшись командной строкой, создадим жёсткую ссылку на файл:
mklink /H hardlink.txt bigfile.txt
На диске не занялось дополнительное место, поскольку hard link не дублирует данные, а просто создаёт ещё одну ссылку на ту же область памяти. Но если выделить оба файла, нажать правую кнопку и выбрать пункт "Свойства", то Windows ошибочно просуммирует размеры файлов как 20 ГБ, так как каждая hard link учитывается как полноценный файл с размером, хотя физически данные занимают лишь 10 ГБ, что видно из количества занятого места на диске.

Отложим в сторону вопрос размера, посмотрим на содержание. Запишем в bigfile.txt некоторую информацию, например, "Это просто статья на Хабре, читайте дальше". Откроем hardlink.txt и увидим ту же информацию, потому что hard link, по сути, является тем же файлом с точки зрения операционной системы.
Удалим bigfile.txt. Но hardlink.txt по-прежнему работает (с ярлыками и символьными ссылками так не получится), это возможно, поскольку hard link указывает на данные напрямую, а не на конкретное имя файла. Пока существует хотя бы один hard link на данные, файл продолжают занимать место на диске. Если удалить все hard link — место освобождается.

XOR и резервное копирование

XOR — это логическая операция «исключающее или», полезная для создания резервных копий данных. XOR позволяет сравнивать данные побитово, что даёт возможность выявлять изменения. Например можно полностью резервировать 2 диска, имея только один дополнительный. Представим, что у нас есть данные на двух дисках, скажем, "0100" и "1111". XOR-результат этих данных будет записан на третий диск.
Вот пример, как это работает:

Диск 1: 0100 Диск 2: 1111 XOR результат: 1011 Теперь можно восстановить любой из трёх дисков, используя другие два. Просто проведя снова XOR операцию над оставшимися двумя дисками. Это позволяет создавать экономные и надёжные резервные копии. По данному принципу работают массив RAID 5. Если бы мы использовали логическую операцию AND (логическое и) имитируя RAID 1 (зеркало), то для резервирования 2-х дисков нам бы потребовалось 2 других диска, экономия на лицо.

Hard links и XOR для резервного копирования

То всё присказка была. Теперь то, для чего писалась эта статья. Комбинация XOR и Hard link даёт отказоустойчивый механики для резервного копирования и работы со снимками состояний. Представьте, что у нас есть два файла. Пусть это будут "Файл А" (1 ГБ) и "Файл Б" (2 ГБ). Допустим первого ноября изменения были внесены только в "Файл А", а в "Файл Б" остался прежним. При традиционном резервном копировании второго ноября пришлось бы копировать оба файла целиком, что заняло бы дополнительно 3 ГБ. Однако сравнив hash суммы файлов, мы определяем какой файл был изменён и записываем только его, в нашем случае "Файл А" (1 ГБ), а для "Файла Б" создаём hard link на его предыдущее состояние. Это позволяет сохранять состояние файловой системы полностью, с детализацией на каждый день, затрачивая место только на изменённые данные. Это похоже на систему теневого копирования в Windows.
Но данные уязвимы к потере из-за выхода из строя накопителя. Поэтому пользуемся программным, аппаратным или собственным RAID, написанном на Python, для создания избыточности и повышения безопасности:

def xor_files(file_a, file_b, file_xor):
    with open(file_a, 'rb') as infile, open(file_b, 'rb') as maskfile, open(file_xor, 'wb') as outfile:
        while True:
            byte = infile.read(1)
            mask_byte = maskfile.read(1)
            if not byte or not mask_byte:
                break
            # XOR каждого байта файла с байтом маски
            outfile.write(bytes([byte[0] ^ mask_byte[0]]))

# Пример использования
input_file = 'original_file.txt'    # Исходный файл
mask_file = 'mask_file.txt'         # Маска-файл
output_file = 'xor_output.txt'      # XOR-файл

xor_files(file_a, file_b, file_xor)

Таким образом, комбинация hard links и XOR открывает возможности для создания надёжных резервных копий и экономии дискового пространства. Этот метод позволяет не только сохранить доступ к состоянию системы на каждый день, но и экономить место, ведь на хранение уходит только то, что действительно изменилось.

Комментарии (28)


  1. Wizard_of_light
    02.11.2024 06:36

    Ну, ещё один способ дифференциального бэкапа. Но там есть нюансы - в NTFS хардлинк работает только для файлов, и только в рамках одного логического раздела. Для папок и разных разделов уже придётся с точками соединения (NTFS junction point) извращаться.


    1. Copernicus Автор
      02.11.2024 06:36

      Полностью согласен, но побоялся лезть в такие дебри)


  1. Vestibulator-1
    02.11.2024 06:36

    А почему эти вольшебные хардлинки не использует микрософт и пресловутая winsxs занимает овердохрена гигабайт которые портят мой твердотельник. Если якобы винда не может посчитать реальный размер, тогда где утилита которая может?


    1. Copernicus Автор
      02.11.2024 06:36

      Это три разных вопроса. По порядку.

      1. Windows использует hard liks. В основном для обратной совместимости. Если мне не сильно изменяет память то на обычный блокнот имеется 7 шт. hard link.

      2. Windows не корректно считает размер файла по отношению к GUI пользователя, занятое место на диске отображается абсолютно корректно.

      3. Уверен что программы, которые учитывают hard links по идентификаторам (inode, если бы мы говорили про UNIX) существуют, просто не являются дефолтными.


  1. rPman
    02.11.2024 06:36

    А когда люди узнают про reflink на cow файловых системах (btrfs,zfs,xfs,..), вообще офигеют (сарказм).

    Файлы, скопированные штатным cp с этой опцией, будут скопированы мгновенно, потому что вместо копирования на каждый кластер (там кажется группами идет, так что сотни гигабайт так же мгновенно) будет скопирована ссылка, но, если исходный или новый файл будет изменен, изменение отразится только на нем, а не на копии (с hardlink как раз 'оба файла' изменятся), т.е. с точки зрения использования это независимая копия файла, но место на диске тратится только на хранение изменений.


    1. RolexStrider
      02.11.2024 06:36

      Более того, в btrfs (за остальные не скажу, в zfs по-моему так же, а в xfs-нет) если в первом файле изменить только один байт - на диске не создастся его измененная копия, а только допишется измененная копия того блока, в котором был измененный байт, и в измененном файле (а файл - это просто цепочка ссылок на блоки) поменяется только ссылка на измененный блок.


    1. Copernicus Автор
      02.11.2024 06:36

      Вообще кода zfs получит распространение будет интересно) Тем не менее буквально в прошлом месяце я встречал флешку отформатированную в FAT32, и видел людей с дискетами 3,5 мб.


      1. DMGarikk
        02.11.2024 06:36

        А в чем флешку форматировать надо? Я тут недавно хотел флешку форматнуть в линуксе и из универсальных фс он мне предложил фат32 и нтфс... Эксфат не было...и?


        1. Copernicus Автор
          02.11.2024 06:36

          Мне кажется современное решение это NTFS. FAT32 не даст записать файл размером более 4 гб, и имеет ограничение на количество файлов в одном каталоге = 65534, на опыте я упирался в оба ограничения. У NTFS один файл может весить до 16ТБ и количество файлов в каталоге может быть более 4 млн. шт. Конечно какие-то экзотические устройства могут принимать только FAT32, но это скорее исключение. Ext (стандартный для Linux) я бы тоже не стал использовать, может потребоваться прочесть флешку на Windows.


          1. breninsul
            02.11.2024 06:36

            а маки современные разве умеют в ntfs?! В чем универсальность?


            1. Copernicus Автор
              02.11.2024 06:36

              macOS понимают NTFS, если мы говорим про чтение. Как писали выше exFAT тоже очень хороший вариант.


        1. Johan_Palych
          02.11.2024 06:36

          exFAT - native file system in Linux 5.4
          Надо было поставить exfatprogs
          https://pkgs.org/download/exfatprogs


        1. PocketSam
          02.11.2024 06:36

          exfat вроде с достаточно давних пор должен быть свободно доступен в Linux.


    1. vikarti
      02.11.2024 06:36

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

      А уж что будет когда узнают что бывает вариант когда в базе хранится идентификатор а сам файл - в S3...


  1. poige
    02.11.2024 06:36

    за такие туториалы надо лет на 10 отбирать возможность публиковать что либо


  1. Copernicus Автор
    02.11.2024 06:36

    Очень аргументированное возмущение) А что так? Ни когда не приходилась восполнять пробелы в знаниях новых сотрудников, или есть предложение как сделать лучше? А нет, всё проще. Я почитал вашу историю комментариев и статей, вам просто нравятся дизлайки))


    1. poige
      02.11.2024 06:36

      Это задумывалось как ответ мне наверное. Просто почему-то оказалось отдельным комментом. В общем, буду краток: русский — хотя бы на уровне правильного написания базиса (того же «никогда»), а потом уже в туториалы пыжиться. И всё это ещё относительно далеко от того, чтобы пытаться проводить какие-то параллели между "AND" и RAID-1, прасти-хоспади… — AND и copy это вообще разные вещи, просто вообще разные. Но как видно, когда в голове каша, то она сама-то этого не видит, вот уже и «туториалы» «для новых сотрудников» создаёт.

      В общем так — 10 лет мало даже, 15 минимум. Оптимистичная оценка.

      P. S. Надеюсь, что "CTO" в титуле это всё же «100» по-русски. Позволяет оставить хоть какую-то надежду за предполагаемый бизнес и его сотрудников.

      (на лицо тут разве что только ладонь)
      (на лицо тут разве что только ладонь)


      1. Copernicus Автор
        02.11.2024 06:36

        Жалко вы не хотите писать полезные комментарии, видимо могли бы. Например предложить, как лучше и нагляднее донести эти простые темы? Или может имеете богатый опыт в развёртывании инфраструктуры, я бы почитал. Найти опечатки, это конечно мило, но может вы способны на что-то более продуктивное?

        P.S. Да с RAID 1формулировка не точная, согласен.


  1. nerudo
    02.11.2024 06:36

    Читал со все большим недоумением пытаясь понять, как хардлинки и хор будут прикручивать к веб-приложению. А тут хоп - и конец.


    1. Copernicus Автор
      02.11.2024 06:36

      Это было в начале, откуда пошло рассуждение. Но если интересно, то вот так:

      1. Когда файлы загружаются на сервер, то создаётся особая структура каталогов и файл именуется по шаблону. Таким образом в самом коде веб приложения сразу пишется паттерн, который указывает на на место в файловой системе. Для того чтобы приложению не нужно было администрировать файлы, то они при загрузке распределялись бы хардлинками по нужным каталогам.

      2. Используя git вы сталкиваетесь именно в hard link и понимание этого есть основная цель.

      3. XOR сам по себе не сильно интересен. Но при решении отдельных задач крайне спасает. Например, не помню где на просторах интернета встречал: СКУД много людей заходят, много выходят. Один не вышел, как найти? И одно из решений было пройти XOR-ом по всем записям. Не помню деталей, но и работало это только в случае, если не вышел 1 человек. Главное - это понимание механизма.


    1. poige
      02.11.2024 06:36

      там, как говорится, прекрасно всё…


    1. Dertefter
      02.11.2024 06:36

      Буквально конец статьи би лайк:


  1. lex899
    02.11.2024 06:36

    Чуть-чуть теории (всего один абзац!)

    Красивая картинка с HEX редактором какое отношение к теории имеет?

    Запишем в bigfile.txt некоторую информацию, например, "Это просто статья на Хабре, читайте дальше". Откроем hardlink.txt и увидим ту же информацию, потому что hard link, по сути, является тем же файлом с точки зрения операционной системы.

    Подскажите, чем правильно редактировать txt файлы, размером 10Gb? Почему выбран именно такой объем файла?

    Поэтому пользуемся программным, аппаратным или собственным RAID, написанном на Python, для создания избыточности и повышения безопасности:

    Даже если воспринимать этот код как концепт - от чего конкретно защищает этот ваш псевдо-raid? От повреждения файлов он не защищает. От удаления одного из файлов? Ну теоретически да, но это функционал резервного копирования а не raid.


    1. Copernicus Автор
      02.11.2024 06:36

      1. HEX редактор напрямую никакого отношения не имеет. Но найти картинку способную точно передать идею я увы не смог. В добавок современные операционные системы хорошо скрывают реальные адреса, а DOS ради этого было ставить очень мутерно. Я использую эту картинку исключительно для визуализации отличия адреса от информации. Это не точное пособие, но в целом идею на нём показать можно.

      2. Чем правильно редактировать например EmEditor или суровое консольное nano. Если хватит оперативной памяти, то оно должно открыть.

      3. Это просто способ пощупать XOR. На этом можно построить защиту от повреждения, файлы будут друг друга контролировать. Но тогда хэш физического файла нужно сравнивать с хэшом XOR от двух других файлов, при не совпадении восстанавливать из XOR файла и даже можно будет определить конкретные битые места.


      1. lex899
        02.11.2024 06:36

        например EmEditor или суровое консольное nano. Если хватит оперативной памяти, то оно должно открыть.

        EmEditor - обещает открыть за час с лишним, nano - виснет, notepad++ вылетает (быть может ему 12Gb свободной оперативки мало), mcedit - открывает на чтение, но выдает ошибку на запись. Очевидно что любой HEX редактор успешно прожует подобный объем (например HxD поскольку тот же WinHEX не сохраняет в бесплатной версии такие объемы), но вы так уверенно рассуждаете в статье про "откроем" и "запишем" что мне стало любопытно - чем конкретно вы открываете и записываете под Win в разумные сроки и с разумным расходом памяти. Видимо это было теоретическое "откроем".

        при не совпадении восстанавливать из XOR файла

        Допустим у нас есть 4 файла A="000", B="000", C="000", XOR="010"

        Мы понимаем что один из них поврежден во 2м бите. Внимание вопрос - который из 4х файлов поврежден?

        Но тогда хэш физического файла нужно сравнивать с хэшом XOR от двух других файлов, при не совпадении восстанавливать из XOR

        При изменении одного из файлов - пересчитывать хэш этого файла и хэш XOR файла. Сильно. Опять же - это сработает только если не сходится только один хэш т.е. все повреждения попали на один файл. Лично мне крайне сложно придумать практическое применение вашей идее, для детектирования ошибок есть просто хэш, для исправления код Хэмминга, Рида-Соломона, raid 6.


        1. Copernicus Автор
          02.11.2024 06:36

          По поводу «откроем и запишем», наверное было бы корректнее сказать «перепишем», но чтобы не отходить от темы опустил этот момент.

          Практического применения код не имеет никакого) Глупо спорить, что уже имеющиеся решения, в том числе аппаратный RAID или mdamd покрывают весь спектр. Но иногда приходится решать узкие проблемы или разбираться в работе того или иного кода, и в этот момент просто полезно знать какой бывает инструментарий.


  1. Politura
    02.11.2024 06:36

    Подобно этому, файл number.txt на рабочем столе, на самом деле, — это только ссылка, указывающая на область в памяти диска допустим 0x7ffd3b56a4d0, где находятся данные «01001001».

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

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


    1. Copernicus Автор
      02.11.2024 06:36

      Согласен, всё описанное вами абсолютно верно. И отлично дополняет. Я решил, чтобы статья была лёгкой для чтения опустить подробности. Это как рассмотрение солнечной системы, если нарисовать все планеты и солнце в масштабе, но это будет не наглядно и начинать погружаться в астрономию с этого будет тяжело. Рассмотрение всех возможных эффективных способов сжатия мне кажется это вообще отдельная тема, в которой я точно не силён. В этом беда наглядных пособий, они не точны и приходится делать некоторые допущения.