blog-post

Поколоночное хранилище в Manticore Search

Введение

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

Хранилище атрибутов по умолчанию (row-wise)

В 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. В этом случае .spa/.spb файлы будут прочитаны в фоновом потоке, что обычно означает (но не гарантирует), что файлы атрибутов будут в памяти и запросам не придется вычитывать данные с диска. Но если система решит, что памяти не хватает, то атрибуты могут быть выгружены из памяти частично или полностью, что опять замедлит запросы. Для того, чтобы этого избежать, в access_plain_attrs / access_blob_attrs можно указать ‘mlock’. Если памяти достаточно, то атрибуты будут гарантированно в памяти и система не будет их выгружать, но если памяти не хватит, то никаких гарантий не будет.

Зачем вообще нужно поколоночное хранилище?

В ситуациях, когда объём доступной памяти достаточен для хранения всех атрибутов, традиционное хранилище ‘row-wise’ работает эффективно. Проблемы возникают, когда памяти на все атрибуты не хватает.
Действительно, технология mmap может автоматически выгружать и загружать обратно части файлов атрибутов по мере необходимости, что позволяет работать даже при ограниченных объёмах памяти. Но на практике такой подход может серьёзно снижать производительность до неприемлемых уровней. Проблема частично кроется в самой архитектуре построчного (row-wise) хранилища, которая предполагает последовательное хранение всех атрибутов одного документа, за которым следуют атрибуты следующего и так далее. Вот пример такой таблицы с данными:

.spa файл при этом имеет такую структуру:

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

Когда запрос включает группировку по определённому атрибуту, нужно извлечь только значение этого атрибута для каждого документа. Однако mmap работает таким образом, что не может вычитывать отдельные байты; вместо этого он загружает одну или несколько страниц памяти, каждая обычно размером 4 КБ. Это означает, что при попытке прочитать значение одного атрибута система зачастую загружает и значения всех хранящихся рядом атрибутов, даже если они не нужны для обработки запроса.
Другим нюансом хранилища rowwise является отсутствие сжатия данных. Поскольку работа идет через прямой доступ к памяти, весь код рассчитан на то, что мы сразу получаем готовые данные. Кроме того, данные разных атрибутов в пределах одного документа неоднородны, поэтому эффективно сжать один документ обычно не получается. А концепции блоков документов, которые можно было бы сжать, в этом хранилище нет.
Как раз для ситуаций, когда памяти не хватает для загрузки всех атрибутов, было разработано поколоночное хранилище (engine='columnar').
В рамках этого хранилища были реализовано следующее:

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

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

Что входит в columnar storage

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

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

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

  • Columnar sorter
    В архитектуре Manticore документы, найденные с помощью полнотекстового поиска, немедленно поступают в так называемый Sorter. Он может просто сортировать, а может и группировать документы, считать агрегаты, и многое другое. Но при этом доступ к атрибутам в поколоночном хранилище получается медленный, потому что из него достаются значения по одному.
    И если сам по себе запрос довольно легкий, то есть полнотекстовый поиск отрабатывает быстро или его и вовсе нет, то выборка атрибутов из хранилища по одному может существенно повлиять на производительность. Поэтому в некоторых случаях выгодно поверх стандартного Sorter использовать еще один, который сортировать ничего не будет, зато будет накапливать в себе документы и доставать поколоночные атрибуты сразу пакетами, и уже после этого отдавать документы в основной Sorter для окончательной обработки, что существенно быстрее.

  • Columnar grouper
    Grouper в Manticore отвечает за преобразование входящих документов в один или несколько ключей группировки для последующей передачи в Sorter. Основная цель columnar grouper — повышение производительности за счёт уменьшения количества виртуальных вызовов. Например, когда работает обычный grouper, он запрашивает данные у выражения, которое должно доставать данные из хранилища. Такое выражение реализовано на стороне демона, оно реализует прозрачный доступ к разным видам хранилищ атрибутов. Внутри себя оно использует columnar iterator (про них будет чуть ниже), реализованный уже на стороне библиотеки MCL, который и осуществляет непосредственный доступ к хранилищу. Специализированный же columnar grouper уже знает, что будет работать только с поколоночным хранилищем и убирает часть абстракций, работая непосредственно с columnar iterator.
    Но в случае, когда идет группировка по строкам, используется другой механизм. При добавлении строк в columnar storage автоматически считается хеш каждой строки (с учетом текущего collation) и сохраняется в виде отдельного целочисленного атрибута. И columnar grouper достает из поколоночного хранилища именно эти хеши, вместо того, чтобы вычитывать саму строку и считать от нее хеш, чтобы в дальнейшем использовать в качестве ключа группировки.

  • По аналогичной схеме, то есть через убирание columnar expression, которое через columnar iterator достает значение атрибута для текущего документа, в демоне реализованы columnar filters и columnar aggregates.

  • Про columnar storage должен знать еще и оптимизатор запросов (Cost based optimizer, CBO). CBO выбирает, по какому пути будет выполняться запрос. Например, он может заменить один из фильтров на поиск по соответствующему secondary index, убрав сам фильтр из запроса. Таким образом, secondary index будет выдавать номера документов (row id), по которым будут отрабатывать оставшиеся фильтры.
    Если же используется поколоночное хранилище, то оптимизатору приходится еще и сравнивать производительность secondary index и columnar analyzer (который осуществляет быстрый поиск по поколоночному хранилищу, подробнее про это будет чуть ниже). Причем в зависимости от самих данных и количества доступных потоков иногда работает быстрее одно, а иногда другое. Например, columnar analyzer лучше распараллеливается.

Что входит в саму MCL?

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

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

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

  • Iterators
    Более медленный способ доступа к хранилищу, Iterator может перемещаться к заданному документу и извлекать значения атрибутов. Он работает медленнее, поскольку не адаптирован для потоковой обработки данных. Однако в некоторых случаях демон не может обрабатывать данные потоково, и именно тогда используются columnar iterators.
  • Analyzers
    Это существенно более быстрый способ доступа к данным. Analyzers принимают на вход набор фильтров, а на выходе выдают номера документов, которые удовлетворяют этим фильтрам. Они работают быстро, потому что при обработке данных используют все имеющиеся метаданные поколоночного хранилища, плюс могут сразу распаковывать и обрабатывать много документов за один вызов. Например, они могут полностью пропускать блоки
    данных (не распаковывая их), если из min-max значений блока ясно, что искомые значения отсутствуют.
    В демоне этот тип доступа включается автоматически на основе анализа CBO, но его также можно включить вручную через index hints; этот тип хинтов называется ColumnarScan.

Заключение

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

Install Manticore Search

Install Manticore Search