Manticore Search — открытый поисковый движок на C++, выросший из Sphinx. Он даёт полнотекстовый поиск с морфологией, ранжированием и подсветкой, запускается одним процессом и говорит на двух языках: SQL и HTTP JSON. Для сайта на WordPress, каталога товаров или форума этого часто достаточно, а ресурсов нужно в разы меньше, чем стеку с JVM. Разберём установку, схему, индексацию, запросы и диагностику.

Место Manticore среди поисковых движков

Elasticsearch и OpenSearch сильны экосистемой, но требуют JVM и заметной оперативной памяти даже на небольшом индексе. Manticore занимает десятки мегабайт и работает как обычная СУБД: подключились клиентом MySQL, выполнили SELECT, получили результат.

  • Плюсы: низкое потребление памяти, индексация в реальном времени, морфология без плагинов, подсветка, сниппеты, репликация.
  • Минусы: меньше готовых коннекторов, чем у Elasticsearch, и свой SQL-диалект.
  • Когда брать: поиск по статьям, товарам, комментариям и логам на одном-двух серверах.
  • Когда не брать: сложная аналитика, агрегации по десяткам полей, геораспределённый кластер.

Установка и запуск

Официальный репозиторий и Docker-образ равноценны. В контейнере уже есть searchd, indexer и утилиты; клиент MySQL ставится отдельно.

docker run -d --name manticore \
  -p 127.0.0.1:9306:9306 \
  -p 127.0.0.1:9308:9308 \
  -v manticore_data:/var/lib/manticore \
  manticoresearch/manticore
sudo apt install mariadb-client
mysql -h127.0.0.1 -P9306

Порт 9306 говорит по протоколу MySQL, 9308 принимает HTTP JSON, 9312 служит для межсерверного обмена. Наружу их открывать не нужно: поиск идёт через бэкенд сайта.

Схема и конфигурация

Современные версии умеют работать без файла конфигурации, но для продакшена удобнее явный manticore.conf: в нём видны каталоги, логи и лимиты памяти.

searchd {
    listen = 127.0.0.1:9306:mysql41
    listen = 127.0.0.1:9308:http
    listen = 127.0.0.1:9312
    log = /var/log/manticoresearch/searchd.log
    query_log = /var/log/manticoresearch/query.log
    pid_file = /run/manticoresearch/searchd.pid
    data_dir = /var/lib/manticore
    binlog_path = /var/lib/manticore/binlog
}

index articles {
    type = rt
    path = /var/lib/manticore/articles
    rt_field = title
    rt_field = body
    rt_attr_string = url
    rt_attr_uint = author_id
    rt_attr_timestamp = published_at
    morphology = stem_ru
    min_word_len = 2
}

Поля rt_field участвуют в полнотекстовом поиске, атрибуты rt_attr_* хранятся отдельно и годятся для фильтров, сортировки и выдачи. По атрибуту нельзя искать слова, а поле нельзя отсортировать.

Индексация

Для сайта удобнее RT-индекс: приложение вставляет документы сразу, без переиндексации корпуса. Таблицу создают и наполняют через SQL-клиент.

CREATE TABLE articles (
  title text,
  body text,
  url string,
  author_id uint,
  published_at timestamp
) morphology = 'stem_ru' min_word_len = '2';

INSERT INTO articles (id, title, body, url, author_id, published_at)
VALUES (1, 'Ядро Linux', 'Обзор изменений в подсистеме памяти', '/news/kernel', 7, 1730000000),
       (2, 'Настройка nginx', 'Балансировка и кэширование ответов', '/howto/nginx', 7, 1730100000);

Если данные лежат в MySQL или PostgreSQL, обычный индекс строят утилитой indexer и обновляют по расписанию. Для больших каталогов это дешевле, но появляется задержка между публикацией и поиском.

indexer --config /etc/manticoresearch/manticore.conf --all --rotate
indexer --config /etc/manticoresearch/manticore.conf --all --rotate --print-queries

Запросы на SQL

Синтаксис близок к обычному SQL, только вместо WHERE title LIKE используется MATCH. Ранжирование включается опцией, а подсветка — функцией.

SELECT id, title,
       WEIGHT() AS relevance,
       HIGHLIGHT({before_match='<b>', after_match='</b>'}, title, 'ядро') AS title_h
