Концепция ZFS
Файловая система ZFS была разработана компанией Sun Microsystems
для удовлетворения растущих потребностей индустрии. В ее основу положены
несколько базовых принципов, которые мы обсудим ниже, а затем
перейдем к обсуждению их реализации и конкретным приемам администрирования.
Вот эти принципы:
организация всего доступного дискового пространства в единый пул;
сквозной контроль целостности данных;
транзакционность;
легкость администрирования.
Рассмотрим их значение подробнее.
Пулы накопителей вместо разрозненных дисков
Когда запускается какое-нибудь приложение (например, по команде ls – то самое, что покажет вам список файлов в текущем каталоге), оно
может запросить у операционной системы определенный объем памяти.
Что будет, если не запросит? Ничего страшного: получит минимально
необходимое пространство, размер которого указан в заголовке исполняемого
файла. А если все-таки запросит, то уж точно не станет указывать,
в какой конкретно микросхеме памяти или на каком модуле DIMM ей
хочется эту память получить. Потому что от приложения механизм реализации
его запроса скрыт.
Ровно в этом и состоит прицип создания пулов накопителей: администратор
просто создает пул (это – логическая сущность, т.е. при создании
пула возникает его описание в системе – никаких физических объектов
не создается) и добавляет в пул имеющиеся в наличии накопители.
Например, единственный жесткий диск того компьютера, для работы на
котором и создается пул.
Затем администратор волен создать одну или несколько файловых
систем внутри пула. Их размер может быть каким угодно – лишь бы
хватило физического объема пула. О фактическом размещении файлов
и каталогов на одном или нескольких физических носителях внутри
пула позаботится драйвер ZFS. Таким образом, при применении ZFS
исчезают проблемы, связанные с ошибочным или недальновидным разбиением
дисков на разделы, – теперь вы можете устанавливать и снимать
ограничения на размер той или иной файловой системы динамически,
без всякой связи с физическими размерами дисков или разделов,
объединенных в пул.
Сквозной контроль целостности
До последнего времени проверка целостности каждого блока данных,
который считывается с диска, казалась неоправданной тратой
процессорного времени. Однако сейчас, с лавинообразным ростом мощности
процессоров, это оказалось совсем недорого по сравнению с ценой
потерь от порчи данных.
Под сквозным контролем целостности понимается запись на диск
контрольной суммы для каждого блока данных, причем контрольная
сумма и данные специально разносятся максимально далеко друг от друга
для снижения вероятности их совместной порчи. Если в пуле несколько
устройств, то для данных, размещенных на одном из них, контрольная
сумма будет записана на другом. Контрольные суммы вычисляются не
только для данных, но и для метаданных, и получается, что в пуле всегда
есть контрольная сумма для каждого блока информации.
При считывании любого блока подсчитывается его контрольная
сумма и результат сравнивается с контрольной суммой, хранящейся на
диске. В случае расхождения ошибка сразу обнаруживается. Разумеется,
если в пуле не было запланировано никакого резервирования (ни RAID-Z,
ни иного), то ошибку уже не исправишь, но зато испорченные данные не
будут выданы за истинные.
Смысл сквозного контроля целостности данных в том, чтобы предотвратить
скрытую незаметную порчу данных в результате сбоя оборудования,
встроенного программного обеспечения диска или контроллера.
Несмотря на то, что вероятность такого события кажется низкой, некоторые
исследования показывают, что она вполне значима для организаций
разного масштаба. Например, такие данные есть в отчете CERN от 8 апреля
2008 года[].
Функциональность сквозного контроля целостности пока предоставляет
всего одна файловая система в мире – ZFS.
Разумеется, обеспечить гарантированную сохранность и неизменность
данных с момента их записи на диск до момента считывания позволяют
только те или иные средства резервирования. Поэтому, если у вас
всего один раздел одного жесткого диска, то лучшее, что может предложить
ZFS, – это проинформировать вас о том, что с данными непорядок.
Эта функциональность реализована и в других файловых системах,
но отличие ZFS в том, что 256-битные контрольные суммы блоков сохраняются
не вместе с блоками, а в блоке адресации верхнего уровня, т.е.
вместе с адресом блока хранится контрольная сумма адресуемого блока
данных. Контрольные суммы считаются по любому объекту, не только по
блоку данных, но и по блоку адресации, и если в блоке адресации из-за
сбоя при хранении оказался неверный адрес, то система сама определит,
что адрес неверный, и ошибочные данные просто не будут выданы по
запросу (рис 8.1).
(рис 8.1) Дерево данных и метаданных ZFSТаким образом, даже если никакого резервирования вы не предусмотрели,
ZFS с бОльшей вероятностью, чем традиционные файловые
системы, сможет определить при считывании данных, что они повредились
между моментами записи и считывания, и вы не получите заведомо
неверную информацию. Обычно этого мало: хотелось бы считывать в точности
те данные, что были записаны – независимо от проблем с дисками,
контроллерами и драйверами.
Транзакционность
Транзакционность ZFS обеспечивается тем, как данные записываются
на диск. Операции записи группируются в транзакции, а те, в свою
очередь – в группы транзакций. Каждая транзакция, состоящая из операций
записи в разные файлы, выполняется так: вначале в свободные блоки
пишутся данные, затем записываются метаданные, относящиеся к модифицированным
файлам; если запись не прошла до конца, изменения в
файлах нигде не будут учтены. Никакой коррекции метаданных в таком
случае никогда не требуется, о программе fsck можно забыть. Это иллюстрирует
рис 8.1 Применяемая в ZFS схема называется "copy on write"
(копирование-при-записи), – это означает, что по ходу записи новых
данных старые не перезаписываются, а остаются записанными "рядом", в
других блоках, и файл считается окончательно измененным только после
изменения всех метаданных.
Это – существенное преимущество ZFS по сравнению с журналируемыми
файловыми системами, которые широко используются в
последние годы во всех операционных системах. Дело в том, что при
наличии журнала возможна ситуация, когда приложение инициировало
две последовательные операции записи на диск, и при этом одна прошла
успешно, а до завершения второй произошел, скажем, сбой питания. В
такой ситуации файл на диске окажется "недомодифицированным", хотя
благодаря журналу метаданные могут остаться согласованными.
В случае с ZFS файл никогда не будет считаться модифицированным
до того, как все метаданные – с самого нижнего уровня адресации до
самого верхнего – не будут корректно изменены.
Легкость администрирования
Следствием продуманной концепции ZFS стало значительное
облегчение работы администраторов с ней. В самом деле, например, для
добавления дискового пространства достаточно дать одну-единственную
команду, включающую в пул новый накопитель.
Но самое впечатляющее – это то, что теперь можно делать резервные
копии всей файловой системы в разгар работы, мгновенно, и всего одной
командой! Подробности рассказаны ниже, в разделе "ZFS в работе" в секции
"снимки и резервное копирование".
ZFS в работе
Элементы файловой системы
С точки зрения логики работы с системой на уровне файлов и каталогов,
ZFS ничем не отличается от любой другой POSIX-совместимой
системы. Однако при более детальном взгляде обнаруживается, что
физическое устройство ZFS достаточно сложно. В задачи данной книги
не входит полное описание всех структур ZFS на диске, однако здесь мы
приводим их краткий обзор.
Основными понятиями ZFS, с которыми мы имеем дело, являются:
пул (пул памяти) – набор виртуальных устройств для размещения на них данных;
данные – информация, хранимая в файлах, каждый из которых имеет имя; в отличие от метаданных, которые хранятся в выделенных блоках и не имеют имен;
метаданные – служебная информация, описывающая объект; например, если объект – это файл, то метаданные включают набор прав доступа к файлу (access control list), тип файла, размер файла, время модификации файла, информацию о виртуальных блоках, в которых размещен файл и т.п.;
RAID-Z – массив дисков, на которые записываются данные и соответствующие им избыточные блоки четности; в ZFS реализованы одинарная и двойная избыточность, позволяющие исправлять однократные и двукратные ошибки в данных соответственно;
снимок – зафиксированное на определенный момент времени состояние файловой системы – всех данных и метаданных;
клон файловой системы – ее снимок, в который разрешена запись;
набор данных (dataset) – файловая система, снимок, клон файловой системы.
ZFS обеспечивает непротиворечивый формат хранения данных –
посредством транзакционной модели копирования при записи. Эта модель
гарантирует, что данные на диске никогда не перезаписываются, а все
операции записи являются атомарнымиЕсли вы
не поняли этот абзац, – это нормально, читайте дальше. Данные на диск
в ZFS пишутся так, чтобы любая запись либо успешно завершалась, либо
не учитывалась вообще, потому что вначале пишутся данные, а затем –
метаданные, и если происходит сбой, препятствующий завершению
записи, на самом верхнем уровне метаданных об этой операции ничего
не будет известно, и все место, куда происходила неудавшаяся запись,
будет считаться пустым. А запись верхнего уровня метаданных – это как
раз атомарная операция (т.е. неделимая на части), и она представляет
собой запись одного блока на диск..
Программное обеспечение ZFS состоит из семи частей:
SPA (Storage Pool Allocator)
DSL (Dataset and Snapshot Layer)
DMU (Data Management LayerВ в оригинале документации – так, но логичнее предположить, что DMU – это Data Management Unit, а не Layer. Был бы Layer, было бы DML. )
ZAP (ZFS Attribute Processor)
ZPL (ZFS POSIX Layer)
ZIL (ZFS Intent Log)
ZVOL (ZFS Volume)
Структуры, располагающиеся на диске, ассоциированы с этими
семью частями. Чтобы не вдаваться в излишние подробности, здесь мы
рассматриваем только одну из этих частей – SPA. Для более глубокого изучения
можно порекомендовать документ "ZFS on-disk specification"[] со
страницы разработчиков ZFS http://opensolaris.org/os/community/zfs/docs.
Виртуальные устройства (vdev), метки виртуальных устройств и загрузочный блок
Виртуальные устройства
Пул ZFSZFS storage pool здесь и далее
называется просто "пул" или "пул ZFS", потому что каждый раз
писать "пул хранения" довольно глупо: никаких других пулов на горизонте
не наблюдается. собирается из нескольких виртуальных устройств.
Виртуальные устройства (vdev) бывают физическими (их иногда называют
leaf vdev, т.е. "устройства нижнего уровня", листья в дереве устройств)
и логическими (их иногда называют interior vdev, "внутренними устройствами",
потому что они находятся внутри дерева устройств). Физические
виртуальные устройства – это пригодные для записи блочные устройства,
например, жесткие диски. Логические виртуальные устройства – это логические
группы физических устройств.
Виртуальные устройства образуют древовидную структуру, листьями
которой являются физические устройства. Каждый пул имеет специальное
логическое виртуальное устройство, которое считается корневым (root).
Все прямые потомки корневого устройства называются виртуальными
устройствами верхнего уровня. Рисунок 8.2 показывает дерево виртуальных
устройств некоего пула, содержащего два зеркала. Первое зеркало, M1,
содержит два диска, A и B. Второе зеркало, M2, содержит другие два диска,
С и D. Виртуальные устройства A, B, C, D – это физические виртуальные
устройства. M1 и M2 – логические виртуальные устройства, которые являются
виртуальными устройвами верхнего уровня, так как они находятся
непосредственно под корневым виртуальным устройством.
(рис 8.2) Пример дерева виртуальных устройств
Метки виртуальных устройств
На каждом физическом виртуальном устройстве в пуле записывается
структура размером 256 Кб, которая называется меткой виртуального
устройства. Эта метка описывает то устройство, на котором она записана
и, кроме того, все устройства, которые в этом пуле имеют предком то же
самое виртуальное устройство верхнего уровня (да-да, самого верхнего,
под корневым!). Например, метка виртуального устройства С на
рисунке 8.2 будет описывать следующие виртуальные устройства: С, D и M2.
Содержание метки виртуального устройства детально разобрано ниже.
Метка виртуального устройства служит двум целям: предоставлению
доступа к пулу в целом и проверке его целостности и доступности
устройств в нем. Чтобы метка всегда содержала актуальную информацию
и могла быть прочтена, используются принципы избыточности и
поэтапного изменения: на каждом физическом виртуальном устройстве
хранится четыре копии метки устройства (избыточность), и (для гарантии
актуальности) эти копии изменяются поэтапно.
Избыточность
На каждом физическом виртуальном устройстве в пуле хранится четыре
копии метки устройства. За исключением короткого времени во время
изменения метки, все четыре копии идентичны и любая из них может
быть использована для доступа к пулу и проверки его содержимого. Когда
устройство добавляется в пул, ZFS помещает две метки в начало устройства
и две – в конец. Рисунок 8.3 показывает метки, размещенные на диске размера
N: L0 и L1 – метки в начале диска, метки L2 и L3 – в конце.
(рис 8.3) Четыре копии метки виртуального устройства, размещенные на устройстве (диске) размера NЕсли отталкиваться от предположения, что повреждения на диске
случаются, как правило, в последовательных его частях, размещение меток
в максимально удаленных друг от друга зонах (в начале и в конце) дает
ZFS большую вероятность сохранения "живой" метки в случае сбоя диска
или его случайной перезаписи (например, использования диска в качестве
устройства для свопа в то время, как он еще не перестал быть частью пула).
Транзакционное двухэтапное обновление меток
Местоположение меток фиксируется в момент добавления устройства
в пул, и, стало быть, метки – это единственный объект в ZFS,
который оказался вне концепции копирования при записи. Когда метка
изменяется, ее содержимое перезаписывается. Каждый раз, когда перезаписывается
содержимое диска, возникает потенциальная опасность сбоя.
Чтобы гарантировать доступность метки в любой момент, используется
поэтапное изменение: на первом этапе на диск записываются четные
копии меток (L0 и L2). Если в любой момент этой записи произойдет
сбой, останутся верные метки с нечетными номерами. Как только четные
метки записаны на диск, начинается запись нечетных меток – L1 и L3. Эта
процедура была специально разработана для того, чтобы на диске в любой
момент времени присутствовала правильная копия метки устройства.
Содержание метки виртуального устройства
Метка виртуального устройства делится на 4 части: 8 Кб свободного
пространства, 8Кб для заголовка загрузочного блока, 112 Кб для содержимого
в виде пар "имя/значение" и 128Кб для ста двадцати восьми
однокилобайтных структур типа uber-блок. На рисунке 8.4 показано, из
чего состоит метка, и следующие четыре раздела рассказывают об этом
подробнее.
(рис 8.4) Элементы метки виртуального устройства (свободное место, заголовок загрузочного блока, пары имя/значение, массив uber-блоков)ZFS поддерживает и метки диска VTOC, и метки EFI в качестве
допустимых описателей разметки диска. Метки EFI пишутся в отдельную
область диска, а метки VTOC – в первые 8Кб первого раздела диска.
Поэтому из сообржений совместимости первые 8 Кб метки виртуального
устройства оставлены пустыми.
Заголовок загрузочного блока
Заголовок загрузочного блока размером 8 Кб зарезервирован на
будущее и будет описан в будущем приложении к этому документу.
Список пар имя/значение
Следующие 112 Кб метки виртуального устройства заняты набором
пар имя/значение, которые описывают это виртуальное устройство и все
связанные с ним виртуальные устройства; так, метка на устройстве "А"
(рисунок 8.2) будет содержать информацию об устройствах "А","В" и "M1".
Uber-блок
Сразу за списком имен/значений в метке виртуального устройства
идет массив uber-блоков. Uber-блок – это часть метки, необходимая
для того, чтобы иметь доступ к содержимому пулаUber-блок
по смыслу похож на суперблок в UFS (прим. авт.).. В каждый момент
времени всего один uber-блок в пуле считается активным, а именно тот,
который имеет самый большой номер группы транзакций и корректную
контрольную сумму, рассчитанную по алгоритму SHA-256.
(рис 8.5) Массив uber-блоков; содержимое uber-блоковЧтобы гарантировать постоянный доступ к активному uber-блоку, он
никогда не перезаписывается. Все изменения в uber-блоке записываются
в какой-нибудь другой элемент массива uber-блоков. При записи нового
uber-блока номер группы транзакций и метка времени увеличиваются,
делая его таким образом новым активным uber-блоком за одну атомарную
операцию. Uber-блок из массива для записи при очередной транзакции
выбирается на основе алгоритма кругового выбора. На рисунке 8.5 два
uber-блока из массива показаны в развернутом виде.
Uber-блок крупным планом
Uber-блок записывается с родным для данного компьютера порядком байтЗдесь и далее мы переводим термин big
endian (в младших адресах памяти располагаются более значимые байты)
как "тупоконечный" и little endian (в младших адресах памяти
располагаются менее значимые байты) – как "остроконечный" порядки
байт. Тупоконечный порядок характерен для процессоров SPARC, а
остроконечный – для Intel и AMD. Термины я впервые услашал от Игоря
Николаева из СПбГУ при обсуждении того, что переводы "обратный" и
"прямой" порядок неявно подразумевали некую "правильность" того или
иного варианта, в то время как на самом деле и тот, и другой порядки
имеют равное право на существование. и содержит:
ub_magic
Специфическое поле, 64-битное целое, идентифицирующее данное
устройство как содержащее данные ZFS. Значение поля – 0x00bab10c
(oo-ba-block, произносится схоже с uberblock). Таблица 8.1 показывает
значения поля в зависимости от порядка байт так, как они записываются
на диски:
| Machine Endianness |
Uberblock Value |
| Big Endian |
0x00bab10c |
| Little Endian |
0x0cb1ba00 |
ub_version
Поле используется для указания формата, в котором на диске хранятся
данные. В настоящее время определено только значение 0x1. Это
поле должно содержать то же значение. что и поле "version", описанное
в разделе 1.3.3.
ub_txg
Все операции записи в ZFS выполняются группами транзакций.
Каждая группа ассоциируется с определенным номером транзакции.
Значение ub_txg показывает, в пределах какой группы транзакций был
записан этот uber-блок. Чтобы это значение считалось корректным, оно
должно быть больше или равно значения поля txg, хранящегося в nvlist
для этой метки виртуального устройства.
ub_guid_sum
Это поле служит для проверки доступности устройств внутри пула.
Пока пул используется, драйвер ZFS пробегает все физические
устройства пула и суммирует значения GUID с каждого устройства в пуле
(значение хранится в паре имя/значение с именем guid в списке пар имя/
значение, как описано в разделе 1.3.3). Подсчитанная сумма сравнивается
с ub_guid_sum, чтобы подтвердить доступность все устройств в пуле.
ub_timestamp
Время, когда был записан этот uber-блок – в секундах, отсчитанное
с 1 января 1970 г. (часовой пояс UTC).
ub_rootbp
ub_rootbp – это структура blkptr, содержащая расположение MOS. И
MOS, и blkptr описаны в главах 4 и 2 этого документа соответственно.
Указатели блоков и блоки косвенной адресации
Данные пересылаются между памятью и диском блоками. Указатель
блока blckptr_t – это 128-байтная структура ZFS, которая нужна для того,
чтобы описать физическое расположение блока на диске, описать сам
блок и иметь возможность проверить его целостность. Структура указателя
блока показана на рис 8.6.
(рис 8.6) Побайтовая структура указателя блокаВиртуальный адрес данных (DVA – Data Virtual Address)
Виртуальный адрес данных – это комбинация полей vdev и offset
указателя блока; например, комбинация значений поля vdev1 и поля
offset1 дает виртуальный адрес dva1. ZFS позволяет иметь до трех копий
данных, на которые указывает данный указатель блока. Каждая копия
имеет свой виртуальный адрес – dva1, dva2 и dva3 соответственно. По
каждому из этих адресов хранятся совершенно одинаковые данные.
Количество используемых виртуальных адресов в указателе блока основано
на политикеПо состоянию на август 2007
года политика такая: самые важные метеданные пишутся с тройным
резервированием (3DVA), менее важные метаданные – с двойным (2 DVA), а
просто данные – без резервирования вовсе (1 DVA).
файловой системы и называется "шириной" указателя блока: обычный
указатель (1 DVA), указатель двойной ширины (2 DVA) и тройной ширины
(3 DVA).
Поле vdev (32-разрядное целое) каждого DVA в указателе блока однозначно
идентифицирует виртуальное устройство, на котором хранится блок.
Поле offset (63-разрядное целое) – это адрес блока на устройстве, отсчет
начинается сразу после меток виртуального устройства L0 и L1 и загрузочного
блока в пределах того устройства, на котором хранятся данные. Vdev и
offset вместе определяют уникальное адрес блока данных. В поле offset указывается
адрес в секторах (т.е. в кусках пространства по 512 байт).
Для того, чтобы найти смещение физического блока в байтах от
начала раздела (slice), значение поля offset следует сдвинуть влево на 9
разрядов (29 = 512) и затем сложить с 0x400000 (размер двух меток
устройства vdev_label и загрузочного блока):
physical block address = (offset << 9) + 0x400000 (4MB)
GRID
Поле зарезервировано для последующего использования в отказоустойчивах
конфигурациях Raid-Z.
GANG
Группирующий (gang) блок – это блок, содержащий указатели на
блоки. Группирующие блоки используются, если запрошенное дисковое
пространство невозможно выделить одним непрерывным блоком. В этом
случае выделяются несколько блоков меньшего размера так, чтобы их
суммарный размер был равен запрошенному, и создается группирующий
блок, в котором будут храниться указатели для выделенных блоков.
Приложению, которое запросило дисковое пространство, возвращается
указатель на группирующий блок, и это создает у него "ощущение", что
ему выделен единственный блок.
Группирующий блок идентифицируется установленным битом "G":
Значения бита G
| Значение бита "G" |
Описание |
| 0 |
обычный блок |
| 1 |
группирующий блок |
Группирующий блок имеет размер 512 байт и сам содержит свою
контрольную сумму. Он может содержать до 3 указателей на блоки, за
которыми следует 32-разрядная контрольная сумма. Формат группирующего
блока описывает следующая структура:
typedef struct zio_gbh {
blkptr_t zg_blkptr[SPA_GBH_NBLKPTRS];
uint64_t zg_filler[SPA_GBH_FILLER];
zio_block_tail_t zg_tail.;
} zio_gbh_phys_t;
zg_blkptr: массив указателей на блоки. Каждый 512-байтный группирующий
блок может содержать до 3 указателей на блоки.
zg_filler: поле заполнителя (filler) требуется для того, чтобы дополнить
группирующий блок до 512 байт.
typedef struct zio_block_tail {
uint64_t zbt_magic;
zio_cksum_t zbt_cksum;
}
zbt_magic: специфическое значение, завершающее блок ZIO. Равно
0x210da7ab10c7a11 (zio-data-bloc-tail).
typedef zio_cksum {
uint64_t zc_word[4];
}zio_cksum_t;
zc_word: четыре 8-байтных слова, содержащих контрольную сумму
группирующего блока.
cksum (Контрольная сумма)
По умолчанию в ZFS контрольная сумма вычисляется для всех данных и метаданных. Поддерживается несколько алгоритмов контрольного
суммирования, в том числе fletcher2, fletcher4 и SHA-256 (256-bit Secure
Hash Algorithm из FIPS 180-2, подробнее на странице
http://csrc.nist.gov/cryptval). Какой алгоритм использовать для вычисления контрольной
суммы этого блока, определяется значением 8-разрядного целого в поле
cksum указателя блока. В таблице 8.3 приводится соответствие значений
алгоритмам.
Значения поля cksum и соответствующие алгоритмы контрольного суммирования
| Описание |
Значение |
Алгоритм |
| включено |
1 |
fletcher2 |
| выключено |
2 |
none |
| метка |
3 |
SHA-256 |
| заголовок группир. блока |
4 |
SHA-256 |
| zilog |
5 |
fletcher2 |
| fletcher2 |
6 |
fletcher2 |
| fletcher4 |
7 |
fletcher4 |
| SHA-256 |
8 |
SHA-256 |
comp (Сжатие)
ZFS поддерживает несколько алгоритмов сжатия. Какой тип сжатия
использовать для данного блока, определяется полем comp указателя
блока:
Значения поля comp и соответствующие алгоритмы сжатия
| Описание |
Значение |
Алгоритм |
| on |
1 |
lzjb |
| off |
2 |
нет сжатия |
| lzjb |
3 |
lzjb |
Размер блока
Размер блока определяется тремя разными полями в указателе
блока: psize, lsize, and asize.
lsize: логический размер. Показывает размер данных без учета сжатия, программного RAID (RAID-Z) или накладных расходов, связанных с группирующими блоками (gang):
psize: физический размер блока на диске после сжатия;
asize: фактически занятое данными пространство, общий размер всех блоков, занятых для хранения этих данных, включая заголовки группирующих блоков или блоки четности RAID-Z.
Если сжатие данных не включено и ZFS организовано без RAID-Z, то
значения lsize, asize, и psize одинаковы. Все эти значения записываются как
количество 512-байтных секторов минус один, которые занимает блок.
Порядок байт в слове
ZFS легко адаптируется к переносу между компьютерами разной
архитектуры, даже если в них используется различный порядок байт в
слове – тупоконечный (big endian, в младших адресах памяти располагаются
более значимые байты, SPARC) или остроконечный (little endian,
в младших адресах памяти располагаются менее значимые байты, x86).
Поле E указателя блока (от Endianness) содержит 1 для блока, записанного
в остроконечном порядке байт (x86), и 0 – для блока, записанного
в тупоконечном порядке (SPARC). Данные всегда пишутся на диск в
формате того компьютера, на котором это происходит. Если пул перемещается
из компьютера с одним порядком байт на компьютер с другим
порядком байт, содержимое блока конвертируется в новый порядок при
чтении, а на диске старое содержимое остается в прежнем формате.
Значения поля E (порядок байт в слове)
| Endian |
Value |
| Little Endian |
1 |
| Big Endian |
0 |
Тип данных в блоке
Поле типа данных в указателе блока показывает, какой тип данных
хранится в этом блоке. Типов немало, и они перечислены в таблице 8.6.
Типы объектов
| Тип |
Значение |
| DMU_OT_NONE |
0 |
| DMU_OT_OBJECT_DIRECTORY |
1 |
| DMU_OT_OBJECT_ARRAY |
2 |
| DMU_OT_PACKED_NVLIST |
3 |
| DMU_OT_NVLIST_SIZE |
4 |
| DMU_OT_BPLIST |
5 |
| DMU_OT_BPLIST_HDR |
6 |
| DMU_OT_SPACE_MAP_HEADER |
7 |
| DMU_OT_SPACE_MAP |
8 |
| DMU_OT_INTENT_LOG |
9 |
| DMU_OT_DNODE |
10 |
| DMU_OT_OBJSET |
11 |
| DMU_OT_DSL_DATASET |
12 |
| DMU_OT_DSL_DATASET_CHILD_MAP |
13 |
| DMU_OT_OBJSET_SNAP_MAP |
14 |
| DMU_OT_DSL_PROPS |
15 |
| DMU_OT_DSL_OBJSET |
16 |
| DMU_OT_ZNODE |
17 |
| DMU_OT_ACL |
18 |
| DMU_OT_PLAIN_FILE_CONTENTS |
19 |
| DMU_OT_DIRECTORY_CONTENTS |
20 |
| DMU_OT_MASTER_NODE |
21 |
| DMU_OT_DELETE_QUEUE |
22 |
| DMU_OT_ZVOL |
23 |
| DMU_OT_ZVOL_PROP |
24 |
Уровень (lvl)
Это поле содержит число уровней до этого блока, т.е. количество
указателей блоков, которые надо просмотреть для того, чтобы добраться
до данных в текущем блоке.
Счетчик заполненных блоков (fill count)
Счетчик заполненных блоков содержит количество ненулевых блоков
под данным указателем блоков (т.е. на более нижних уровнях дерева блоков).
Это поле равно 1 для указателя на блок данных, так как он не имеет
ни одного указателя блоков ниже себя. Это поле имеет иное значение для
указателя блоков типа DMU_OT_DNODE: для таких блоков поле содержит
количество свободных дескрипторов ниже этого указателя блоков.
Номер породившей блок группы транзакций (birth txg)
64-битное целое поле birth txg (birth transaction) содержит номер группы
транзакций, в ходе которой данный блок был занят под информацию.
Заполнители (padding)
Три блока-заполнителя в указателе блоков зарезервированы для
использования в будущем.
Копирование при записи данных в ZFS (copy on write)
Предположим, что у нас есть некое дерево блоков (рис 8.1).
Файловые системы традиционно представляют как дерево блоков. Любой
блок данных в такой файловой системе можно найти, пройдя по ссылкам
на дочерние блоки в направлении от корневого. Дерево на рисунке похоже
на двоичное, но на самом деле дерево метаданных и данных в ZFS не
является таковым: оно более широкое и менее глубокое.
Количество уровней в дереве блоков никак не соотносится с количеством
уровней вложенных каталогов в файловой системе.
Допустим, мы хотим модифицировать некий файл и поэтому надо
записать новые данные в два блока. Для этого мы записываем два новых
блока с новыми данными на диск, а старые при этом остаются такими же,
как и раньше. Это показано в верхней правой части рис 8.1.
Пришла пора перезаписывать метаданные, а именно блоки прямой и
косвенной адресации, которые должны указывать на наши новые блоки.
Для этого записывают новые блоки адресации, а старые остаются такими
же, как и раньше. Это показано в нижней левой части рис 8.1.
Как только будут записаны все измененные блоки данных, а также
указывающие на них блоки прямой и косвенной адресации, на диске будет
записано две копии самосогласованного состояния файловой системы –
соответствующее первоначальному, изображенное серыми блоками, и соответствующее
измененному состоянию, изображенное белыми блоками.
Для того, чтобы завершить операцию записи новых данных, требуется
перевести файловую систему из первоначального в измененное
состояние (это показано справа внизу на рис 8.1). Для этого надо
перезаписать всего один блок, а именно – uber-блок. Эта операция является
атомарной, поскольку при ее выполнении записывается ровно один блок
на диске: ведь на диск на физическом уровне нельзя записать данные размером
меньше блока.
На самом деле uber-блок тоже не перезаписывается физически: дело в
том, что в начале каждого устройства хранится массив из нескольких uber-блоков,
среди которых только один является актуальным в каждый момент
времени, и при "перезаписи" на самом деле происходит запись в один из
"неактивных" uber-блоков, после чего он становится "активным".
Как только uber-блок записан, группа транзакций считается завершенной
и файловая система оказывается в новом, тоже самосогласованном
состоянии.
Снимки и резервное копирование
Кому и зачем это надо? Очень просто: представьте, что системному
администратору нужно установить обновление системы или поменять
версию, скажем, PHP на сервере. Раньше осторожные руководства
требовали вначале делать полную резервную копию системы, а затем
уже что-то обновлять. Но если каждый день требует каких-то новых
настроек и программ – не уйдет ли на резервное копирование все рабочее
время?
Функциональность снимков файловой системы (snapshot) дает возможность
зафиксировать состояние всей файловой системы на определенный
момент времени (причем гарантировано – все файлы фиксируются в
том виде, в котором они существовали в ту конкретную наносекунду, когда
вы потребовали сделать snapshot).
А чему радоваться разработчику? Да это же настоящая находка: не
надо никаких средств архивирования версий; достаточно создать по мгновенному
снимку на каждую версию разрабатываемой программы и двигаться
дальше. Все версии ваших файлов будут сохранены автоматически.
Разумеется, снимки требуют места на диске – за функциональность
приходится платить. Но так как снимок не требует копирования всех
файлов (фактически новое место требуется только под измененные после
"фотографирования" файлы), то и накладные расходы куда меньше, чем
при резервном копировании всей файловой системы на соседний диск
или ленту. Плюс к тому, снимок создается мгновенно, никакой траты
времени на перезаписывание гигабайтов данных нет.
Сделать снимок файловой системы проще простого: он создается
при помощи команды zfs snapshot, которой следует указать имя создаваемого
снимка. Имя снимка указывается как filesystem @snapname:
# zfs snapshot pool/home/ahrens@friday
Здесь friday – имя снимка файловой системы pool/home/ahrens.
Создать снимки всех подчиненных файловых систем можно при
помощи ключа -r.
Снимки можно использовать для резервного копирования; и – внимание!
– все это на лету, без перехода в однопользовательский режим, в
том числе – с версии Solaris Express build 62 – и для корневой файловой
системы!
Как же это устроено внутри файловой системы? Что происходит при
создании снимка?
Как видно из рис 8.1, при перезаписи файла блоки с его старым
содержимым остаются неизменными. Ничто не мешает в любой момент
потребовать от системы сохранить старое состояние всего дерева блоков
файловой системы, включая метаданные. Более того, снимок занимает
мало места, так как данные, которые не изменялись с момента создания
снимка, попросту являются общими и для рабочей файловой системы, и
для снимка.
Кроме снимков можно создавать клоны файловой системы – снимки
с возможностью записи в них. Когда в любом из клонов изменяются данные,
новые блоки оказываются своими для каждого из клонов, но неизмененые
данные хранятся в блоках, общих для всех клонов.
RAID-Z
В ZFS предусмотрена реализация RAID-Z – организация программного
RAID-массива с блоками переменной длины. Переменная длина
дает значительный выигрыш в производительности, но меняет алгоритм
восстановления данных, поскольку каждый блок в массиве может иметь
разную длину. Кроме этого, в отличие от традиционных массивов RAID,
в ZFS можно организовать не только однократный, но и двукратный контроль
четности, и это защищает от одновременного сбоя двух физических
носителей.
Легко видеть, что ZFS в самом деле гарантирует сохранность данных,
если у вас достаточно надежное оборудование – по крайней мере
один диск в запасе. Кстати, ZFS поддерживает и диски "горячего резерва"
(spare disks), на которые ничего не записывается до момента, когда один
из рабочих дисков RAID-Z выйдет из строя. Зато как только понадобится,
сбойный диск будет исключен из пула дисков, а диск горячего резерва
немедленно включен туда.
Для поддержки зеркалирования в ZFS реализована процедура
ресинхронизации (resilvering) после замены сбойного диска исправным.
Процедура выполняется автоматически.
(рис 8.7) Самовосстановление данныхЕсли же диск не дает сбоев в целом, но какая-то часть даных оказалась
повреждена (т.е. контрольные суммы не соответствуют данным), то
происходит автоматическая запись на сбойный диск данных с "правильного"
диска, как видно на рис. 8.7. Ошибочный блок данных показан
серым цветом, корректный – белым. Вначале при выполнении чтения
обнаруживается ошибочный блок (расхождение с контрольной суммой),
приложению передается корректный блок данных с другого диска в зеркале,
и затем сбойный блок на первом диске замещается корректным.
Резервирование метаданных
Все метаданные в ZFS пишутся на диск в нескольно разных мест для
большей отказоустойчивости. В зависимости от ценности метаданных,
делается три или – как минимум – две копии. В отличие от UFS, где на
диск записывается несколько копий только суперблока, здесь делаются
копии всех метаданных, причем если в пуле несколько устройств, ZFS
старается разнести копии метаданных по разным устройствам.
Таким образом, для надежного хранения данных в ZFS предусмотрены:
транзакционность;
контрольные суммы в метаданных;
программный RAID (RAID-Z), типов "зеркало", "RAID с однократной четностью", "RAID с двукратной четностью";
диски горячего резерва для RAID-Z;
"самовосстановление" данных, когда они записываются с избыточностью (т.е. с контролем четности или зеркалированием);
тройное резервирование метаданных.
Масштабируемость ZFS
Минимальный размер устройства, которое может быть включено в
пул ZFS, – 64 мегабайта.
Максимальный размер файла, который можно сохранить в ZFS, –
264 байт, т.е. 16 экзабайт. Это в 18 миллиардов (18,4 x 1018)
раз больше, чем позволяют современные 64-битные файловые системы.
Максимальный размер одной файловой системы ZFS такой же – 264 байт.
Если создавать по 1000 файлов в секунду, то для достижения максимального
количества файлов в ZFS (248 штук) понадобится примерно 9000 лет.
Нетрудно видеть, что ZFS действительно создавалась так, чтобы избежать
скорого достижения лимитов!
Автор проекта ZFS Джефф Бонвик (Jeff Bonwick) подсчитал, что на
100%-е заполнение файловой системы ZFS потребуется больше энергии,
чем для того, чтобы вскипятить океан. []
Так, Сет Ллойд (Seth Lloyd) в 2000 году в статье "Ultimate physical
limits to computation" [] показал, что 1 килограмм материи, ограниченный
1 литром объема, может произвести не более чем 1051 операций
над не более чем 1031 битами информации. Заполненный до отказа
128-битный пул памяти будет содержать 2128 блоков = 2137 байт = 2140 бит.
Минимальная масса, которая вместит такое количество информации,
будет равна (2140 бит) / (1031 бит/кг) = 136 миллиардов килограммов.
Если вспомнить известную формулу зависимости массы и энергии
E=mc2 и представить массу такого компьютера в форме энергии, это
составит 1.2 x 1028джоулей.
Известно, что масса всех океанов на планете примерно равна 1.2 x 1028 кг,
и что для того, чтобы превратить в кипяток 1 кг льда, требуется 400 000 джоулей,
причем скрытая энергия на испарение добавит еще 2 000 000 джоулей на
килограмм, итого 2,4 x 106 джоулей на килограмм. Стало быть, для испарения
океанов надо порядка 2.4 x 106 Дж/кг x 1.4 x 1021 кг = 3.4 x 1027 Дж.
Выходит, заполнить 128-битную файловую систему действительно
сложнее, чем вскипятить океан!
Конечно, в этом расчете Джефф не учел, что океан не находится в
замороженном состоянии, но – согласитесь – потенциальный объем ZFS
все равно впечаталяет! И вряд ли в ближайшие несколько десятилетий
бизнес, выбравший ZFS, будет сталкиваться с проблемой переполнения
файловой системы – скорее уж перестанет хватать электричества для
питания дисковых массивов!
Производительность ZFS
Удивительно, но все эти возможности достались нам в ZFS без снижения
производительности: для этого используется несколько важных
технологий, среди которых конвейерный механизм ввода-вывода, подобный
конвейерам центрального процессора.
Каждая операция ввода-вывода имеет свой приоритет и связанный
с ним крайний срок выполнения. Чем выше приоритет, тем ближе крайний
срок. С крайним сроком выполнения связывается логический адрес
блока (LBA) для операции записи – чем более поздний срок ей назначен,
тем меньше будет значение LBA: так достигается практически линейное
движение по диску по мере выполнения все менее и менее "срочных"
операций. Операции чтения с диска имеют больший приоритет, чем операции
записи, потому что при записи асинхронный вызов write() возвратит
управление приложению, как только данные попадут в кэш, а вызов
read() – синхронный, потому что приложение вынуждено ждать, пока ему
будут выданы данные с диска.
Кроме этого, ZFS осуществляет интеллектуальную предвыборку,
подбирая схему предвыборки для каждого потребителя данных в отдельности,
в отличие от других систем, которые делают предвыборку всегда
одинаково – в расчете на последовательный доступ к данным.
При записи ZFS старается писать данные в последовательные блоки,
благо схема copy-on-write исключает модификацию уже заполненных
данными блоков. ZFS также поддерживает параллельную запись в один
файл. Кроме этого, ZFS выбирает размер блока – от 512 байт до 128 Кбайт
исходя из соображений производительности диска, а не его размера.
Когда в системе несколько устройств, ZFS балансирует нагрузку на
них так, чтобы задействовать каждое с максимальной продуктивностью,
и при добавлении нового диска к пулу автоматически заполняет его
новыми данными так, чтобы достичь максимальной скорости доступа
к данным. Если какое-то устройство в пуле замедлилось из-за сбоя или
заполнения значительной части его объема, ZFS старается не записывать
на него новых данных, чтобы не снижать общую скорость системы.
Встроенное сжатие данных (для каждой файловой системы можно
разрешить компрессию) не только уменьшает занимаемый данными
объем на диске, но и уменьшает требуемый объем ввода-вывода вдвое-втрое.
Поэтому в системах с быстрым слабо нагруженным процессором
включение сжатия ускоряет ввод-вывод.
Вот еще некоторые приемы, которые помогают повысить производительность
ZFS – пусть не всегда, но в определенных случаях.
Пустые блоки, если файл содержит таковые, не копируются физически
при копировании файла.
В чем здесь идея? Предположим, мы записали 1 байт в начале файла,
затем отступили от начала 1 гигабайт, и записали еще один значимый байт
в файл. Получили файл размером в 1 гигабайт и 1 байт, который в середине
содержит гигабайтное пространство, заполненное нулями. При копировании такого файла нет смысла переписывать все эти нули, это бы значительно снизило скорость операции. Поэтому было найдено решение, логично
вытекающее из структуры ZFS: файл в ZFS – это дерево блоков, каждый
блок косвенной адресации содержит поле, в котором указывается число
блоков, лежащих одним уровнем ниже него (см. ].
(рис 8.8) Файл с нулевыми блоками внутри (рисунок взят из [5]). Файл состоит из 9 блоков, три из которых на 100% заполнены нулями.Строго говоря, как именно обращаться с файлами, содержащими
обширные поля нулей внутри себя, решает не файловая система, а каждое
приложение индивидуально. В случае с ZFS файловая система умеет
работать с расширенной функцией lseek, в которой поддерживаются
операции SEEK_HOLE (найти следующую область нулей, размером не
менее переданного lseek аргумента) и SEEK_DATA (найти следующую
за областью нулей область единиц, не менее переданного аргумента размером).
В Solaris такая функциональность реализована, а пользоваться ей
или нет – дело разработчика программы.
Еще один момент – место под файл выделяется согласно простому,
но эффективному эмпирическому алгоритму: при выборе блоков для размещения
файла выбираются вначале устройство, на которое будет происходить
запись, затем область диска – более удаленая от центра диска
или менее удаленная, и наконец – собственно блоки. Выбор делается в
соответствии с весом каждого устройства, области и блока; вес определяется
по правилам, которые в следующих версиях ZFS могут измениться
для повышения эффективности. Текущие правила, например, включают
предпочтительность записи данных на свободные внешние дорожки дисков,
потому что конструкция современных дисков дает двукратное превышение
пропускной способности диска при такой записи – по сравнению
с внутренними дорожками. Естественно, что это некие усредненные данные
– на практике реальный прирост производительности будет зависеть
от размера записываемого блока, от размера файла, от конструкции диска
и от того, как соотносится размер кэша диска с размером файла и блока.
Что касается цифр, то любое тестирование можно провести так,
чтобы нужный продукт выглядел более выигрышно. Поэтому все, что я
считаю важным подчеркнуть в отношении известных результатов тестирования
ZFS, сводится к перечню тестовых операций, в которых она уже
показала значительное (2-6 раз) превышение производительности по
сравнению с UFS: []
открытие файла, резервирование пространства под его расширение и однократная запись в файл;
последовательная модификация (перезапись) всех данных в файле порциями по 32К;
создание файла размером 1/2 в свободной памяти посредством записи в него порциями по 1Мб, затем двукратное последовательное чтение всего файла;
параллельная синхронная запись в 100-мегабайтный файл четырьмя потоками.
Разумеется, количество тестов будет расти с ростом промышленного
использования ZFS, так что в скором времени появятся новые результаты.
Управление ZFS
Каждый системный администратор хочет, чтобы надежность его
систем и корректность прав доступа к данным не стоила ему долгих ночных
бдений. Так вот: в ZFS и резервное копирование, и назначение прав
доступа, и изменение размеров файловой системы делается очень просто.
Ощущение от перехода к ZFS можно передать аналогией с переходом
от автомобиля с ручной коробкой передач к машине с автоматической
коробкой.
Итак, к делу: как создать пул памяти ZFS, как смонтировать файловые
системы и как модифицировать созданные системы?
Создание и уничтожение пулов и файловых систем
Создание пула giant из двух дисков – c1t0d0 и c1t1d0
# zpool create giant c1t0d0 c1t1d0
Система ищет указанные диски в каталоге /dev/dsk, и помечает их
как принадлежащие одному пулу.
Так создается зеркальный пул (дисков в зеркале может быть больше,
чем два):
# zpool create giant mirror c1d0 c2d0
Массив RAID-Z из нескольких дисков создается командой
# zpool create giant raidz c1t0d0 c2t0d0 c3t0d0 c4t0d0
Для создания массива с двойным контролем четности надо вместо raidz указать raidz2.
Уничтожить пул можно командой zpool destroy. Осторожно: она уничтожает
пул, даже если в нем содержатся смонтированные наборы данных.
# zpool destroy giant
Добавление накопителей
Добавление устройства в пул – команда zpool add ; например, для
добавления еще двух устройств к зеркалу giant надо скомандовать
zpool add giant mirror c2t1d0 c2t2d0
После создания пула пора создавать файловые системы, которые
будут размещаться в нем – командой zfs create, которой требуется аргумент
– имя файловой системы; оно указывается как путь, начиная с
имени пула: pool-name/[filesystem-name/]filesystem-name
Имя пула и исходные имена файловой системы в пути идентифицируют
то местоположение, где будет создана новая файловая система. Все файловые
системы верхнего уровня уже должны существовать в пуле. Последнее
имя в пути идентифицирует имя создаваемой файловой системы.
Так мы создаем файловую систему oracle в файловой системе giant/home.
# zfs create giant/home/oracle
Если создание файловой системы прошло успешно, система ZFS
автоматически монтирует ее. По умолчанию точкой монтирования считается
путь, указанный в zfs create. В нашем примере вновь созданная
файловая система oracle монтируется в /giant/home/oracle.
Для получения информации о состоянии систем в пуле используют
команду zpool list:
# zpool list
NAME SIZE USED AVAIL CAP HEALTH ALTROOT
dbspace 340G 162G 178G 47% ONLINE -
samba 204G 134G 69.9G 65% ONLINE -
usrdb 68G 27.3G 40.7G 40% ONLINE -
Включить сжатие для файловой системы (в примере – home/archive )
можно командой
# zfs set compression=on concat/archive
Создание снимков, резервное копирование и восстановление файловых систем
Для создания снимка файловой системы достаточно дать такую команду:
zfs snapshot test/opt@18Feb08
В этой команде указывается имя файловой системы, снимок которой
мы хотим сделать, и сразу за ним, после символа @, указывается
имя снимка. Снимок делается практически мгновенно, так как никакого
физического копирования или переноса данных в момент создания
снимка не производится. В дальнейшем мы можем проверить наличие
такого снимка в файловой системе c помощью команды zfs list.
Файловую систему, для которой существуют снимки, нельзя удалить,
не удалив предварительно все ее снимки.
Кроме снимков, можно создавать клоны файловых систем, которые
фактически являются "снимками", смонтированными не только для чтения,
но и для записи. На этом основано клонирование зон с использованием
клонов ZFS (см. лекцию 6 курса "Системное администрирование ОС Solaris 10").
Снимок можно переслать на другое устройство для резервного копирования
командой zfs send:
zfs send test/opt@18Feb08 > backup
В этом примере мы выполнили копирование снимка в файл с именем backup, но вместо файла мы могли использовать любое устройство, в
том числе накопитель на магнитной ленте.
Для восстановления файловой системы (равно и снимка) из такой
резервной копии следует дать команду zfs receive:
zfs receive -F test/opt@snap < backup
Ключ -F требуется, если мы восстанавливаем из резервной копии
снимок, который в данный момент существует в файловой системе, и
тогда нам нужно явным образом указать, что мы хотим его перезаписать,
используя резервную копию.
Откат всей файловой системы к состоянию, в котором она пребывала
на момент снимка, выполняется командой zfs rollback:
zfs rollback test/opt@18Feb08
После выполнения этой команды файловая система test/opt будет
иметь в точности такое же содержание, что и в момент создания ее снимка
с именем 18Feb08.
Квотирование и резервирование пространства
Ограничение прав пользователей на заполнение всего свободного пространства
разнообразным мусором – от псевдо-деловой переписки позапрошлогодней
давности до видеоклипов разнообразного толка – давно стало
рутинным делом сисадминов, преимущественно с помощью механизма квот.
Квотирование сейчас поддерживается в любой современной файловой
системе, а вот в ZFS введена еще одна интересная возможность:
резервирование места для файлов пользователя. В самом деле, если вы
знаете, что пользователь oracle или mail нуждается в хранении большого
количества данных, удобно гарантировать ему это хранение независимо
от чьих-то аппетитов по выкачиванию видео из Сети.
Для резервирования в ZFS следует дать несколько простых команд
(только надо помнить, что зарезервировать можно только свободное
место; если его уже не хватает, то вначале надо удалить лишнее):
# zfs set reservation=4G pool/home/oracle
# zfs get reservation pool/home/oracle
NAME PROPERTY VALUE SOURCE
pool/home/oracle reservation 4.00G local
Нельзя зарезервировать для пользователя больше места, чем дозволено
квотой. При резервировании размер зарезервированного пространства
определяется последней по порядку командой zfs set reservation,
значения последовательных команд НЕ складываются.
# zfs set quota=5G pool/filesystem
# zfs set reservation=10G pool/filesystem/user1
cannot set reservation for 'pool/filesystem/user1': size is
greater than available space
Открытый код – в массы (перенос ZFS в другие системы UNIX)
В данный момент ZFS портирована во FreeBSD 7.0-RELEASE, однако
с некоторыми ограничениями по сравнению с Solaris []:
ZFS скомпилирована в качестве модуля ядра и доступна пока только для архитектуры i386; поддержка для amd64 будет добавлена в ближайшем будущем, другие архитектуры тоже планируется поддерживать;
Не поддерживается разделение ZVOL на базе iSCSI;
Не поддержтиваются ACL и расширенные атрибуты;
С устройства под ZFS нельзя загрузить систему.
Перенос ZFS в Linux затруднен по лицензионным соображениям,
потому что ядро Linux распространяется с лицензией GPL, запрещающий
присоединение к нему кода, который распространяется с отличной от
GPL лицензией. Код Solaris, и в частности код ZFS, тоже открыт для всех,
но на условиях лицензии CDDL. В рамках программы Google Summer of
Code был поддержан проект портирования ZFS под Linux таким образом,
чтобы код поддержки файловой системы выполнялся в пространстве
пользователя (user space), а не в пространстве ядра (kernel space). Для
этого необходимо иметь ядро версии 2.6.14 либо более свежее, так как в
нем можно скомпилировать модуль FUSE, позволяющий исполнять код
файловой системы таким образом. Однако в такой конфигурации следует
ожидать заметного снижения скорости работы файловой системы.
В Mac OS X Leopard (Build 9a321), вышедшем в декабре 2006 года,
впервые в истории Mac OS была встроена поддержка ZFS.
Дополнительные материалы для чтения о ZFS
ZFS Best Parctices Guide:
http://www.solarisinternals.com/wiki/index.php/ZFS_Best_Practices_Guide#ZFS_Management_Tools_.2F_Observability
Manual setup to boot ZFS on x86:
http://www.opensolaris.org/os/community/zfs/boot/zfsboot-manual/
Администрирование ZFS (перевод на русский язык): документ
можно скачать со страницы http://dlc.sun.com/osol/g11n/downloads/docs/current/
в виде архива с .html-файлами.
Литература
Bernd PanzerSteindel, Data integrity, http://indico.cern.ch/getFile.py/access?contribId=3sessionId=0resId=1materialId=paperconfId=13797
ZFS on-disk specification, 2006, http://opensolaris.org/os/community/zfs/docs/ondiskformat0822.pdf
http://en.wikipedia.org/wiki/ZFS
Seth Lloyd, Ultimate physical limits to computation, 2000
Jeff Bonwick, http://blogs.sun.com/bonwick/
http://blogs.sun.com/roch/entry/zfs_to_ufs_performance_comparison
Pawel Jakub Dawidek, 2007, http://lists.freebsd.org/pipermail/freebsdcurrent/2007-April/070544.html
Концепция ZFS
Файловая система ZFS была разработана компанией Sun Microsystems
для удовлетворения растущих потребностей индустрии. В ее основу положены
несколько базовых принципов, которые мы обсудим ниже, а затем
перейдем к обсуждению их реализации и конкретным приемам администрирования.
Вот эти принципы:
организация всего доступного дискового пространства в единый пул;
сквозной контроль целостности данных;
транзакционность;
легкость администрирования.
Рассмотрим их значение подробнее.
Пулы накопителей вместо разрозненных дисков
Когда запускается какое-нибудь приложение (например, по команде ls – то самое, что покажет вам список файлов в текущем каталоге), оно
может запросить у операционной системы определенный объем памяти.
Что будет, если не запросит? Ничего страшного: получит минимально
необходимое пространство, размер которого указан в заголовке исполняемого
файла. А если все-таки запросит, то уж точно не станет указывать,
в какой конкретно микросхеме памяти или на каком модуле DIMM ей
хочется эту память получить. Потому что от приложения механизм реализации
его запроса скрыт.
Ровно в этом и состоит прицип создания пулов накопителей: администратор
просто создает пул (это – логическая сущность, т.е. при создании
пула возникает его описание в системе – никаких физических объектов
не создается) и добавляет в пул имеющиеся в наличии накопители.
Например, единственный жесткий диск того компьютера, для работы на
котором и создается пул.
Затем администратор волен создать одну или несколько файловых
систем внутри пула. Их размер может быть каким угодно – лишь бы
хватило физического объема пула. О фактическом размещении файлов
и каталогов на одном или нескольких физических носителях внутри
пула позаботится драйвер ZFS. Таким образом, при применении ZFS
исчезают проблемы, связанные с ошибочным или недальновидным разбиением
дисков на разделы, – теперь вы можете устанавливать и снимать
ограничения на размер той или иной файловой системы динамически,
без всякой связи с физическими размерами дисков или разделов,
объединенных в пул.
Сквозной контроль целостности
До последнего времени проверка целостности каждого блока данных,
который считывается с диска, казалась неоправданной тратой
процессорного времени. Однако сейчас, с лавинообразным ростом мощности
процессоров, это оказалось совсем недорого по сравнению с ценой
потерь от порчи данных.
Под сквозным контролем целостности понимается запись на диск
контрольной суммы для каждого блока данных, причем контрольная
сумма и данные специально разносятся максимально далеко друг от друга
для снижения вероятности их совместной порчи. Если в пуле несколько
устройств, то для данных, размещенных на одном из них, контрольная
сумма будет записана на другом. Контрольные суммы вычисляются не
только для данных, но и для метаданных, и получается, что в пуле всегда
есть контрольная сумма для каждого блока информации.
При считывании любого блока подсчитывается его контрольная
сумма и результат сравнивается с контрольной суммой, хранящейся на
диске. В случае расхождения ошибка сразу обнаруживается. Разумеется,
если в пуле не было запланировано никакого резервирования (ни RAID-Z,
ни иного), то ошибку уже не исправишь, но зато испорченные данные не
будут выданы за истинные.
Смысл сквозного контроля целостности данных в том, чтобы предотвратить
скрытую незаметную порчу данных в результате сбоя оборудования,
встроенного программного обеспечения диска или контроллера.
Несмотря на то, что вероятность такого события кажется низкой, некоторые
исследования показывают, что она вполне значима для организаций
разного масштаба. Например, такие данные есть в отчете CERN от 8 апреля
2008 года[].
Функциональность сквозного контроля целостности пока предоставляет
всего одна файловая система в мире – ZFS.
Разумеется, обеспечить гарантированную сохранность и неизменность
данных с момента их записи на диск до момента считывания позволяют
только те или иные средства резервирования. Поэтому, если у вас
всего один раздел одного жесткого диска, то лучшее, что может предложить
ZFS, – это проинформировать вас о том, что с данными непорядок.
Эта функциональность реализована и в других файловых системах,
но отличие ZFS в том, что 256-битные контрольные суммы блоков сохраняются
не вместе с блоками, а в блоке адресации верхнего уровня, т.е.
вместе с адресом блока хранится контрольная сумма адресуемого блока
данных. Контрольные суммы считаются по любому объекту, не только по
блоку данных, но и по блоку адресации, и если в блоке адресации из-за
сбоя при хранении оказался неверный адрес, то система сама определит,
что адрес неверный, и ошибочные данные просто не будут выданы по
запросу (рис 8.1).
(рис 8.1) Дерево данных и метаданных ZFSТаким образом, даже если никакого резервирования вы не предусмотрели,
ZFS с бОльшей вероятностью, чем традиционные файловые
системы, сможет определить при считывании данных, что они повредились
между моментами записи и считывания, и вы не получите заведомо
неверную информацию. Обычно этого мало: хотелось бы считывать в точности
те данные, что были записаны – независимо от проблем с дисками,
контроллерами и драйверами.
Транзакционность
Транзакционность ZFS обеспечивается тем, как данные записываются
на диск. Операции записи группируются в транзакции, а те, в свою
очередь – в группы транзакций. Каждая транзакция, состоящая из операций
записи в разные файлы, выполняется так: вначале в свободные блоки
пишутся данные, затем записываются метаданные, относящиеся к модифицированным
файлам; если запись не прошла до конца, изменения в
файлах нигде не будут учтены. Никакой коррекции метаданных в таком
случае никогда не требуется, о программе fsck можно забыть. Это иллюстрирует
рис 8.1 Применяемая в ZFS схема называется "copy on write"
(копирование-при-записи), – это означает, что по ходу записи новых
данных старые не перезаписываются, а остаются записанными "рядом", в
других блоках, и файл считается окончательно измененным только после
изменения всех метаданных.
Это – существенное преимущество ZFS по сравнению с журналируемыми
файловыми системами, которые широко используются в
последние годы во всех операционных системах. Дело в том, что при
наличии журнала возможна ситуация, когда приложение инициировало
две последовательные операции записи на диск, и при этом одна прошла
успешно, а до завершения второй произошел, скажем, сбой питания. В
такой ситуации файл на диске окажется "недомодифицированным", хотя
благодаря журналу метаданные могут остаться согласованными.
В случае с ZFS файл никогда не будет считаться модифицированным
до того, как все метаданные – с самого нижнего уровня адресации до
самого верхнего – не будут корректно изменены.
Легкость администрирования
Следствием продуманной концепции ZFS стало значительное
облегчение работы администраторов с ней. В самом деле, например, для
добавления дискового пространства достаточно дать одну-единственную
команду, включающую в пул новый накопитель.
Но самое впечатляющее – это то, что теперь можно делать резервные
копии всей файловой системы в разгар работы, мгновенно, и всего одной
командой! Подробности рассказаны ниже, в разделе "ZFS в работе" в секции
"снимки и резервное копирование".
ZFS в работе
Элементы файловой системы
С точки зрения логики работы с системой на уровне файлов и каталогов,
ZFS ничем не отличается от любой другой POSIX-совместимой
системы. Однако при более детальном взгляде обнаруживается, что
физическое устройство ZFS достаточно сложно. В задачи данной книги
не входит полное описание всех структур ZFS на диске, однако здесь мы
приводим их краткий обзор.
Основными понятиями ZFS, с которыми мы имеем дело, являются:
пул (пул памяти) – набор виртуальных устройств для размещения на них данных;
данные – информация, хранимая в файлах, каждый из которых имеет имя; в отличие от метаданных, которые хранятся в выделенных блоках и не имеют имен;
метаданные – служебная информация, описывающая объект; например, если объект – это файл, то метаданные включают набор прав доступа к файлу (access control list), тип файла, размер файла, время модификации файла, информацию о виртуальных блоках, в которых размещен файл и т.п.;
RAID-Z – массив дисков, на которые записываются данные и соответствующие им избыточные блоки четности; в ZFS реализованы одинарная и двойная избыточность, позволяющие исправлять однократные и двукратные ошибки в данных соответственно;
снимок – зафиксированное на определенный момент времени состояние файловой системы – всех данных и метаданных;
клон файловой системы – ее снимок, в который разрешена запись;
набор данных (dataset) – файловая система, снимок, клон файловой системы.
ZFS обеспечивает непротиворечивый формат хранения данных –
посредством транзакционной модели копирования при записи. Эта модель
гарантирует, что данные на диске никогда не перезаписываются, а все
операции записи являются атомарнымиЕсли вы
не поняли этот абзац, – это нормально, читайте дальше. Данные на диск
в ZFS пишутся так, чтобы любая запись либо успешно завершалась, либо
не учитывалась вообще, потому что вначале пишутся данные, а затем –
метаданные, и если происходит сбой, препятствующий завершению
записи, на самом верхнем уровне метаданных об этой операции ничего
не будет известно, и все место, куда происходила неудавшаяся запись,
будет считаться пустым. А запись верхнего уровня метаданных – это как
раз атомарная операция (т.е. неделимая на части), и она представляет
собой запись одного блока на диск..
Программное обеспечение ZFS состоит из семи частей:
SPA (Storage Pool Allocator)
DSL (Dataset and Snapshot Layer)
DMU (Data Management LayerВ в оригинале документации – так, но логичнее предположить, что DMU – это Data Management Unit, а не Layer. Был бы Layer, было бы DML. )
ZAP (ZFS Attribute Processor)
ZPL (ZFS POSIX Layer)
ZIL (ZFS Intent Log)
ZVOL (ZFS Volume)
Структуры, располагающиеся на диске, ассоциированы с этими
семью частями. Чтобы не вдаваться в излишние подробности, здесь мы
рассматриваем только одну из этих частей – SPA. Для более глубокого изучения
можно порекомендовать документ "ZFS on-disk specification"[] со
страницы разработчиков ZFS http://opensolaris.org/os/community/zfs/docs.
Виртуальные устройства (vdev), метки виртуальных устройств и загрузочный блок
Виртуальные устройства
Пул ZFSZFS storage pool здесь и далее
называется просто "пул" или "пул ZFS", потому что каждый раз
писать "пул хранения" довольно глупо: никаких других пулов на горизонте
не наблюдается. собирается из нескольких виртуальных устройств.
Виртуальные устройства (vdev) бывают физическими (их иногда называют
leaf vdev, т.е. "устройства нижнего уровня", листья в дереве устройств)
и логическими (их иногда называют interior vdev, "внутренними устройствами",
потому что они находятся внутри дерева устройств). Физические
виртуальные устройства – это пригодные для записи блочные устройства,
например, жесткие диски. Логические виртуальные устройства – это логические
группы физических устройств.
Виртуальные устройства образуют древовидную структуру, листьями
которой являются физические устройства. Каждый пул имеет специальное
логическое виртуальное устройство, которое считается корневым (root).
Все прямые потомки корневого устройства называются виртуальными
устройствами верхнего уровня. Рисунок 8.2 показывает дерево виртуальных
устройств некоего пула, содержащего два зеркала. Первое зеркало, M1,
содержит два диска, A и B. Второе зеркало, M2, содержит другие два диска,
С и D. Виртуальные устройства A, B, C, D – это физические виртуальные
устройства. M1 и M2 – логические виртуальные устройства, которые являются
виртуальными устройвами верхнего уровня, так как они находятся
непосредственно под корневым виртуальным устройством.
(рис 8.2) Пример дерева виртуальных устройств
Метки виртуальных устройств
На каждом физическом виртуальном устройстве в пуле записывается
структура размером 256 Кб, которая называется меткой виртуального
устройства. Эта метка описывает то устройство, на котором она записана
и, кроме того, все устройства, которые в этом пуле имеют предком то же
самое виртуальное устройство верхнего уровня (да-да, самого верхнего,
под корневым!). Например, метка виртуального устройства С на
рисунке 8.2 будет описывать следующие виртуальные устройства: С, D и M2.
Содержание метки виртуального устройства детально разобрано ниже.
Метка виртуального устройства служит двум целям: предоставлению
доступа к пулу в целом и проверке его целостности и доступности
устройств в нем. Чтобы метка всегда содержала актуальную информацию
и могла быть прочтена, используются принципы избыточности и
поэтапного изменения: на каждом физическом виртуальном устройстве
хранится четыре копии метки устройства (избыточность), и (для гарантии
актуальности) эти копии изменяются поэтапно.
Избыточность
На каждом физическом виртуальном устройстве в пуле хранится четыре
копии метки устройства. За исключением короткого времени во время
изменения метки, все четыре копии идентичны и любая из них может
быть использована для доступа к пулу и проверки его содержимого. Когда
устройство добавляется в пул, ZFS помещает две метки в начало устройства
и две – в конец. Рисунок 8.3 показывает метки, размещенные на диске размера
N: L0 и L1 – метки в начале диска, метки L2 и L3 – в конце.
(рис 8.3) Четыре копии метки виртуального устройства, размещенные на устройстве (диске) размера NЕсли отталкиваться от предположения, что повреждения на диске
случаются, как правило, в последовательных его частях, размещение меток
в максимально удаленных друг от друга зонах (в начале и в конце) дает
ZFS большую вероятность сохранения "живой" метки в случае сбоя диска
или его случайной перезаписи (например, использования диска в качестве
устройства для свопа в то время, как он еще не перестал быть частью пула).
Транзакционное двухэтапное обновление меток
Местоположение меток фиксируется в момент добавления устройства
в пул, и, стало быть, метки – это единственный объект в ZFS,
который оказался вне концепции копирования при записи. Когда метка
изменяется, ее содержимое перезаписывается. Каждый раз, когда перезаписывается
содержимое диска, возникает потенциальная опасность сбоя.
Чтобы гарантировать доступность метки в любой момент, используется
поэтапное изменение: на первом этапе на диск записываются четные
копии меток (L0 и L2). Если в любой момент этой записи произойдет
сбой, останутся верные метки с нечетными номерами. Как только четные
метки записаны на диск, начинается запись нечетных меток – L1 и L3. Эта
процедура была специально разработана для того, чтобы на диске в любой
момент времени присутствовала правильная копия метки устройства.
Содержание метки виртуального устройства
Метка виртуального устройства делится на 4 части: 8 Кб свободного
пространства, 8Кб для заголовка загрузочного блока, 112 Кб для содержимого
в виде пар "имя/значение" и 128Кб для ста двадцати восьми
однокилобайтных структур типа uber-блок. На рисунке 8.4 показано, из
чего состоит метка, и следующие четыре раздела рассказывают об этом
подробнее.
(рис 8.4) Элементы метки виртуального устройства (свободное место, заголовок загрузочного блока, пары имя/значение, массив uber-блоков)ZFS поддерживает и метки диска VTOC, и метки EFI в качестве
допустимых описателей разметки диска. Метки EFI пишутся в отдельную
область диска, а метки VTOC – в первые 8Кб первого раздела диска.
Поэтому из сообржений совместимости первые 8 Кб метки виртуального
устройства оставлены пустыми.
Заголовок загрузочного блока
Заголовок загрузочного блока размером 8 Кб зарезервирован на
будущее и будет описан в будущем приложении к этому документу.
Список пар имя/значение
Следующие 112 Кб метки виртуального устройства заняты набором
пар имя/значение, которые описывают это виртуальное устройство и все
связанные с ним виртуальные устройства; так, метка на устройстве "А"
(рисунок 8.2) будет содержать информацию об устройствах "А","В" и "M1".
Uber-блок
Сразу за списком имен/значений в метке виртуального устройства
идет массив uber-блоков. Uber-блок – это часть метки, необходимая
для того, чтобы иметь доступ к содержимому пулаUber-блок
по смыслу похож на суперблок в UFS (прим. авт.).. В каждый момент
времени всего один uber-блок в пуле считается активным, а именно тот,
который имеет самый большой номер группы транзакций и корректную
контрольную сумму, рассчитанную по алгоритму SHA-256.
(рис 8.5) Массив uber-блоков; содержимое uber-блоковЧтобы гарантировать постоянный доступ к активному uber-блоку, он
никогда не перезаписывается. Все изменения в uber-блоке записываются
в какой-нибудь другой элемент массива uber-блоков. При записи нового
uber-блока номер группы транзакций и метка времени увеличиваются,
делая его таким образом новым активным uber-блоком за одну атомарную
операцию. Uber-блок из массива для записи при очередной транзакции
выбирается на основе алгоритма кругового выбора. На рисунке 8.5 два
uber-блока из массива показаны в развернутом виде.
Uber-блок крупным планом
Uber-блок записывается с родным для данного компьютера порядком байтЗдесь и далее мы переводим термин big
endian (в младших адресах памяти располагаются более значимые байты)
как "тупоконечный" и little endian (в младших адресах памяти
располагаются менее значимые байты) – как "остроконечный" порядки
байт. Тупоконечный порядок характерен для процессоров SPARC, а
остроконечный – для Intel и AMD. Термины я впервые услашал от Игоря
Николаева из СПбГУ при обсуждении того, что переводы "обратный" и
"прямой" порядок неявно подразумевали некую "правильность" того или
иного варианта, в то время как на самом деле и тот, и другой порядки
имеют равное право на существование. и содержит:
ub_magic
Специфическое поле, 64-битное целое, идентифицирующее данное
устройство как содержащее данные ZFS. Значение поля – 0x00bab10c
(oo-ba-block, произносится схоже с uberblock). Таблица 8.1 показывает
значения поля в зависимости от порядка байт так, как они записываются
на диски:
| Machine Endianness |
Uberblock Value |
| Big Endian |
0x00bab10c |
| Little Endian |
0x0cb1ba00 |
ub_version
Поле используется для указания формата, в котором на диске хранятся
данные. В настоящее время определено только значение 0x1. Это
поле должно содержать то же значение. что и поле "version", описанное
в разделе 1.3.3.
ub_txg
Все операции записи в ZFS выполняются группами транзакций.
Каждая группа ассоциируется с определенным номером транзакции.
Значение ub_txg показывает, в пределах какой группы транзакций был
записан этот uber-блок. Чтобы это значение считалось корректным, оно
должно быть больше или равно значения поля txg, хранящегося в nvlist
для этой метки виртуального устройства.
ub_guid_sum
Это поле служит для проверки доступности устройств внутри пула.
Пока пул используется, драйвер ZFS пробегает все физические
устройства пула и суммирует значения GUID с каждого устройства в пуле
(значение хранится в паре имя/значение с именем guid в списке пар имя/
значение, как описано в разделе 1.3.3). Подсчитанная сумма сравнивается
с ub_guid_sum, чтобы подтвердить доступность все устройств в пуле.
ub_timestamp
Время, когда был записан этот uber-блок – в секундах, отсчитанное
с 1 января 1970 г. (часовой пояс UTC).
ub_rootbp
ub_rootbp – это структура blkptr, содержащая расположение MOS. И
MOS, и blkptr описаны в главах 4 и 2 этого документа соответственно.
Указатели блоков и блоки косвенной адресации
Данные пересылаются между памятью и диском блоками. Указатель
блока blckptr_t – это 128-байтная структура ZFS, которая нужна для того,
чтобы описать физическое расположение блока на диске, описать сам
блок и иметь возможность проверить его целостность. Структура указателя
блока показана на рис 8.6.
(рис 8.6) Побайтовая структура указателя блокаВиртуальный адрес данных (DVA – Data Virtual Address)
Виртуальный адрес данных – это комбинация полей vdev и offset
указателя блока; например, комбинация значений поля vdev1 и поля
offset1 дает виртуальный адрес dva1. ZFS позволяет иметь до трех копий
данных, на которые указывает данный указатель блока. Каждая копия
имеет свой виртуальный адрес – dva1, dva2 и dva3 соответственно. По
каждому из этих адресов хранятся совершенно одинаковые данные.
Количество используемых виртуальных адресов в указателе блока основано
на политикеПо состоянию на август 2007
года политика такая: самые важные метеданные пишутся с тройным
резервированием (3DVA), менее важные метаданные – с двойным (2 DVA), а
просто данные – без резервирования вовсе (1 DVA).
файловой системы и называется "шириной" указателя блока: обычный
указатель (1 DVA), указатель двойной ширины (2 DVA) и тройной ширины
(3 DVA).
Поле vdev (32-разрядное целое) каждого DVA в указателе блока однозначно
идентифицирует виртуальное устройство, на котором хранится блок.
Поле offset (63-разрядное целое) – это адрес блока на устройстве, отсчет
начинается сразу после меток виртуального устройства L0 и L1 и загрузочного
блока в пределах того устройства, на котором хранятся данные. Vdev и
offset вместе определяют уникальное адрес блока данных. В поле offset указывается
адрес в секторах (т.е. в кусках пространства по 512 байт).
Для того, чтобы найти смещение физического блока в байтах от
начала раздела (slice), значение поля offset следует сдвинуть влево на 9
разрядов (29 = 512) и затем сложить с 0x400000 (размер двух меток
устройства vdev_label и загрузочного блока):
physical block address = (offset << 9) + 0x400000 (4MB)
GRID
Поле зарезервировано для последующего использования в отказоустойчивах
конфигурациях Raid-Z.
GANG
Группирующий (gang) блок – это блок, содержащий указатели на
блоки. Группирующие блоки используются, если запрошенное дисковое
пространство невозможно выделить одним непрерывным блоком. В этом
случае выделяются несколько блоков меньшего размера так, чтобы их
суммарный размер был равен запрошенному, и создается группирующий
блок, в котором будут храниться указатели для выделенных блоков.
Приложению, которое запросило дисковое пространство, возвращается
указатель на группирующий блок, и это создает у него "ощущение", что
ему выделен единственный блок.
Группирующий блок идентифицируется установленным битом "G":
Значения бита G
| Значение бита "G" |
Описание |
| 0 |
обычный блок |
| 1 |
группирующий блок |
Группирующий блок имеет размер 512 байт и сам содержит свою
контрольную сумму. Он может содержать до 3 указателей на блоки, за
которыми следует 32-разрядная контрольная сумма. Формат группирующего
блока описывает следующая структура:
typedef struct zio_gbh {
blkptr_t zg_blkptr[SPA_GBH_NBLKPTRS];
uint64_t zg_filler[SPA_GBH_FILLER];
zio_block_tail_t zg_tail.;
} zio_gbh_phys_t;
zg_blkptr: массив указателей на блоки. Каждый 512-байтный группирующий
блок может содержать до 3 указателей на блоки.
zg_filler: поле заполнителя (filler) требуется для того, чтобы дополнить
группирующий блок до 512 байт.
typedef struct zio_block_tail {
uint64_t zbt_magic;
zio_cksum_t zbt_cksum;
}
zbt_magic: специфическое значение, завершающее блок ZIO. Равно
0x210da7ab10c7a11 (zio-data-bloc-tail).
typedef zio_cksum {
uint64_t zc_word[4];
}zio_cksum_t;
zc_word: четыре 8-байтных слова, содержащих контрольную сумму
группирующего блока.
cksum (Контрольная сумма)
По умолчанию в ZFS контрольная сумма вычисляется для всех данных и метаданных. Поддерживается несколько алгоритмов контрольного
суммирования, в том числе fletcher2, fletcher4 и SHA-256 (256-bit Secure
Hash Algorithm из FIPS 180-2, подробнее на странице
http://csrc.nist.gov/cryptval). Какой алгоритм использовать для вычисления контрольной
суммы этого блока, определяется значением 8-разрядного целого в поле
cksum указателя блока. В таблице 8.3 приводится соответствие значений
алгоритмам.
Значения поля cksum и соответствующие алгоритмы контрольного суммирования
| Описание |
Значение |
Алгоритм |
| включено |
1 |
fletcher2 |
| выключено |
2 |
none |
| метка |
3 |
SHA-256 |
| заголовок группир. блока |
4 |
SHA-256 |
| zilog |
5 |
fletcher2 |
| fletcher2 |
6 |
fletcher2 |
| fletcher4 |
7 |
fletcher4 |
| SHA-256 |
8 |
SHA-256 |
comp (Сжатие)
ZFS поддерживает несколько алгоритмов сжатия. Какой тип сжатия
использовать для данного блока, определяется полем comp указателя
блока:
Значения поля comp и соответствующие алгоритмы сжатия
| Описание |
Значение |
Алгоритм |
| on |
1 |
lzjb |
| off |
2 |
нет сжатия |
| lzjb |
3 |
lzjb |
Размер блока
Размер блока определяется тремя разными полями в указателе
блока: psize, lsize, and asize.
lsize: логический размер. Показывает размер данных без учета сжатия, программного RAID (RAID-Z) или накладных расходов, связанных с группирующими блоками (gang):
psize: физический размер блока на диске после сжатия;
asize: фактически занятое данными пространство, общий размер всех блоков, занятых для хранения этих данных, включая заголовки группирующих блоков или блоки четности RAID-Z.
Если сжатие данных не включено и ZFS организовано без RAID-Z, то
значения lsize, asize, и psize одинаковы. Все эти значения записываются как
количество 512-байтных секторов минус один, которые занимает блок.
Порядок байт в слове
ZFS легко адаптируется к переносу между компьютерами разной
архитектуры, даже если в них используется различный порядок байт в
слове – тупоконечный (big endian, в младших адресах памяти располагаются
более значимые байты, SPARC) или остроконечный (little endian,
в младших адресах памяти располагаются менее значимые байты, x86).
Поле E указателя блока (от Endianness) содержит 1 для блока, записанного
в остроконечном порядке байт (x86), и 0 – для блока, записанного
в тупоконечном порядке (SPARC). Данные всегда пишутся на диск в
формате того компьютера, на котором это происходит. Если пул перемещается
из компьютера с одним порядком байт на компьютер с другим
порядком байт, содержимое блока конвертируется в новый порядок при
чтении, а на диске старое содержимое остается в прежнем формате.
Значения поля E (порядок байт в слове)
| Endian |
Value |
| Little Endian |
1 |
| Big Endian |
0 |
Тип данных в блоке
Поле типа данных в указателе блока показывает, какой тип данных
хранится в этом блоке. Типов немало, и они перечислены в таблице 8.6.
Типы объектов
| Тип |
Значение |
| DMU_OT_NONE |
0 |
| DMU_OT_OBJECT_DIRECTORY |
1 |
| DMU_OT_OBJECT_ARRAY |
2 |
| DMU_OT_PACKED_NVLIST |
3 |
| DMU_OT_NVLIST_SIZE |
4 |
| DMU_OT_BPLIST |
5 |
| DMU_OT_BPLIST_HDR |
6 |
| DMU_OT_SPACE_MAP_HEADER |
7 |
| DMU_OT_SPACE_MAP |
8 |
| DMU_OT_INTENT_LOG |
9 |
| DMU_OT_DNODE |
10 |
| DMU_OT_OBJSET |
11 |
| DMU_OT_DSL_DATASET |
12 |
| DMU_OT_DSL_DATASET_CHILD_MAP |
13 |
| DMU_OT_OBJSET_SNAP_MAP |
14 |
| DMU_OT_DSL_PROPS |
15 |
| DMU_OT_DSL_OBJSET |
16 |
| DMU_OT_ZNODE |
17 |
| DMU_OT_ACL |
18 |
| DMU_OT_PLAIN_FILE_CONTENTS |
19 |
| DMU_OT_DIRECTORY_CONTENTS |
20 |
| DMU_OT_MASTER_NODE |
21 |
| DMU_OT_DELETE_QUEUE |
22 |
| DMU_OT_ZVOL |
23 |
| DMU_OT_ZVOL_PROP |
24 |
Уровень (lvl)
Это поле содержит число уровней до этого блока, т.е. количество
указателей блоков, которые надо просмотреть для того, чтобы добраться
до данных в текущем блоке.
Счетчик заполненных блоков (fill count)
Счетчик заполненных блоков содержит количество ненулевых блоков
под данным указателем блоков (т.е. на более нижних уровнях дерева блоков).
Это поле равно 1 для указателя на блок данных, так как он не имеет
ни одного указателя блоков ниже себя. Это поле имеет иное значение для
указателя блоков типа DMU_OT_DNODE: для таких блоков поле содержит
количество свободных дескрипторов ниже этого указателя блоков.
Номер породившей блок группы транзакций (birth txg)
64-битное целое поле birth txg (birth transaction) содержит номер группы
транзакций, в ходе которой данный блок был занят под информацию.
Заполнители (padding)
Три блока-заполнителя в указателе блоков зарезервированы для
использования в будущем.
Копирование при записи данных в ZFS (copy on write)
Предположим, что у нас есть некое дерево блоков (рис 8.1).
Файловые системы традиционно представляют как дерево блоков. Любой
блок данных в такой файловой системе можно найти, пройдя по ссылкам
на дочерние блоки в направлении от корневого. Дерево на рисунке похоже
на двоичное, но на самом деле дерево метаданных и данных в ZFS не
является таковым: оно более широкое и менее глубокое.
Количество уровней в дереве блоков никак не соотносится с количеством
уровней вложенных каталогов в файловой системе.
Допустим, мы хотим модифицировать некий файл и поэтому надо
записать новые данные в два блока. Для этого мы записываем два новых
блока с новыми данными на диск, а старые при этом остаются такими же,
как и раньше. Это показано в верхней правой части рис 8.1.
Пришла пора перезаписывать метаданные, а именно блоки прямой и
косвенной адресации, которые должны указывать на наши новые блоки.
Для этого записывают новые блоки адресации, а старые остаются такими
же, как и раньше. Это показано в нижней левой части рис 8.1.
Как только будут записаны все измененные блоки данных, а также
указывающие на них блоки прямой и косвенной адресации, на диске будет
записано две копии самосогласованного состояния файловой системы –
соответствующее первоначальному, изображенное серыми блоками, и соответствующее
измененному состоянию, изображенное белыми блоками.
Для того, чтобы завершить операцию записи новых данных, требуется
перевести файловую систему из первоначального в измененное
состояние (это показано справа внизу на рис 8.1). Для этого надо
перезаписать всего один блок, а именно – uber-блок. Эта операция является
атомарной, поскольку при ее выполнении записывается ровно один блок
на диске: ведь на диск на физическом уровне нельзя записать данные размером
меньше блока.
На самом деле uber-блок тоже не перезаписывается физически: дело в
том, что в начале каждого устройства хранится массив из нескольких uber-блоков,
среди которых только один является актуальным в каждый момент
времени, и при "перезаписи" на самом деле происходит запись в один из
"неактивных" uber-блоков, после чего он становится "активным".
Как только uber-блок записан, группа транзакций считается завершенной
и файловая система оказывается в новом, тоже самосогласованном
состоянии.
Снимки и резервное копирование
Кому и зачем это надо? Очень просто: представьте, что системному
администратору нужно установить обновление системы или поменять
версию, скажем, PHP на сервере. Раньше осторожные руководства
требовали вначале делать полную резервную копию системы, а затем
уже что-то обновлять. Но если каждый день требует каких-то новых
настроек и программ – не уйдет ли на резервное копирование все рабочее
время?
Функциональность снимков файловой системы (snapshot) дает возможность
зафиксировать состояние всей файловой системы на определенный
момент времени (причем гарантировано – все файлы фиксируются в
том виде, в котором они существовали в ту конкретную наносекунду, когда
вы потребовали сделать snapshot).
А чему радоваться разработчику? Да это же настоящая находка: не
надо никаких средств архивирования версий; достаточно создать по мгновенному
снимку на каждую версию разрабатываемой программы и двигаться
дальше. Все версии ваших файлов будут сохранены автоматически.
Разумеется, снимки требуют места на диске – за функциональность
приходится платить. Но так как снимок не требует копирования всех
файлов (фактически новое место требуется только под измененные после
"фотографирования" файлы), то и накладные расходы куда меньше, чем
при резервном копировании всей файловой системы на соседний диск
или ленту. Плюс к тому, снимок создается мгновенно, никакой траты
времени на перезаписывание гигабайтов данных нет.
Сделать снимок файловой системы проще простого: он создается
при помощи команды zfs snapshot, которой следует указать имя создаваемого
снимка. Имя снимка указывается как filesystem @snapname:
# zfs snapshot pool/home/ahrens@friday
Здесь friday – имя снимка файловой системы pool/home/ahrens.
Создать снимки всех подчиненных файловых систем можно при
помощи ключа -r.
Снимки можно использовать для резервного копирования; и – внимание!
– все это на лету, без перехода в однопользовательский режим, в
том числе – с версии Solaris Express build 62 – и для корневой файловой
системы!
Как же это устроено внутри файловой системы? Что происходит при
создании снимка?
Как видно из рис 8.1, при перезаписи файла блоки с его старым
содержимым остаются неизменными. Ничто не мешает в любой момент
потребовать от системы сохранить старое состояние всего дерева блоков
файловой системы, включая метаданные. Более того, снимок занимает
мало места, так как данные, которые не изменялись с момента создания
снимка, попросту являются общими и для рабочей файловой системы, и
для снимка.
Кроме снимков можно создавать клоны файловой системы – снимки
с возможностью записи в них. Когда в любом из клонов изменяются данные,
новые блоки оказываются своими для каждого из клонов, но неизмененые
данные хранятся в блоках, общих для всех клонов.
RAID-Z
В ZFS предусмотрена реализация RAID-Z – организация программного
RAID-массива с блоками переменной длины. Переменная длина
дает значительный выигрыш в производительности, но меняет алгоритм
восстановления данных, поскольку каждый блок в массиве может иметь
разную длину. Кроме этого, в отличие от традиционных массивов RAID,
в ZFS можно организовать не только однократный, но и двукратный контроль
четности, и это защищает от одновременного сбоя двух физических
носителей.
Легко видеть, что ZFS в самом деле гарантирует сохранность данных,
если у вас достаточно надежное оборудование – по крайней мере
один диск в запасе. Кстати, ZFS поддерживает и диски "горячего резерва"
(spare disks), на которые ничего не записывается до момента, когда один
из рабочих дисков RAID-Z выйдет из строя. Зато как только понадобится,
сбойный диск будет исключен из пула дисков, а диск горячего резерва
немедленно включен туда.
Для поддержки зеркалирования в ZFS реализована процедура
ресинхронизации (resilvering) после замены сбойного диска исправным.
Процедура выполняется автоматически.
(рис 8.7) Самовосстановление данныхЕсли же диск не дает сбоев в целом, но какая-то часть даных оказалась
повреждена (т.е. контрольные суммы не соответствуют данным), то
происходит автоматическая запись на сбойный диск данных с "правильного"
диска, как видно на рис. 8.7. Ошибочный блок данных показан
серым цветом, корректный – белым. Вначале при выполнении чтения
обнаруживается ошибочный блок (расхождение с контрольной суммой),
приложению передается корректный блок данных с другого диска в зеркале,
и затем сбойный блок на первом диске замещается корректным.
Резервирование метаданных
Все метаданные в ZFS пишутся на диск в нескольно разных мест для
большей отказоустойчивости. В зависимости от ценности метаданных,
делается три или – как минимум – две копии. В отличие от UFS, где на
диск записывается несколько копий только суперблока, здесь делаются
копии всех метаданных, причем если в пуле несколько устройств, ZFS
старается разнести копии метаданных по разным устройствам.
Таким образом, для надежного хранения данных в ZFS предусмотрены:
транзакционность;
контрольные суммы в метаданных;
программный RAID (RAID-Z), типов "зеркало", "RAID с однократной четностью", "RAID с двукратной четностью";
диски горячего резерва для RAID-Z;
"самовосстановление" данных, когда они записываются с избыточностью (т.е. с контролем четности или зеркалированием);
тройное резервирование метаданных.
Масштабируемость ZFS
Минимальный размер устройства, которое может быть включено в
пул ZFS, – 64 мегабайта.
Максимальный размер файла, который можно сохранить в ZFS, –
264 байт, т.е. 16 экзабайт. Это в 18 миллиардов (18,4 x 1018)
раз больше, чем позволяют современные 64-битные файловые системы.
Максимальный размер одной файловой системы ZFS такой же – 264 байт.
Если создавать по 1000 файлов в секунду, то для достижения максимального
количества файлов в ZFS (248 штук) понадобится примерно 9000 лет.
Нетрудно видеть, что ZFS действительно создавалась так, чтобы избежать
скорого достижения лимитов!
Автор проекта ZFS Джефф Бонвик (Jeff Bonwick) подсчитал, что на
100%-е заполнение файловой системы ZFS потребуется больше энергии,
чем для того, чтобы вскипятить океан. []
Так, Сет Ллойд (Seth Lloyd) в 2000 году в статье "Ultimate physical
limits to computation" [] показал, что 1 килограмм материи, ограниченный
1 литром объема, может произвести не более чем 1051 операций
над не более чем 1031 битами информации. Заполненный до отказа
128-битный пул памяти будет содержать 2128 блоков = 2137 байт = 2140 бит.
Минимальная масса, которая вместит такое количество информации,
будет равна (2140 бит) / (1031 бит/кг) = 136 миллиардов килограммов.
Если вспомнить известную формулу зависимости массы и энергии
E=mc2 и представить массу такого компьютера в форме энергии, это
составит 1.2 x 1028джоулей.
Известно, что масса всех океанов на планете примерно равна 1.2 x 1028 кг,
и что для того, чтобы превратить в кипяток 1 кг льда, требуется 400 000 джоулей,
причем скрытая энергия на испарение добавит еще 2 000 000 джоулей на
килограмм, итого 2,4 x 106 джоулей на килограмм. Стало быть, для испарения
океанов надо порядка 2.4 x 106 Дж/кг x 1.4 x 1021 кг = 3.4 x 1027 Дж.
Выходит, заполнить 128-битную файловую систему действительно
сложнее, чем вскипятить океан!
Конечно, в этом расчете Джефф не учел, что океан не находится в
замороженном состоянии, но – согласитесь – потенциальный объем ZFS
все равно впечаталяет! И вряд ли в ближайшие несколько десятилетий
бизнес, выбравший ZFS, будет сталкиваться с проблемой переполнения
файловой системы – скорее уж перестанет хватать электричества для
питания дисковых массивов!
Производительность ZFS
Удивительно, но все эти возможности достались нам в ZFS без снижения
производительности: для этого используется несколько важных
технологий, среди которых конвейерный механизм ввода-вывода, подобный
конвейерам центрального процессора.
Каждая операция ввода-вывода имеет свой приоритет и связанный
с ним крайний срок выполнения. Чем выше приоритет, тем ближе крайний
срок. С крайним сроком выполнения связывается логический адрес
блока (LBA) для операции записи – чем более поздний срок ей назначен,
тем меньше будет значение LBA: так достигается практически линейное
движение по диску по мере выполнения все менее и менее "срочных"
операций. Операции чтения с диска имеют больший приоритет, чем операции
записи, потому что при записи асинхронный вызов write() возвратит
управление приложению, как только данные попадут в кэш, а вызов
read() – синхронный, потому что приложение вынуждено ждать, пока ему
будут выданы данные с диска.
Кроме этого, ZFS осуществляет интеллектуальную предвыборку,
подбирая схему предвыборки для каждого потребителя данных в отдельности,
в отличие от других систем, которые делают предвыборку всегда
одинаково – в расчете на последовательный доступ к данным.
При записи ZFS старается писать данные в последовательные блоки,
благо схема copy-on-write исключает модификацию уже заполненных
данными блоков. ZFS также поддерживает параллельную запись в один
файл. Кроме этого, ZFS выбирает размер блока – от 512 байт до 128 Кбайт
исходя из соображений производительности диска, а не его размера.
Когда в системе несколько устройств, ZFS балансирует нагрузку на
них так, чтобы задействовать каждое с максимальной продуктивностью,
и при добавлении нового диска к пулу автоматически заполняет его
новыми данными так, чтобы достичь максимальной скорости доступа
к данным. Если какое-то устройство в пуле замедлилось из-за сбоя или
заполнения значительной части его объема, ZFS старается не записывать
на него новых данных, чтобы не снижать общую скорость системы.
Встроенное сжатие данных (для каждой файловой системы можно
разрешить компрессию) не только уменьшает занимаемый данными
объем на диске, но и уменьшает требуемый объем ввода-вывода вдвое-втрое.
Поэтому в системах с быстрым слабо нагруженным процессором
включение сжатия ускоряет ввод-вывод.
Вот еще некоторые приемы, которые помогают повысить производительность
ZFS – пусть не всегда, но в определенных случаях.
Пустые блоки, если файл содержит таковые, не копируются физически
при копировании файла.
В чем здесь идея? Предположим, мы записали 1 байт в начале файла,
затем отступили от начала 1 гигабайт, и записали еще один значимый байт
в файл. Получили файл размером в 1 гигабайт и 1 байт, который в середине
содержит гигабайтное пространство, заполненное нулями. При копировании такого файла нет смысла переписывать все эти нули, это бы значительно снизило скорость операции. Поэтому было найдено решение, логично
вытекающее из структуры ZFS: файл в ZFS – это дерево блоков, каждый
блок косвенной адресации содержит поле, в котором указывается число
блоков, лежащих одним уровнем ниже него (см. ].
(рис 8.8) Файл с нулевыми блоками внутри (рисунок взят из [5]). Файл состоит из 9 блоков, три из которых на 100% заполнены нулями.Строго говоря, как именно обращаться с файлами, содержащими
обширные поля нулей внутри себя, решает не файловая система, а каждое
приложение индивидуально. В случае с ZFS файловая система умеет
работать с расширенной функцией lseek, в которой поддерживаются
операции SEEK_HOLE (найти следующую область нулей, размером не
менее переданного lseek аргумента) и SEEK_DATA (найти следующую
за областью нулей область единиц, не менее переданного аргумента размером).
В Solaris такая функциональность реализована, а пользоваться ей
или нет – дело разработчика программы.
Еще один момент – место под файл выделяется согласно простому,
но эффективному эмпирическому алгоритму: при выборе блоков для размещения
файла выбираются вначале устройство, на которое будет происходить
запись, затем область диска – более удаленая от центра диска
или менее удаленная, и наконец – собственно блоки. Выбор делается в
соответствии с весом каждого устройства, области и блока; вес определяется
по правилам, которые в следующих версиях ZFS могут измениться
для повышения эффективности. Текущие правила, например, включают
предпочтительность записи данных на свободные внешние дорожки дисков,
потому что конструкция современных дисков дает двукратное превышение
пропускной способности диска при такой записи – по сравнению
с внутренними дорожками. Естественно, что это некие усредненные данные
– на практике реальный прирост производительности будет зависеть
от размера записываемого блока, от размера файла, от конструкции диска
и от того, как соотносится размер кэша диска с размером файла и блока.
Что касается цифр, то любое тестирование можно провести так,
чтобы нужный продукт выглядел более выигрышно. Поэтому все, что я
считаю важным подчеркнуть в отношении известных результатов тестирования
ZFS, сводится к перечню тестовых операций, в которых она уже
показала значительное (2-6 раз) превышение производительности по
сравнению с UFS: []
открытие файла, резервирование пространства под его расширение и однократная запись в файл;
последовательная модификация (перезапись) всех данных в файле порциями по 32К;
создание файла размером 1/2 в свободной памяти посредством записи в него порциями по 1Мб, затем двукратное последовательное чтение всего файла;
параллельная синхронная запись в 100-мегабайтный файл четырьмя потоками.
Разумеется, количество тестов будет расти с ростом промышленного
использования ZFS, так что в скором времени появятся новые результаты.
Управление ZFS
Каждый системный администратор хочет, чтобы надежность его
систем и корректность прав доступа к данным не стоила ему долгих ночных
бдений. Так вот: в ZFS и резервное копирование, и назначение прав
доступа, и изменение размеров файловой системы делается очень просто.
Ощущение от перехода к ZFS можно передать аналогией с переходом
от автомобиля с ручной коробкой передач к машине с автоматической
коробкой.
Итак, к делу: как создать пул памяти ZFS, как смонтировать файловые
системы и как модифицировать созданные системы?
Создание и уничтожение пулов и файловых систем
Создание пула giant из двух дисков – c1t0d0 и c1t1d0
# zpool create giant c1t0d0 c1t1d0
Система ищет указанные диски в каталоге /dev/dsk, и помечает их
как принадлежащие одному пулу.
Так создается зеркальный пул (дисков в зеркале может быть больше,
чем два):
# zpool create giant mirror c1d0 c2d0
Массив RAID-Z из нескольких дисков создается командой
# zpool create giant raidz c1t0d0 c2t0d0 c3t0d0 c4t0d0
Для создания массива с двойным контролем четности надо вместо raidz указать raidz2.
Уничтожить пул можно командой zpool destroy. Осторожно: она уничтожает
пул, даже если в нем содержатся смонтированные наборы данных.
# zpool destroy giant
Добавление накопителей
Добавление устройства в пул – команда zpool add ; например, для
добавления еще двух устройств к зеркалу giant надо скомандовать
zpool add giant mirror c2t1d0 c2t2d0
После создания пула пора создавать файловые системы, которые
будут размещаться в нем – командой zfs create, которой требуется аргумент
– имя файловой системы; оно указывается как путь, начиная с
имени пула: pool-name/[filesystem-name/]filesystem-name
Имя пула и исходные имена файловой системы в пути идентифицируют
то местоположение, где будет создана новая файловая система. Все файловые
системы верхнего уровня уже должны существовать в пуле. Последнее
имя в пути идентифицирует имя создаваемой файловой системы.
Так мы создаем файловую систему oracle в файловой системе giant/home.
# zfs create giant/home/oracle
Если создание файловой системы прошло успешно, система ZFS
автоматически монтирует ее. По умолчанию точкой монтирования считается
путь, указанный в zfs create. В нашем примере вновь созданная
файловая система oracle монтируется в /giant/home/oracle.
Для получения информации о состоянии систем в пуле используют
команду zpool list:
# zpool list
NAME SIZE USED AVAIL CAP HEALTH ALTROOT
dbspace 340G 162G 178G 47% ONLINE -
samba 204G 134G 69.9G 65% ONLINE -
usrdb 68G 27.3G 40.7G 40% ONLINE -
Включить сжатие для файловой системы (в примере – home/archive )
можно командой
# zfs set compression=on concat/archive
Создание снимков, резервное копирование и восстановление файловых систем
Для создания снимка файловой системы достаточно дать такую команду:
zfs snapshot test/opt@18Feb08
В этой команде указывается имя файловой системы, снимок которой
мы хотим сделать, и сразу за ним, после символа @, указывается
имя снимка. Снимок делается практически мгновенно, так как никакого
физического копирования или переноса данных в момент создания
снимка не производится. В дальнейшем мы можем проверить наличие
такого снимка в файловой системе c помощью команды zfs list.
Файловую систему, для которой существуют снимки, нельзя удалить,
не удалив предварительно все ее снимки.
Кроме снимков, можно создавать клоны файловых систем, которые
фактически являются "снимками", смонтированными не только для чтения,
но и для записи. На этом основано клонирование зон с использованием
клонов ZFS (см. лекцию 6 курса "Системное администрирование ОС Solaris 10").
Снимок можно переслать на другое устройство для резервного копирования
командой zfs send:
zfs send test/opt@18Feb08 > backup
В этом примере мы выполнили копирование снимка в файл с именем backup, но вместо файла мы могли использовать любое устройство, в
том числе накопитель на магнитной ленте.
Для восстановления файловой системы (равно и снимка) из такой
резервной копии следует дать команду zfs receive:
zfs receive -F test/opt@snap < backup
Ключ -F требуется, если мы восстанавливаем из резервной копии
снимок, который в данный момент существует в файловой системе, и
тогда нам нужно явным образом указать, что мы хотим его перезаписать,
используя резервную копию.
Откат всей файловой системы к состоянию, в котором она пребывала
на момент снимка, выполняется командой zfs rollback:
zfs rollback test/opt@18Feb08
После выполнения этой команды файловая система test/opt будет
иметь в точности такое же содержание, что и в момент создания ее снимка
с именем 18Feb08.
Квотирование и резервирование пространства
Ограничение прав пользователей на заполнение всего свободного пространства
разнообразным мусором – от псевдо-деловой переписки позапрошлогодней
давности до видеоклипов разнообразного толка – давно стало
рутинным делом сисадминов, преимущественно с помощью механизма квот.
Квотирование сейчас поддерживается в любой современной файловой
системе, а вот в ZFS введена еще одна интересная возможность:
резервирование места для файлов пользователя. В самом деле, если вы
знаете, что пользователь oracle или mail нуждается в хранении большого
количества данных, удобно гарантировать ему это хранение независимо
от чьих-то аппетитов по выкачиванию видео из Сети.
Для резервирования в ZFS следует дать несколько простых команд
(только надо помнить, что зарезервировать можно только свободное
место; если его уже не хватает, то вначале надо удалить лишнее):
# zfs set reservation=4G pool/home/oracle
# zfs get reservation pool/home/oracle
NAME PROPERTY VALUE SOURCE
pool/home/oracle reservation 4.00G local
Нельзя зарезервировать для пользователя больше места, чем дозволено
квотой. При резервировании размер зарезервированного пространства
определяется последней по порядку командой zfs set reservation,
значения последовательных команд НЕ складываются.
# zfs set quota=5G pool/filesystem
# zfs set reservation=10G pool/filesystem/user1
cannot set reservation for 'pool/filesystem/user1': size is
greater than available space
Открытый код – в массы (перенос ZFS в другие системы UNIX)
В данный момент ZFS портирована во FreeBSD 7.0-RELEASE, однако
с некоторыми ограничениями по сравнению с Solaris []:
ZFS скомпилирована в качестве модуля ядра и доступна пока только для архитектуры i386; поддержка для amd64 будет добавлена в ближайшем будущем, другие архитектуры тоже планируется поддерживать;
Не поддерживается разделение ZVOL на базе iSCSI;
Не поддержтиваются ACL и расширенные атрибуты;
С устройства под ZFS нельзя загрузить систему.
Перенос ZFS в Linux затруднен по лицензионным соображениям,
потому что ядро Linux распространяется с лицензией GPL, запрещающий
присоединение к нему кода, который распространяется с отличной от
GPL лицензией. Код Solaris, и в частности код ZFS, тоже открыт для всех,
но на условиях лицензии CDDL. В рамках программы Google Summer of
Code был поддержан проект портирования ZFS под Linux таким образом,
чтобы код поддержки файловой системы выполнялся в пространстве
пользователя (user space), а не в пространстве ядра (kernel space). Для
этого необходимо иметь ядро версии 2.6.14 либо более свежее, так как в
нем можно скомпилировать модуль FUSE, позволяющий исполнять код
файловой системы таким образом. Однако в такой конфигурации следует
ожидать заметного снижения скорости работы файловой системы.
В Mac OS X Leopard (Build 9a321), вышедшем в декабре 2006 года,
впервые в истории Mac OS была встроена поддержка ZFS.
Дополнительные материалы для чтения о ZFS
ZFS Best Parctices Guide:
http://www.solarisinternals.com/wiki/index.php/ZFS_Best_Practices_Guide#ZFS_Management_Tools_.2F_Observability
Manual setup to boot ZFS on x86:
http://www.opensolaris.org/os/community/zfs/boot/zfsboot-manual/
Администрирование ZFS (перевод на русский язык): документ
можно скачать со страницы http://dlc.sun.com/osol/g11n/downloads/docs/current/
в виде архива с .html-файлами.
Литература
Bernd PanzerSteindel, Data integrity, http://indico.cern.ch/getFile.py/access?contribId=3sessionId=0resId=1materialId=paperconfId=13797
ZFS on-disk specification, 2006, http://opensolaris.org/os/community/zfs/docs/ondiskformat0822.pdf
http://en.wikipedia.org/wiki/ZFS
Seth Lloyd, Ultimate physical limits to computation, 2000
Jeff Bonwick, http://blogs.sun.com/bonwick/
http://blogs.sun.com/roch/entry/zfs_to_ufs_performance_comparison
Pawel Jakub Dawidek, 2007, http://lists.freebsd.org/pipermail/freebsdcurrent/2007-April/070544.html