Привет, Хабр! Меня зовут Алексей, я главный эксперт по разработке ПО в департаменте разработки СХД YADRO. В этой статье я расскажу об устройстве такого важного программного компонента СХД, как маппер, о реализуемой с его помощью функциональности TATLIN.UNIFIED — полноценных тонких томах, снапшотах, клонах — и о планах по развитию этого компонента.

Введение: блочные устройства

Для начала — несколько слов для читателей, мало знакомых с тематикой. СХД TATLIN.UNIFIED предоставляет как блочный, так и файловый доступ к данным. В этой статье я буду рассматривать именно блочный доступ, поскольку это базовая функциональность, поверх которой уже реализуется файловый.

Блочный доступ — это механизм доступа к данным, реализованный во множестве устройств, от дискет и HDD до СХД промышленного масштаба. Пример типичного блочного устройства — это диск вашего ноутбука, который вы знаете как диск «C:». Поверх блочного устройства операционная система создает файловую систему и обеспечивает работу с более дружелюбной сущностью — файлами. Однако под капотом общение с диском происходит на блочном уровне.

Блочные устройства оперируют блоками фиксированного размера — например, 512 байт. Данные сохраняют и извлекают целыми блоками, указывая их логический адрес (Logical Block Address, LBA). Объем записанных данных при блочном доступе всегда кратен объему одного блока, вне зависимости от того, сколько нужно записать.

Виртуальные, то есть определенные именно на программном уровне блочные устройства называют логическими томами. На одной СХД — например, TATLIN.UNIFIED — могут быть созданы сотни логических томов, которые доступны внешним серверам по сети через блочные протоколы SCSI или NVMe. 

Косвенная адресация

Изначально СХД TATLIN.UNIFIED поддерживала только логические тома с прямой адресацией, и сейчас эта поддержка тоже предусмотрена. Адресное пространство томов с прямой адресацией отображается на адресное пространство T-RAID напрямую — отсюда и название. Прямая адресация обеспечивает надежное и отказоустойчивое хранение данных. Но ее ограничения не дают реализовать функциональность уровня современных СХД — например, снапшоты или сжатие данных. Для этого в TATLIN.UNIFIED был введен новый вид логических томов — тома с косвенной адресацией.

Том с косвенной адресацией — это том с картой указателей на блоки данных, сохраненной в области метаданных. Карта содержит статус каждого блока данных логического тома и его физический адрес, если блок занят. При создании пустой том с косвенной адресацией не занимает места в области данных. В области метаданных при этом записан минимальный объем, где отражено наличие тома и отсутствие у него занятых блоков. По мере записи информации из пула свободных блоков в области данных выделяются новые блоки. После записи в них карта указателей обновляется. В томах с косвенной адресацией размер блока равен 4 КиБ, а значит, они занимают в хранилище ровно столько места, сколько данных в них записали — с точностью до 4096 байт.

При повторной записи в уже занятые блоки данных оригинальные данные не перезаписываются. Вместо этого запись осуществляется в новые блоки с соответствующим обновлением метаданных тома. А освобожденные после перезаписи оригинальные блоки становятся доступны для последующих записей. Эта схема обеспечивает высокую производительность при записи данных «случайным» паттерном. Множество разных записей объединяется в одну последовательную запись, которая гораздо эффективнее с точки зрения быстродействия. Такой способ записи также упрощает реализацию мгновенных снимков.

Снапшоты

Мгновенный снимок (снапшот) — это компактная с точки зрения дискового пространства копия данных, созданная в определенный момент времени. Снапшот способен моментально зафиксировать состояние тома, в отличие от резервной копии, создание которой при большом объеме данных может занять длительное время и требовать остановки записи для сохранения консистентности. Снапшот же не создает независимую копию данных, а лишь обеспечивает возможность обратиться к данным тома на момент создания снапшота.

В TATLIN.UNIFIED снапшоты создаются путем копирования карты блоков данных оригинального тома. Как я сказал выше, сами данные не копируются, поэтому снапшоты создаются очень быстро и не занимают дополнительного места в области данных.

Со временем в родительском томе заполняются новые блоки данных. Некоторые данные у родительского тома и снапшота начинают различаться, но данные, на которые уже ссылается снапшот, не перезаписываются и не освобождаются. Оригинальный физический блок данных считается занятым до тех пор, пока снапшот, который на него ссылается, не будет удален. После удаления снапшота блоки данных, которые он не разделял с другими ресурсами, освобождаются и могут быть использованы для последующих операций записи. Такой вариант реализации снапшотов называют Redirect-On-Write (RoW).

Клоны

Еще одна полезная функциональность томов с косвенной адресацией — это клоны. Снапшоты не поддерживают операции ввода-вывода, но от снапшота можно создавать клоны. Как и снапшот, клон создается мгновенно и не занимает дополнительного места в области данных. Клон доступен как для записи, так и для чтения. В следующем релизе мы добавим клоны, доступные только для чтения, чтобы исключить риск перезаписи архивируемых данных.