FROM articles
WHERE MATCH('@(title,body) ядро | kernel')
ORDER BY relevance DESC
LIMIT 10
OPTION ranker=bm25, field_weights=(title=10, body=1);

Оператор @(title,body) ограничивает поиск полями, черта задаёт альтернативу. После запроса выполните SHOW META: там видно время, число документов и словарь запроса.

HTTP JSON и интеграция с сайтом

Бэкенду не нужен MySQL-клиент: тот же поиск доступен через JSON. Ответ содержит hits с атрибутами и подсветкой.

curl -s -X POST http://127.0.0.1:9308/search \
  -H 'Content-Type: application/json' \
  -d '{"table":"articles","query":{"match":{"body":"ядро"}},"highlight":{"fields":["title","body"]},"limit":5}'

Для PHP, Python, Go и JavaScript есть официальные клиенты, но при небольших объёмах хватает обычного POST с JSON.

Русская морфология и релевантность

Без морфологии запрос «ядра» не найдёт «ядро». Параметр morphology = stem_ru включает стеммер Snowball, min_word_len = 2 отсекает шум, а wordforms и stopwords задают точные формы и синонимы.

  • CALL KEYWORDS('ядрами', 'articles', 1); — покажет, как запрос разложился на слова.
  • OPTION ranker=bm25 — базовая модель; веса полей задаются отдельно.
  • CALL SNIPPETS(...) — сниппет по длинному тексту без подсветки всего документа.
  • OPTIMIZE TABLE articles — склеивает чанки после массовой вставки.

Релевантность почти всегда требует настройки: заголовок весит больше тела, свежие материалы получают буст, а служебные разделы — понижение. Проверяйте изменения на реальных запросах из лога, а не на придуманных примерах.

Обновление, удаление, репликация

Полнотекстовые поля нельзя изменить через UPDATE: документ переиндексируют через REPLACE. Атрибуты обновляются на месте, что дешевле.

REPLACE INTO articles (id, title, body, url, author_id, published_at)
VALUES (2, 'Настройка nginx', 'Обновлённый текст статьи', '/howto/nginx', 7, 1730200000);

UPDATE articles SET author_id = 9 WHERE id = 2;
DELETE FROM articles WHERE id = 1;

Для отказоустойчивости несколько узлов объединяют в кластер с репликацией: CREATE CLUSTER, ALTER CLUSTER ... ADD, затем JOIN CLUSTER ... AT 'host:9312'. Одиночный сервер проще в обслуживании, но требует регулярных бэкапов; утилита manticore-backup снимает согласованную копию без остановки searchd.

Диагностика

Первое, что стоит проверить при странных результатах, — как разобрался запрос и что говорит статистика. Эти команды выполняются в том же SQL-клиенте.

SHOW STATUS;
SHOW TABLE articles STATUS;
SHOW META;
SHOW PROFILE;
CALL KEYWORDS('ядрами', 'articles', 1);

Если поиск не находит очевидное слово, проверьте морфологию, min_word_len и кодировку вставляемого текста. Если searchd не стартует, смотрите searchd.log и права на data_dir. Для обычных индексов полезна проверка indextool --check articles, а для оценки размера — SHOW TABLE articles STATUS с числом документов и дисковым объёмом.

Чек-лист внедрения

  • Порт 9306 закрыт снаружи, доступ только с бэкенда.
  • Задан binlog_path, чтобы не терять данные при перезапуске.
  • Настроены морфология, стоп-слова и веса полей.
  • Массовая вставка завершается OPTIMIZE TABLE.
  • Бэкап проверен восстановлением на тестовом узле.
  • Логи и SHOW STATUS собираются системой мониторинга.

Итог

Manticore Search закрывает типовую задачу поиска по сайту без кластера из нескольких машин и без JVM. Начните с RT-таблицы и SQL-запросов, включите русскую морфологию, проверьте выдачу на реальных запросах и только потом добавляйте репликацию. Движок не заменит аналитическую платформу, но для статей, товаров и комментариев он даёт быстрый и предсказуемый результат при скромных требованиях к железу.

Насколько публикация полезна?

Нажмите на звезду, чтобы оценить!

Средняя оценка 0 / 5. Количество оценок: 0

Оценок пока нет. Поставьте оценку первым.

Поделиться: Telegram ВКонтакте