Предыстория
При проектировании программного обеспечения порой возникают неожиданные вопросы. Мой коллега задал один такой вопрос, когда решал, как устроить работу с файлами в веб-приложении. Методы взаимодействия с файлами давно отработаны, и существуют две основные стратегии: хранить файлы прямо в базе данных или записывать в базу данных только ссылки, а сам файл оставить в файловой системе сервера.
Коллега решил рассмотреть третий вариант (который впоследствии был аргументированно отвергнут). Один и тот же файл по логике приложения нужно использовать в нескольких местах (во многих местах), для этого он предложил просто задавать имя по шаблону, а для множественного использования создавать ссылки (символьные или жёсткие в файловой системе, не веб ссылки типа 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. Для этого потребуется лишь командная строка.
-
Сначала посмотрите, сколько свободного места на жёстком диске
-
Откройте командную строку
Win + R, затем введите cmd
-
Перейдите на рабочий стол командой
cd Desktop
-
Создайте файл, например, размером 10 ГБ, используя команду:
fsutil file createnew bigfile.txt 10737418240
. После создания файла на диске уменьшится количество свободного места на 10 ГБ -
Попробуйте скопировать файл. После создания копии свободное место уменьшится ещё на 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 открывает возможности для создания надёжных резервных копий и экономии дискового пространства. Этот метод позволяет не только сохранить доступ к состоянию системы на каждый день, но и экономить место, ведь на хранение уходит только то, что действительно изменилось.
Автор: Copernicus