Meshbeyn / Projects

Сжатые и разреженные потоки

NTFS поддерживает несколько способов уменьшить занимаемое файлами место.

Упаковка хвостов
Если все потоки файла настолько короткие, что умещаются в записи MFT (1 килобайт), то для файла вообще не выделяется дополнительное место. Файлы размером до нескольких сотен байт часто хранятся непосредственно в своей записи. Такой файл занимает всего 1кб вне зависимости от размера кластера. Этот метод встроен в драйвер NTFS и повлиять на него нельзя. На самом деле, это не совсем упаковка хвостов, но такое название для этой способности уже устоялось. Настоящая упаковка хвостов собирает окончания нескольких файлов в одном кластере. Управление такой структурой - довольно сложная и медленная операция. А сохранение потока в записи NTFS наоборот может существенно ускорить доступ к данным.
Ссылки на файлы
Если требуется иметь много файлов с одинаковым содержимым, можно воспользоваться механизмом жестких или символьных ссылок. Это позволяет хранить данные файла только один раз, а все остальные "копии" будут просто на него ссылаться.
Сжатые потоки
NTFS может хранить потоки в сжатом виде. При этом каждые 16 кластеров потока сжимаются методом LZNT1, и если получившийся результат умещается в меньшее количество кластеров, то сохраняется сжатая версия. Иначе сохраняются просто несжатые кластеры. Благодаря этому файл не может увеличиться в размерах при неудачном сжатии. По незанятым кластерам драйвер NTFS может отличить сжатые части потока от несжатых. Алгоритм сжатия не очень эффективен, но достаточно быстр и не деградирует. При последовательной записи и чтении хорошо сжатый поток обычно читается даже быстрее, чем без сжатия. Но неупорядоченное чтение и особенно запись будут требовать гораздо больше ресурсов и заметно медленнее выполняться. Сжатый файл всегда хранится в MFT как сильно фрагментированный, с большими накладными расходами, поэтому не рекомендуется сжимать очень крупные файлы. После нескольких сотен мегабайт нагрузка на драйвер при работе с такими файлами уже значительно возрастает, а примерно при 30Г происходит отказ драйвера обрабатывать такой файл: возвращается сообщение о нехватке ресурсов.
Разреженные потоки
Содержимое файлов не всегда представляет собой непрерывный поток данных. Часто данные хранятся в сложных структурах, поддерживающих быстрый сортированный или хешированный доступ и обновление информации. Такие файлы часто содержат крупные области без полезной информации. В базах данных такие участки зачастую занимают гораздо больше места, чем полезные данные. Чтобы не тратить дисковое пространство на "несуществующие" байты, NTFS позволяет не сохранять на диск некоторые кластеры файла. Пользователь может указывать системе, что некоторый регион не содержит полезных данных. Система сама принимает решение, удалить этот регион или просто обнулить его байты. Необходимо учитывать, что удален может быть минимум целый кластер, а в некоторых случаях накладываются и более жесткие ограничения. Например, на моей файловой системе с размером кластера 4 кб, можно обнулять только блоки по 64 кб. Поэтому обнулять надо стараться сразу большой регион целиком, а не кусками. Система при чтении файла вместо удаленных кластеров просто вернет массив нулей, так что для программ никаких проблем с чтением файла не возникнет. Также программа может запросить список занятых экстентов файла и читать только их. Программы, которые активно работают с разреженными файлами могут обрабатывать файлы размером в десятки гигабайт и занимающие всего несколько мегабайт на диске. Поэтому может быть очень важным иметь возможность читать лишь занятые участки файла.

За работу со сжатыми и разреженными потоками отвечает класс NTFSExtensions.Compression. Основная цель сжатия или прореживания - сократить занимаемое файлами место. Архиваторы могут это сделать более эффективно, но при использовании встроенных возможностей NTFS возможна работа со сжатым файлом как с обычным. Вы можете читать или изменять любую часть файла. В зависимости от операции и сжимаемости файла, работа со сжатым файлом может быть чуть быстрее или сильно медленнее, чем с обычным. Вы можете смело использовать сжатие, если это файл с текстом или несжатыми графическими данными. Обычно хорошо сжимаются и выполнимые файлы. Не стоит устанавливать сжатие, если файл часто и непоследовательно будет изменяться мелкими частями. Если в системе не слишком много физической памяти, не стоит сжимать выполнимые файлы. Ни в коем случае не сжимайте рабочие файлы баз данных (если в них часто пишутся новые данные) или журнальные файлы. Несмотря на хорошую сжимаемость, операции записи в них могут замедлиться в десятки раз! Не имеет смысла сжимать архивы и сжатые мультимедийные файлы. Самые лучшие кандидаты на сжатие - файлы, которые часто читаются и редко записываются последовательно большими кусками.

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

И сжатые и разреженные файлы кодируют реальные кластеры при помощи экстентов. Поэтому такие файлы для системы "очень сильно фрагментированы" даже если данные идут строго друг за другом. Особенно от этого страдают сжатые файлы. При нехватке памяти работа с такими файлами может существенно замедлиться. Также не рекомендуется сжатие файлов больше 300 МБ, так как количество записей для экстентов станет слишком большим для быстрой работы.

Со сжатыми и разреженными файлами Вы можете выполнять следующие действия:

SetCompressedAttribute
Делает файл сжатым или несжатым. Сжатие или распаковка производятся уже в момент вызова этого метода, так что он может выполняться очень долго. Последующие операции записи будут уже обрабатываться драйвером NTFS в соответствии с флагом Compressed. Для каталога этот флаг не имеет действия, но все новые файлы и подкаталоги копируют этот флаг у своего родительского каталога. Существующие файлы в таком каталоге не будут автоматически сжиматься или распаковываться.
SetSparseAtribute
Делает файл разреженным или обычным. Windows XP не поддерживает снятие этого атрибута, в ней единственный способ избавиться от него - создать новый файл и заменить им существующий. В более новых версиях этот атрибут можно снять, но файл при этом не должен содержать дырок.
GetLengthCompressed
Возвращает физическую длину файла с учетом сжатия или разреженности. Для несжатых файлов возвращает логическую длину.
SetZeroData
Обнуляет интервал в файле. Если это разреженный файл, то кластеры, полностью попадающие в этот интервал, будут удалены. Те части интервала, которые нельзя удалить, будут заполнены нулями.
GetZeroRanges
Возвращает обнуленные интервалы.
GetAllocatedRanges
Возвращает необнуленные интервалы. Эта функция очень полезна для обработки только тех частей файла, которые содержат полезную информацию.
RefillZeroRegions
Восстанавливает обнуленные интервалы и заполняет их нулями. Эту функцию нужно вызывать перед тем, как снять флаг разреженного файла.

Примеры работы с классом Compression

Сжатие файла и проверка занимаемого места

Обнуление региона в разреженном файле