В этой статье мы обсуждаем введение в индексы Manticore.
Manticore Search поддерживает два типа индексов:
простой (также называемый оффлайн или дисковым) индекс. Данные индексируются один раз при создании, он поддерживает онлайн перестройку и онлайн обновления для не текстовых атрибутов
индекс RealTime. Похож на таблицу базы данных, онлайн обновления возможны в любое время
Кроме того, специальный индекс на основе типа RealTime, называемый percolate, может использоваться для хранения <span class="std std-ref">Запросов перколяции</span> .
В текущей версии индексы используют схему, как в обычной таблице базы данных. Схема может иметь 3 больших типа столбцов:
первый столбец всегда является беззнаковым 64-битным ненулевым числом, называемым id. В отличие от базы данных, здесь нет механизма автоинкрементации, поэтому необходимо убедиться, что идентификаторы документов уникальны
поля полнотекстового поиска - они содержат индексируемый контент. В индексе может быть несколько полнотекстовых полей. Полнотекстовый поиск может выполняться по всем полям или выборочно. В настоящее время оригинальный текст не хранится, если требуется показать его содержимое в результатах поиска, необходимо обратиться к исходному источнику, используя идентификаторы (или другой идентификатор), предоставленные результатом поиска
атрибуты - их значения хранятся и не используются в полнотекстовом сопоставлении. Вместо этого они могут использоваться для обычной фильтрации, группировки, сортировки. Они также могут использоваться в выражениях ранжирования по оценке.
Следующие типы данных могут храниться в атрибутах:
беззнаковые 32-битные и знаковые 64-битные целые числа
32-битные числа с плавающей запятой одинарной точности
временные метки UNIX
булевы значения
строки
объекты JSON
список атрибутов с несколькими значениями беззнаковых 32-битных целых чисел или знаковых 64-битных целых чисел
Manticore Search поддерживает тип индекса без хранения, называемый распределенным, который позволяет выполнять поиск по нескольким индексам. Подключенные индексы могут быть локальными или удаленными. Распределенные индексы позволяют распределять большие данные по нескольким машинам или строить высокодоступные конфигурации.
Другой тип индекса без хранения - это шаблон . Индекс шаблона не хранит данные, но может содержать настройки токенизации, как индекс со хранением. Он может использоваться для тестирования правил токенизации или для генерации выделений.
Простые индексы
За исключением числовых (включая MVA) атрибутов, остальные данные в простом индексе неизменяемы. Если вам нужно обновить/добавить новые записи, вам нужно снова выполнить перестройку. Пока индекс перестраивается, существующий индекс все еще доступен для обработки запросов. Когда новая версия готова, выполняется процесс, называемый ротация, который ставит новую версию в онлайн и отбрасывает старую.
Производительность индексации зависит от нескольких факторов:
насколько быстро источник может предоставлять данные
настройки токенизации
аппаратные ресурсы (мощность ЦП, скорость хранения)
В самом простом сценарии использования мы будем использовать один простой индекс, который мы перестраиваем время от времени.
Это подразумевает:
индекс не так свеж, как данные из источника
продолжительность индексации увеличивается с объемом данных
Если мы хотим, чтобы данные были более свежими, нам нужно сократить интервал индексации. Если индексация занимает слишком много времени, это может даже пересекаться с временем между индексациями, что является серьезной проблемой. Однако Manticore Search может выполнять поиск по нескольким индексам. Из этого родилась идея использовать вторичный индекс, который захватывает только самые последние обновления.
Этот индекс будет значительно меньше, и мы будем индексировать его чаще. Время от времени, по мере роста этого дельта индекса, мы захотим "сбросить" его.
Это можно сделать либо повторной индексацией основного индекса, либо объединением дельты с основным. Схема основного+дельта индекса подробно описана в <span class="std std-ref">Обновления дельта индекса</span> .
Поскольку движок не может глобально обеспечить уникальность идентификаторов документов, важным моментом, который необходимо учитывать, является то, может ли дельта индекс содержать обновления существующих индексированных записей в основном индексе.
Для этого есть опция, которая позволяет определить список идентификаторов документов, которые подавляются дельта индексом. Для получения дополнительной информации смотрите <span class="std std-ref">sql_query_killlist</span> .
Индексы реального времени
Индексы RealTime позволяют онлайн обновления, но обновление полнотекстовых данных и нечисловых атрибутов требует полной замены строки.
Индекс RealTime начинается пустым, и вы можете добавлять, заменять, обновлять или удалять данные так же, как для таблицы базы данных. Обновления сначала хранятся в области памяти (называемой RAM chunk), определяемой <span class="std std-ref">rt_mem_limit</span> . Когда это заполняется, оно сбрасывается как дисковый кусок - структура которого похожа на простой индекс. По мере увеличения числа дисковых кусков производительность поиска снижается, так как поиск выполняется последовательно по кускам. Чтобы преодолеть это, куски необходимо объединить в один, что делается с помощью команды <span class="std std-ref">OPTIMIZE INDEX</span> .
RAM chunk также может быть принудительно сброшен на диск с помощью FLUSH RAMCHUNK . Лучшая производительность индекса RT достигается после сброса RAM chunk и оптимизации индекса - индекс RT будет иметь все данные в одном куске и будет иметь такую же производительность, как простой индекс.
Заполнение RealTime может быть выполнено двумя способами: выполнение INSERT‑ов или конвертирование обычного индекса в RealTime. Существующие данные можно вставлять по одной записи за раз или пакетировать множество записей в один INSERT. Несколько параллельных воркеров, вставляющих данные, ускорят процесс, но будет использовано больше CPU.
Размер RAM chunk влияет на скорость обновлений, больший RAM chunk обеспечит лучшую производительность, но его размер должен подбираться в зависимости от доступной памяти. Также следует отметить, что rt_mem_limit ограничивает только размер RAM chunk. Дисковые чанки (по сути обычный индекс) будут иметь собственные требования к памяти (для загрузки словаря или атрибутов).
Содержимое RAM chunk записывается на диск во время чистого завершения работы или периодически, определяемое директивой rt_flush_period (может быть принудительно выполнено командой FLUSH RTINDEX). RT‑индекс также может использовать бинарный журнал для записи изменений. Бинарный журнал может быть воспроизведён при запуске демона для восстановления после некорректного завершения и очищается после сброса RAM chunk на диск.
Стратегия сброса binlog стратегия (аналогичная innodb_flush_log_at_trx_commit в MySQL) может влиять на производительность. Binlog также может быть отключён (установкой пустого пути к binlog), но это оставляет без защиты обновления, которые ещё не сброшены на диск.
Локальные распределённые индексы
Распределённый индекс в Manticore Search не хранит данные. Вместо этого он выступает в роли ‘master node’, отправляя требуемый запрос к другим индексам и предоставляя объединённые результаты из ответов, полученных от ‘node’ индексов. Распределённый индекс может подключаться к локальным индексам или к индексам, расположенным на других серверах. В нашем случае распределённый индекс будет выглядеть так:
index_dist {
type = distributed
local = index1
local = index2
...
}
Последний шаг для включения многопоточного поиска — определить dist_threads в секции searchd. Dist_threads указывает движку максимальное количество потоков, которое он может использовать для распределённого индекса.
Удалённые распределённые индексы и высокая доступность
index mydist {
type = distributed
agent = box1:9312:shard1
agent = box2:9312:shard2
agent = box3:9312:shard3
agent = box4:9312:shard4
}
Здесь мы распределили данные по 4 серверам, каждый обслуживает один из шардов. Если один из серверов выйдет из строя, наш распределённый индекс всё равно будет работать, но мы потеряем результаты от вышедшего из строя шарда.
index mydist {
type = distributed
agent = box1:9312|box5:9312:shard1
agent = box2:9312:|box6:9312:shard2
agent = box3:9312:|box7:9312:shard3
agent = box4:9312:|box8:9312:shard4
}
Теперь мы добавили зеркала, каждый шард находится на 2 серверах. По умолчанию мастер (экземпляр searchd с распределённым индексом) будет случайным образом выбирать одно из зеркал.
Режим выбора зеркал можно задать с помощью ha_strategy. Помимо random, ещё один простой метод — выбор по круговому расписанию (ha_strategy= roundrobin).
Более интересные стратегии — это основанные на вероятностях, взвешенных по задержке. noerrors и nodeads не только исключают зеркала с проблемами, но и мониторят время отклика и выполняют балансировку. Если зеркало отвечает медленнее (например из‑за выполняющихся на нём операций), оно будет получать меньше запросов. Когда зеркало восстановится и будет показывать лучшие времена отклика, оно получит больше запросов.