Глубокое погружение в реализацию хранилища метаданных

Глубокое погружение в реализацию хранилища метаданных

21 мая 2022 г.

В этой статье рассказывается о дизайне и реализации хранилища метаданных в Alluxio Master как в куче, так и вне кучи (на основе RocksDB).


Фон


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


В качестве распределенной системы Alluxio использует архитектуру Master-Worker. Кластер Alluxio состоит из одного или нескольких главных узлов и нескольких рабочих узлов. Alluxio Master обслуживает все пользовательские запросы и регистрирует изменения метаданных файловой системы. Работники Alluxio несут ответственность за управление настраиваемыми пользователем локальными ресурсами, выделенными для Alluxio (например, памятью, твердотельными и жесткими дисками). Рабочие процессы Alluxio хранят данные в виде блоков и обслуживают запросы клиентов, которые читают или записывают данные, читая или создавая новые блоки в своих локальных ресурсах.



В этой статье рассказывается, как Alluxio реализует хранилище метаданных в Alluxio Master.


Два способа хранения метаданных в Alluxio


Внутри мастера Alluxio каждый файл или каталог представлен структурой данных, называемой inode, которая содержит атрибуты, такие как права доступа к файлам, и другие метаданные, такие как расположение блоков. Alluxio master хранит метаданные всей файловой системы в виде дерева инодов, аналогично HDFS и другим файловым системам на базе UNIX.


Alluxio предоставляет два способа хранения метаданных:


  • ROCKS: хранилище метаданных на основе RocksDB на диске.

  • HEAP: хранилище метаданных в куче

По умолчанию это камни.


Исходный код Alluxio предоставляет два интерфейса, InodeStore и BlockStore, в каталоге /core/server/master/src/main/java/alluxio/master/metastore. Метаданные, управляемые InodeStore, включают отдельные индексные дескрипторы и отношения родитель-потомок между различными индексными дескрипторами. BlockStore отвечает за управление размером блока и расположением блоков файловых данных. cachingInodeStore реализует настраиваемый кеш в куче Java перед RocksDB для повышения производительности. В HEAP эти интерфейсы реализованы через HeapInodeStore и HeapBlockStore. Общие отношения зависимости следующие.



Во время AlluxioMasterProcess две фабрики генерируются для соответствующих хранилищ, а в фабриках генерируются разные InodeStore и BlockStore на основе конфигураций.


РОКС Метастор



RocksDB — это встроенная база данных типа «ключ-значение». Пользователи могут добиться эффективного хранения данных KV и доступа к ним, вызвав интерфейс API.


Интерфейс BlockStore реализуется RocksBlockStore со следующим процессом.


  1. Фабрика добавляется в mContext в AlluxioMasterProcess.

  1. MasterUtils.createMasters() создаст все основные потоки по порядку. При создании DefaultBlockMaster он вызывает BlockStore.Factory для создания экземпляра RocksBlockStore.

  1. RocksDB.loadLibrary() будет вызываться при запуске RocksBlockStore для загрузки зависимых библиотек, а затем создания экземпляра класса RocksStore в соответствии с файлом конфигурации. RocksStore используется для работы с базой данных RocksDB, включая инициализацию базы данных, резервное копирование и восстановление базы данных и т. д.

  1. При создании RocksStore экземпляр класса RocksDB создается путем вызова метода RocksDB.open() в методе create().

  1. DefaultBlockMaster читает и записывает в базу данных RocksDB, используя этот класс RocksDB.

RocksBlockStore использует следующие основные методы:


  • getBlock(длинный идентификатор)

  • putBlock (длинный идентификатор, метаданные BlockMeta)

  • removeBlock(длинный идентификатор)

  • getLocations (длинный идентификатор)

  • addLocation (длинный идентификатор, местоположение BlockLocation)

  • removeLocation (длинный идентификатор блока, длинный рабочий идентификатор)

Давайте возьмем getBlock() в качестве примера. getBlock() используется для получения метаданных соответствующего блока по blockId следующим образом.


@Override


