Введение
В этой статье мы рассмотрим назначение колонкового хранения Manticore, чем оно отличается от построчного хранения и в каких случаях имеет смысл его использовать. Мы также познакомимся с основной структурой формата хранения и особенностями его интеграции в процесс обработки запросов поиска.
Хранение атрибутов по умолчанию (построчное)
В Manticore существуют два отдельных сущности: полнотекстовые поля, которые поддерживают только полнотекстовые запросы, и атрибуты различных типов, которые могут использоваться для группировки, сортировки и фильтрации. Хранилище по умолчанию (engine='rowwise'
) сохраняет все атрибуты всех документов в памяти.
Чтобы загрузить атрибуты в память, используется mmap, что можно настроить через параметры access_plain_attrs
и access_blob_attrs
. Первый параметр отвечает за загрузку файлов .spa
, которые содержат все атрибуты фиксированной длины (целое число, bigint, float и т.д.). Второй параметр предназначен для загрузки файлов .spb
, которые содержат атрибуты переменной длины (строка, 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 КБ. Это означает, что при попытке прочитать значение одного атрибута система часто загружает значения всех соседних атрибутов, даже если они не нужны для обработки запроса.
Еще одной особенностью построчного хранения является отсутствие сжатия данных. Поскольку оно работает через прямой доступ к памяти, код предполагает, что готовые данные немедленно доступны. Кроме того, данные различных атрибутов в одном документе являются гетерогенными, что затрудняет эффективное сжатие одного документа. Более того, в этом формате хранения нет концепции блоков документов, которые можно было бы сжимать.
Колонковое хранение (engine='columnar'
) было разработано именно для ситуаций, когда недостаточно памяти для загрузки всех атрибутов. Этот формат хранения предлагает следующие преимущества:
- Данные для каждого атрибута хранятся отдельно и могут читаться без влияния на другие атрибуты.
- Поскольку данные внутри одного атрибута обычно однородны, можно применить сжатие.
- Значения атрибутов могут быть разбиты на блоки нескольких документов для сжатия.
- После распаковки блоков могут применяться оптимизации для потоковой обработки.
- Очень небольшой объем метаданных хранится в RAM, все остальное на диске.
- Для быстрого доступа к “горячим” данным, вместо загрузки страниц в память через mmap, используется кеш файловой системы.
Схематически структура данных в колонковом хранении может быть представлена следующим образом:
Колонковое хранение поставляется в отдельной библиотеке под названием MCL (Manticore Columnar Library). Эта библиотека отвечает за создание хранилища, упаковку и чтение данных.
Кроме того, в сам демон поиска было добавлено значительное количество кода для обработки атрибутов с учетом специфики колонкового хранения.
Изначально демон реализовывал rowwise
хранилище, которое нацелено на случайный доступ к данным. Можно было просто заменить доступ к данным в памяти на колонковое хранение, но сделать это, не учитывая, что колонковое хранение предназначено для потоковой обработки и хранит данные в сжатых блоках, серьезно ухудшило бы производительность.
Вот несколько примеров того, что было добавлено в демон для работы с колонковым хранением:
Колонковый сортировщик
В архитектуре Manticore документы, найденные с помощью полнотекстового поиска, немедленно отправляются в так называемый Сортировщик. Сортировщик может просто сортировать, или он может группировать документы, вычислять агрегации и многое другое. Тем не менее, доступ к атрибутам в колонковом хранении медленный, поскольку значения извлекаются по одному.
Если запрос относительно легкий — то есть полнотекстовый поиск быстрый или вовсе отсутствует — извлечение атрибутов из хранилища по одному за раз может значительно повлиять на производительность. Поэтому в некоторых случаях полезно использовать дополнительный сортировщик поверх стандартного Сортировщика. Этот дополнительный сортировщик не сортирует, а накапливает документы и извлекает колонные атрибуты партиями, прежде чем передать документы основному Сортировщику для окончательной обработки, что значительно быстрее.Колонный группировщик
Группировщик в Manticore отвечает за преобразование входящих документов в один или несколько ключей группировки для последующей передачи в Сортировщик. Основная цель колонного группировщика — улучшить производительность за счет снижения количества виртуальных вызовов . Например, когда работает обычный группировщик, он запрашивает данные из выражения, которое извлекает данные из хранилища. Это выражение реализуется на стороне демона и предоставляет прозрачный доступ к различным типам хранения атрибутов. Внутренне оно использует колонный итератор (обсуждаемый ниже), реализованный на стороне библиотеки MCL, который напрямую обращается к хранилищу. Специализированный колонный группировщик, однако, знает, что будет работать только с колонным хранилищем, и устраняет некоторые абстракции, работая непосредственно с колонным итератором.
При группировке по строкам используется другой механизм. Когда строки добавляются в колонное хранилище, автоматически рассчитывается и сохраняется хэш каждой строки (с учетом текущей коллации ) в качестве отдельного целочисленного атрибута. Колонный группировщик извлекает эти хэши из колонного хранилища вместо того, чтобы читать саму строку и вычислять хэш для использования в качестве ключа группировки.Аналогично, убирая колонное выражение, которое через колонный итератор извлекает значение атрибута для текущего документа, колонные фильтры и колонные агрегаты реализованы в демоне.
Оптимизатор запросов ( Оптимизатор на основе стоимости , CBO) также должен быть в курсе колонного хранилища. CBO определяет путь выполнения запроса. Например, он может заменить один из фильтров на поиск по соответствующему вторичному индексу, убирая фильтр из запроса. В результате вторичный индекс вернет номера документов (ID строки), по которым будут работать остальные фильтры.
Когда используется колонное хранилище, оптимизатор должен сравнить производительность вторичного индекса и колонного анализатора (который выполняет быстрый поиск в колонном хранилище, подробнее об этом ниже). В зависимости от данных и количества доступных потоков иногда один из методов работает быстрее другого. Например, колонный анализатор лучше параллелит.
Что входит в MCL?
Среди компонентов, которые непосредственно являются частью библиотеки MCL, можно выделить две основные группы:
1.Строитель
Ответственный за получение сырых данных от демона и построение колонного хранилища.
2.Аксессоры
Ответственные за доступ к данным. Существует два основных типа:
Итераторы
Более медленный метод доступа к хранилищу. Итератор может перейти к определенному документу и извлечь значения атрибутов. Он работает медленнее, потому что не адаптирован для потоковой обработки. Однако в некоторых случаях демон не может обрабатывать данные в потоке, и тогда используются колонные итераторы.Анализаторы
Значительно более быстрый метод доступа к данным. Анализаторы принимают набор фильтров на вход и выводят номера документов (ID строк), которые удовлетворяют этим фильтрам. Они работают быстро, потому что используют всю доступную метаданные колонного хранилища во время обработки данных и могут сразу распаковывать и обрабатывать множество документов за один вызов. Например, они могут полностью пропустить блоки данных (без их распаковки), если минимальные и максимальные значения блока указывают на то, что желаемые значения отсутствуют.
В демоне этот тип доступа автоматически включается на основе анализа CBO, но также может быть вручную включен через подсказки индекса; этот тип подсказки называется ColumnarScan.
Заключение
В этой статье мы в общих чертах охватили принципы Колонного Хранилища Manticore и некоторые детали его интеграции в демон поиска Manticore.