Блоки, на которые ссылается клон, сохраняются в области данных. Зависимости между клоном и родительским снапшотом нет: после создания клона снапшот вполне можно удалить. Клон также может иметь снапшоты, для которых, в свою очередь, можно создавать другие клоны.

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

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

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

Восстановление из снапшота

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

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

Группы консистентности

Рассказ о возможностях томов с косвенной адресацией будет неполным без упоминания групп консистентности. Это группы ресурсов, для которых снапшот создается одновременно. Так гарантируется консистентное (crash-consistent) состояние всех снапшотов в группе. Подобные снапшоты необходимы приложениям, использующим несколько томов для хранения данных.

Детальное описание сценариев и принципов работы групп консистентности достойно отдельной статьи. Далее же я предлагаю посмотреть, что находится под капотом у томов с косвенной адресацией.

Маппер

Как и тома с прямой адресацией, тома с косвенной адресацией создаются в рамках дискового пула. Только в данном случае это новый тип пулов — пул с косвенной адресацией, представляющий собой T-RAID пул с заранее размещенными областями для хранения данных и метаданных. Все тома с косвенной адресацией в пределах одного пула используют одни и те же области данных и метаданных. За отображение адресов логических томов на область данных с помощью метаданных отвечает маппер — архитектурный слой программного стека TATLIN.UNIFIED.

В стеке обработки запросов ввода-вывода маппер находится над уровнем T-RAID и опирается на его алгоритмы защиты целостности данных. Область данных пула с косвенной адресацией защищена по схеме, заданной пользователем, — D + P (например, 8 + 2). Так можно определять характеристики производительности и отказоустойчивости пула.

Область метаданных же всегда защищена схемой с зеркалированием, уровень защиты которой совпадает с уровнем защиты области данных. То есть для области данных со схемой D + P область метаданных будет использовать схему 1 + P.

Такой подход обусловлен характерным паттерном обновления метаданных — случайная запись небольшим блоком. С зеркалом можно избежать ресурсозатратных частичных обновлений страйпов (Read-Modify-Write) и тем самым обеспечивает лучшую производительность. Дополнительной избыточностью, характерной для схем с зеркалированием, в случае метаданных можно пренебречь: в типичных сценариях объем метаданных намного меньше объема данных.

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

Такая организация стека позволяет мапперу эффективно работать и с T-RAID. Множество разрозненных записей объединяются в одну последовательную запись нескольких страйпов — full-stripe write. Это устраняет проблему низкой производительности при записи мелким блоком, когда обновление части страйпа приводит к дополнительному чтению и записи из-за пересчета контрольных сумм. Объединенная запись требует и меньше обновлений метаданных, что также улучшает производительность.

Кэш и снапшоты

В таком построении стека существуют и свои сложности. По стеку маппер находится ниже кэша на запись, поэтому ничего не знает о данных в нем. И если просто создать снапшот в маппере, то он не будет содержать части записанных данных. Здесь необходимо предварительно сбросить кэш на диски, не обрабатывая при этом новые запросы на запись, чтобы новые данные не попали в снапшот. Сброс кэша может занимать длительное время. Большие задержки в обработке запросов приведут к серьезным проблемам на клиентской стороне и совершенно недопустимы. Поэтому при включенном кэше на запись создание снапшота состоит из двух этапов.

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

Второй этап — сброс данных старой эпохи на диски, принадлежащие снапшоту. Это продолжительный этап, но на обработку запросов ввода-вывода он не влияет. Данные старой эпохи, сброшенные на диски, освобождают в кэше место для новых данных. Когда все данные старой эпохи сброшены на диски, на уровне маппера создается полноценный снапшот. Затем разрешается сброс на диски страниц кэша, принадлежащих новой эпохе. Благодаря механизму Redirect-On-Write маппер запишет их как не вошедшие в снапшот.

Метаданные

Все, что делает маппер, так или иначе связано с чтением или обновлением метаданных. Они содержат служебную информацию: структуру данных на дисках, атрибуты созданных томов и снапшотов, отображение логических адресов в физические, сведения о занятых и свободных блоках и другие важные данные.

Все метаданные упакованы в страницы размером 4 KиБ. Это минимальный блок, который может быть записан в зеркало T-RAID без выполнения дополнительного чтения. Так маппер минимизирует накладные расходы при обновлении метаданных. Каждая страница метаданных самодостаточна и содержит служебную информацию — с ней можно определить тип содержащихся метаданных и убедиться в их достоверности.