public Optional getBlock(длинный идентификатор) {


байт [] мета;


пытаться {


meta = db().get(mBlockMetaColumn.get(), Longs.toByteArray(id));


} поймать (RocksDBException e) {


бросить новое исключение RuntimeException(e);


если (мета == ноль) {


вернуть необязательный.пустой();


пытаться {


return Optional.of(BlockMeta.parseFrom(meta));


} поймать (Исключение e) {


бросить новое исключение RuntimeException(e);


Ниже приведено взаимодействие с RocksDB:


meta = db().get(mBlockMetaColumn.get(), Longs.toByteArray(id));


Метод db() в приведенном выше коде вернет ранее созданный класс RocksDB, а затем вызовет метод RocksDB.get(). Метод get() требует два параметра: первый — ColumnFamilyHandle, а второй — blockId.


Каждая пара K-V в RocksDB соответствует ColumnFamily, а ColumnFamily эквивалентна логическому разделу в RocksDB. Когда нам нужно запросить данные в ColumnFamily, нам нужно управлять базовой базой данных через ColumnFamilyHandle, который создается при создании экземпляра RocksDB.


Данные K-V, хранящиеся в RocksDB, хранятся в виде строк байтов, поэтому нам нужно преобразовать blockId в byte[] и преобразовать значение, возвращенное из RocksDB, обратно в объект BlockMeta Java с использованием буферов протокола Google.


Интерфейс InodeStore в методе ROCKS реализуется CachingInodeStore и RocksInodeStore. CachingInodeStore использует память для хранения кеша метаданных, а RocksInodeStore — это хранилище метаданных, реализованное RocksDB в качестве резервного хранилища CachingInodeStore.


Когда метаданные кластера Alluxio могут быть полностью сохранены в CachingInodeStore, Alluxio не взаимодействует с RocksInodeStore, а вместо этого использует CachingInodeStore для повышения производительности. Когда емкость хранилища CachingInodeStore достигает порогового значения, Alluxio автоматически переносит метаданные из CachingInodeStore в RocksInodeStore. На данный момент производительность доступа к метаданным зависит от скорости попадания в кэш CachingInodeStore и производительности RocksDB.


Процесс создания и использования RocksInodeStore аналогичен процессу RocksBlockStore. Если для параметра MASTER_METASTORE_INODE_CACHE_MAX_SIZE установлено значение 0, используется RocksInodeStore. если он не равен 0, то необходимо создать как CachingInodeStore, так и RocksInodeStore.


кейс РОКС:


Конфигурация экземпляра = ServerConfiguration.global();


если (conf.getInt(PropertyKey.MASTER_METASTORE_INODE_CACHE_MAX_SIZE) == 0) {


вернуть lockManager -> новый RocksInodeStore (baseDir);


} еще {


return lockManager -> new CachingInodeStore (new RocksInodeStore (baseDir), lockManager);


Хранилище метаданных кучи


Alluxio использует динамическую память в качестве хранилища в методе Heap. При создании AlluxioMasterProcess создаются HeapInodeStore и HeapBlockStore для реализации интерфейсов InodeStore и BlockStore.


При создании HeapInodeStore создается ConcurrentHashMap с именем mInodes для хранения информации Inode файлов и папок. В то же время будет создан TwoKeyConcurrentMap с именем mEdges для хранения отношений родитель-потомок различных узлов.


private final Map<Long, MutableInode<?>> mInodes = new ConcurrentHashMap<>();


// Сопоставление идентификатора инода с идентификаторами дочерних элементов этого инода. Внутренние карты упорядочены по имени ребенка.


private final TwoKeyConcurrentMap> mEdges =


новый TwoKeyConcurrentMap<>(() -> новый ConcurrentHashMap<>(4));


TwoKeyConcurrentMap — это класс, определенный в Alluxio, который реализует ConcurrentMap, поддерживающий два ключа со следующей логической структурой: >.


Каждый ключ в структуре данных mEdges сопоставляет идентификатор Inode с картой его дочерних элементов, которая затем сопоставляет имя каждого дочернего элемента с его идентификатором Inode.


В HeapBlockStore создается ConcurrentHashMap с именем mBlocks для хранения метаданных каждого блока, а TwoKeyConcurrentMap с именем mBlockLocations создается для хранения местоположения блока в рабочем потоке.


// Сопоставление идентификатора блока с метаданными блока.


public final Map mBlocks = new ConcurrentHashMap<>();


// Сопоставление идентификатора блока с расположением блоков.


открытый финал TwoKeyConcurrentMap>


mBlockLocations = new TwoKeyConcurrentMap<>(() -> new HashMap<>(4));


Два ключа в mBlockLocations — это blockId и workerId, а значение — это конкретные места в рабочем потоке, где хранится блок. BlockId можно использовать для получения местоположения блока, хранящегося в рабочем потоке.


Резюме


Alluxio предоставляет два способа хранения метаданных: ROCKS и HEAP, а хранилище по умолчанию — ROCKS. Для ROCKS, помимо использования RocksDB, Alluxio предоставляет кеш в памяти для повышения производительности чтения и записи метаданных. В результате мы можем получить высокую производительность при ограничении размера хранилища метаданных. С RocksDB мы можем хранить метаданные на жестком диске, чтобы получить больше места для хранения.


В общем, мы должны использовать RocksDB для хранения метаданных в Alluxio Master. Если вам нужно хранить только небольшой объем метаданных, требующих очень высокой производительности чтения и записи метаданных, вы также можете рассмотреть возможность использования метода HEAP.


Об авторе


Чаншэн Гу, инженер по работе с большими данными в China Mobile.


Гу работает в China Mobile Cloud Center, разрабатывая облачное озеро данных с упором на HDFS и Alluxio.



Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE