⚠️ Эта страница автоматически переведена, и перевод может быть несовершенным.
blog-post

About Columnar storage in Manticore Search

Введение

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

Хранилище атрибутов по умолчанию (строковое)

В Manticore существуют две отдельные сущности: полнотекстовые поля, поддерживающие только полнотекстовые запросы, и атрибуты различных типов, которые могут использоваться для группировки, сортировки и фильтрации. Хранилище по умолчанию (engine='rowwise') сохраняет все атрибуты всех документов в памяти.

Для загрузки атрибутов в память используется mmap, который можно настроить через параметры access_plain_attrs и access_blob_attrs. Первый параметр отвечает за загрузку файлов .spa, содержащих все атрибуты фиксированной длины (integer, bigint, float и т.д.). Второй параметр — за загрузку файлов .spb, содержащих атрибуты переменной длины (string, mva, float_vector и т.д.). Поскольку mmap загружает данные в память только при обращении, начальные запросы, использующие атрибуты, могут быть медленнее. Чтобы смягчить это, по умолчанию для access_plain_attrs и access_blob_attrs установлен режим mmap_preread, при котором Manticore читает файлы .spa и .spb в фоновом потоке при старте. Это обычно означает (но не гарантирует), что файлы атрибутов будут находиться в памяти, избегая необходимости чтения данных с диска во время запросов. Однако, если система решит, что памяти недостаточно, атрибуты могут быть частично или полностью выгружены из памяти, что снова замедлит запросы. Чтобы избежать этого, в access_plain_attrs / access_blob_attrs можно указать mlock. Если памяти достаточно, атрибуты будут гарантированно оставаться в памяти, и система не будет их выгружать. Однако при недостатке памяти никаких гарантий нет.

Зачем нам колонное хранилище?

В ситуациях, когда доступной памяти достаточно для хранения всех атрибутов, традиционное «строковое» хранилище работает эффективно. Проблемы возникают, когда памяти недостаточно для всех атрибутов.
Действительно, mmap может автоматически выгружать и перезагружать части файлов атрибутов по мере необходимости, позволяя работать даже при ограниченной памяти. Однако на практике такой подход может значительно снизить производительность до неприемлемого уровня. Проблема частично кроется в архитектуре строкового хранилища, которое сохраняет все атрибуты одного документа последовательно, затем атрибуты следующего документа и так далее. Ниже пример такой таблицы данных:

Файл .spa имеет следующую структуру:

Файл .spb выглядит так:

Когда запрос включает группировку по конкретному атрибуту, требуется получить только значение этого атрибута для каждого документа. Однако mmap работает так, что не может читать отдельные байты; вместо этого он загружает одну или несколько страниц памяти, обычно по 4 КБ каждая. Это означает, что при попытке прочитать значение одного атрибута система часто загружает значения всех соседних атрибутов, даже если они не нужны для обработки запроса.

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

Колонное хранилище (engine='columnar') было разработано именно для ситуаций, когда памяти недостаточно, чтобы загрузить все атрибуты. Этот формат хранилища предлагает следующие преимущества:

  • Данные каждого атрибута хранятся отдельно и могут читаться без влияния на другие атрибуты.
  • Поскольку данные внутри одного атрибута обычно однородны, их можно сжимать.
  • Значения атрибутов могут делиться на блоки из нескольких документов для сжатия.
  • После распаковки блоков можно применять оптимизации для потоковой обработки.
  • В ОЗУ хранится лишь очень небольшой кусок метаданных, всё остальное находится на диске.
  • Для быстрого доступа к «горячим» данным вместо загрузки страниц в память через mmap используется кэш файловой системы.

Схематически структуру данных в колонном хранилище можно представить так:

Колонное хранилище поставляется в отдельной библиотеке под названием MCL (Manticore Columnar Library). Эта библиотека отвечает за создание хранилища, упаковку и чтение данных.
Кроме того, в сам поисковый демон было добавлено значительное количество кода для работы с атрибутами, учитывающего особенности колонного хранилища.

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

Ниже приведены несколько примеров того, что было добавлено в демон для работы с колонным хранилищем:

  • Колонный сортировщик
    В архитектуре Manticore документы, найденные через полнотекстовый поиск, сразу отправляются в то, что называется Сортировщиком. Сортировщик может просто сортировать, либо группировать документы, вычислять агрегаты и многое другое. Однако доступ к атрибутам в колонковом хранилище медленный, потому что значения извлекаются по одному.
    Если запрос относительно лёгкий — то есть полнотекстовый поиск быстрый или вовсе отсутствует — извлечение атрибутов из хранилища по одному может существенно влиять на производительность. Поэтому в некоторых случаях выгодно использовать дополнительный сортировщик поверх стандартного Сортировщика. Этот дополнительный сортировщик не сортирует, а аккумулирует документы и извлекает колонковые атрибуты пакетами перед передачей документов основному Сортировщику для окончательной обработки, что значительно быстрее.

  • Колонный группировщик
    Группировщик в Manticore отвечает за преобразование входящих документов в один или несколько ключей группировки для последующей передачи в Сортировщик. Основная цель колонного группировщика — улучшить производительность за счёт уменьшения количества virtual calls . Например, когда работает обычный группировщик, он запрашивает данные из выражения, которое извлекает данные из хранилища. Это выражение реализовано на стороне демона и обеспечивает прозрачный доступ к различным типам хранилищ атрибутов. Внутри оно использует колонковый итератор (рассмотренный ниже), реализованный на стороне библиотеки MCL, который напрямую обращается к хранилищу. Специализированный колонный группировщик, однако, знает, что будет работать только с колонковым хранилищем, и убирает некоторые абстракции, работая напрямую с колонковым итератором.
    При группировке по строкам используется иной механизм. Когда строки добавляются в колонковое хранилище, хеш каждой строки (с учётом текущей collation ) автоматически вычисляется и сохраняется как отдельный целочисленный атрибут. Колонный группировщик извлекает эти хеши из колонкового хранилища вместо чтения самой строки и вычисления хеша для использования в качестве ключа группировки.

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

  • Оптимизатор запросов ( Cost based optimizer , CBO) также должен учитывать колонковое хранилище. CBO определяет путь выполнения запроса. Например, он может заменить один из фильтров поиском по соответствующему вторичному индексу, убрав фильтр из запроса. В результате вторичный индекс вернёт номера документов (row IDs), на которых будут работать оставшиеся фильтры.
    Когда используется колонковое хранилище, оптимизатор должен сравнивать производительность вторичного индекса и колонкового анализатора (который выполняет быстрый поиск в колонковом хранилище, подробнее ниже). В зависимости от данных и количества доступных потоков иногда один метод работает быстрее другого. Например, колонковый анализатор лучше масштабируется параллельно.

Что включено в MCL?

Среди компонентов, непосредственно входящих в библиотеку MCL, можно выделить две основные группы:

1.Builder
Отвечает за получение необработанных данных от демона и построение колонкового хранилища.

2.Accessors
Отвечает за доступ к данным. Существует два основных типа:

  • Iterators
    Более медленный способ доступа к хранилищу. Итератор может переместиться к указанному документу и извлечь значения атрибутов. Он работает медленнее, потому что не адаптирован для потоковой обработки. Однако в некоторых случаях демон не может обрабатывать данные в потоке, и тогда используются columnar iterators.

  • Analyzers
    Значительно более быстрый способ доступа к данным. Анализаторы принимают набор фильтров на вход и выводят номера документов (row IDs), удовлетворяющих этим фильтрам. Они работают быстро, потому что используют всю доступную метадату колонкового хранилища во время обработки данных и могут сразу распаковывать и обрабатывать множество документов за один вызов. Например, они могут полностью пропустить блоки данных (не распаковывая их), если значения min‑max блока указывают, что требуемые значения отсутствуют.
    В демоне такой тип доступа автоматически включается на основе анализа CBO, но также может быть включён вручную через подсказки индекса; такой тип подсказки называется ColumnarScan.

Заключение

В этой статье мы в общих чертах рассмотрели принципы колонкового хранилища Manticore и некоторые детали его интеграции в демон Manticore Search.

Установить Manticore Search

Установить Manticore Search