Время обращения к метаданным критически важно при обработке запросов ввода-вывода, поэтому в маппере предусмотрен кэш метаданных — как на чтение, так и на запись. Кэш на чтение работает по алгоритму LRU (least recently used) и держит прочитанные или записанные страницы метаданных в оперативной памяти, пока к ним периодически обращаются. Кэш на запись сглаживает спайки при массированных обновлениях метаданных и позволяет выполнять запись позднее, когда система не занята. Или не выполнять совсем, если страница метаданных еще раз перезаписана.

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

Ещё один важный аспект работы с метаданными — это транзакционность. В рамках одной транзакции обновление нескольких страниц метаданных должно либо выполняться полностью, либо же не выполняться вовсе. Это необходимо для поддержания консистентного состояния метаданных.

Самый простой пример, объясняющий это требование, — запись нового блока данных тома. Здесь обновляются две страницы метаданных: битовая карта свободных блоков, в которой блок помечается как занятый, и карта адресации тома, в которую записывается адрес блока данных. Эти изменения должны быть выполнены в полном объеме, иначе блок может оказаться утерянным (отмечен занятым, но нигде не используется) или, наоборот, использован дважды (по факту используется, но отмечен свободным).

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

Если же кэш на запись выключен (например, когда батарея вышла из строя), то данный способ не работает. В отличие от обновления в оперативной памяти, запись различных блоков метаданных на диск невозможно выполнить атомарно. Поэтому в таком случае включается механизм журналирования. Изменения метаданных предварительно записываются в журнал и уже потом применяются к реальным метаданным. В случае аварийного останова и перезагрузки журнал проигрывается, гарантируя применение всех изменений атомарно. Использование журнала требует дополнительных операций ввода-вывода и вполне ожидаемо замедляет работу маппера, но зато гарантирует сохранность и консистентность метаданных.

Механизмы адресации

Детальное описание всех видов метаданных маппера и сценариев их использования выходит за рамки данной статьи. Однако один тип метаданных — а именно блоки косвенной адресации — все же стоит рассмотреть как важную составляющую компонента.

Основная задача маппера — это отображение логических адресов тома на физические адреса в области данных. Похожую задачу, только для файлов, решает почти любая файловая система. Обычно информация о блоках данных, принадлежащих файлу, хранится в виде древовидной структуры. Здесь один из самых распространенных вариантов — это использование B-деревьев. Хоть маппер и не является полноценной файловой системой, но в данном случае использует аналогичный подход.

B-дерево — это сильно ветвящееся дерево поиска, специально разработанное для эффективного поиска данных на устройствах хранения. Один узел такого дерева хранит множество элементов, что снижает количество операций ввода-вывода и позволяет получить за одну операцию максимум релевантной информации. Как правило, размер узла выбирается равным или кратным размеру блока устройства хранения.

Маппер реализует специальный вариант B-дерева с элементами префиксного дерева, в котором узлы не хранят ключи в явном виде. Узел такого дерева содержит 640 указателей на данные либо на узлы-потомки. Каждый узел отвечает за определенный диапазон логических адресов тома. Размер диапазона определяется высотой узла в иерархии, а начало диапазона — позицией в массиве указателей родительского узла. Указатели в листьях адресуют блоки в области данных размером 4 КиБ, соответственно такой узел отвечает за диапазон 4 КиБ × 640 = 2,5 МиБ. На следующем уровне узлы отвечают за диапазон 2,5 МиБ × 640 = 1600 МиБ и так далее. Высота дерева определяется максимальным размером тома.

Коэффициент ветвления 640 в узлах дерева выбран не случайно. Маппер использует 48-битные указатели, которые при выравнивании по размеру блока позволяют адресовать до экзабайта данных. Массив из 640 таких указателей занимает 3840 байт, что идеально упаковывается в одну страницу метаданных (4 КиБ), оставляя небольшой резерв для служебной информации.

Вот так схематично выглядит адресация к данным с помощью метаданных. Для поиска по дереву необходимо последовательно делить логический адрес (LBA) на 640 и брать остаток от деления. Первый остаток будет индексом в массиве указателей листа, следующий остаток — в узле уровнем выше и так далее. Физический адрес получается последовательным чтением узлов дерева согласно полученным индексам начиная с корневого узла. Нулевое значение указателя означает, что данные в этот диапазон не записаны и поиск можно прервать досрочно.

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

Соотношение данных и метаданных

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

Для TATLIN.UNIFIED GEN2 в конфигурации по умолчанию объем кэша метаданных на чтение составляет около 60 ГиБ, то есть 15 млн страниц метаданных. Активный диапазон логических адресов в кэше метаданных составит около 37 ТиБ (15 млн × 2.5 МиБ). Узлами дерева выше листьев, самого нижнего слоя, в этих расчетах можно пренебречь. Уже на следующем уровне их в 640 раз меньше и они не вносят существенный вклад в результат. По той же причине мы пренебрегаем в расчетах и другими видами метаданных.

