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-запросов, включите русскую морфологию, проверьте выдачу на реальных запросах и только потом добавляйте репликацию. Движок не заменит аналитическую платформу, но для статей, товаров и комментариев он даёт быстрый и предсказуемый результат при скромных требованиях к железу.