Стоит рассмотреть и накладные расходы на хранение данных, которые вносит маппер, то есть соотношение между данными и метаданными. Сразу после создания пула место в нем не относится ни к данным, ни к метаданным. По мере записи оно распределяется в соответствии с характером нагрузки. Можно представить, что данные и метаданные движутся навстречу друг другу, занимая свободное пространство:

Рассмотрим два крайних случая соотношения данных и метаданных:

  • Если мы имеем дело с последовательной записью, когда каждый листовой узел полностью заполнен указателями на данные, то соотношение объемов метаданных и данных составит 1:640.

  • Если запись блоком 4 кБ полностью случайна и широко распределена в адресном пространстве тома, то в худшем случае в один листовой узел дерева будет записан только один указатель на блок данных. Тогда соотношение метаданных и данных составит 1:1.

Второй сценарий хоть и возможен теоретически, но на практике встретить его вряд ли получится, если только не сгенерировать такой паттерн вручную. Реальные приложения соблюдают некоторую локальность записи, и объем метаданных обычно не превышает 2–3% от объема данных даже при случайном характере нагрузки. Любопытно, что наличие снапшотов и клонов на это соотношение практически не влияет. Созданный снапшот увеличивает объем как данных, так и метаданных — в пропорции, определяемой паттерном записи.

Учет занятого места для тома

Кроме объема данных и метаданных администратору СХД нужно понимать, сколько места в пуле занимает тот или иной том. Тома с косвенной адресацией всегда тонкие, поэтому сразу после создания не занимают места в области данных, а заполняют его впоследствии по мере записи. Маппер отслеживает количество занятых блоков данных для каждого тома. Это значение позволяет узнать, сколько данных было записано в том с косвенной адресацией, и в простом случае оценить, сколько места в пуле он занимает.

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

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

Заключение

Немного расскажу о планах развития маппера. В одном из ближайших релизов появится полноценная поддержка SCSI-команд UNMAP и WRITE_SAME. Они позволяют помечать диапазоны адресов тома как неиспользуемые и тем самым высвобождать блоки данных для других записей. Современные файловые системы активно используют эту полезную функциональность.

В дополнение к этому появится функциональность Zero Detect — автоматическая замена записываемых нулевых блоков данных на соответствующие команды UNMAP для экономии места. Также планируется реализовать учет используемого места для родственных групп, оптимизации при работе в режиме одного контроллера хранения, увеличение лимитов и многие другие улучшения. В более отдаленных планах — поддержка компрессии и дедупликации данных, над которыми уже активно ведется работа.

Для лучшей наглядности можно посмотреть отдельное видео о снапшотах на сайте YADRO. Больше о планах по развитию СХД TATLIN вы можете узнать в роадмапе.

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


  1. RodionGork
    22.05.2025 10:53

    Простите за вопрос - вот эта СХД TATLIN.UNIFIED - она на каких условиях доступна корпоративным и персональным пользователям? Это не то что можно найти и скачать в гитхабе?


    1. klauss_z
      22.05.2025 10:53

      СХД — это программно-аппаратный комплекс, это не то, что можно скачать на гитхабе, конечно. Но в статье рассматривается именно программная часть решения.


  1. rm76
    22.05.2025 10:53

    Скажите, есть ли планы по увеличению максимального количества снепшотов на ресурс?


    И, что кажется совсем простым для реализации - создания снепшотов по расписанию, удалению их либо по расписанию либо по некоторму retention policy исходя из свободного места в пуле?


    1. axle Автор
      22.05.2025 10:53

      Максимальное количество снапшотов на ресурс планируется увеличить уже в следующем крупном релизе. Будет поддерживаться до 16 снапшотов на ресурс.

      Создание снапшотов по расписанию тоже обязательно будет, но несколько позже, не в ближайшее время.


  1. adrozhzhov
    22.05.2025 10:53

    Спасибо (от человека, который с утра на конлюенсе добавил такое чтобы понимали кишочки работы)

    В целом так, на дисковом массиве вложенность посложнее правда.

    LUN -> LUN GROUP -> PROTECTION GROUP -> SNAPSHOT CONSISTENCY GROUP.

    Т.е. базовая единица хранения LUN, луны по логике использования объединяются в LUN GROUP.

    Далее есть функционал «защиты данных». Создается PROTECTION GROUP – набор связанных данных которые нужно «защитить».

    Далее создается CONSISTECY GROUP – абстракция, которая означает «место дублирования исходных данных». Т.к. защищать можно разными методами и их комбинациями (снепшоты, репликация, клоны и всякие вариации) есть разные типы CONSISTENCY GROUP: SNAPSHOT CG, REPLICATION CG. Ну и с этими группами доступны разные методы работы (обновить снепшоты, запустить/остановить репликацию, сделать/удалить копию данных).

    Добавлю теперь ссылку на эту статью туда же, для тех кто