
Sphinx – это поисковый сервер / движок. Sphinx предназначен для практически всех поисковых задач. Он может искать по тексту по релевантности, умеет искать склоняемые слова («котят» – «котята»), масштабируется горизонтально и имеет удобный интерфейс. Мне недавно пришлось изучить данный комбайн для одной весьма специфической задачи.
Постановка задачи

На самом деле, в проекте много сложных для объяснения вещей (не совсем полное техническое задание занимает под 500 страниц 12-м шрифтом), но в сильно упрощённом виде всё выглядит следующим образом:
Имеется Classified система - система с объявлениями различных категорий. У всех объявлений одной категории имеется определённый набор полей различных типов. Набор полей изменяется из административной панели.
Например, для объявлений раздела “Авто - Легковые авто” у нас имеется следующий набор полей:
- Марка
- Модель
- Тип кузова
- ...
Для объявлений раздела “Недвижимость - Квартиры - Продажа” поля такие:
- Область
- Город
- Район
- ...
Необходимо реализовать все возможные операции над объявлениями и, самое главное, их поиск.
Почему именно Sphinx?

Я много, много думал и нашел всего 2 варианта хранения значения полей - сериализованные данные (JSON / XML / ...) в общей таблице объявлений или “кучка таблиц”. На первых порах, когда мы затевали этот проект, я не нашел ни одного варианта полноценного поиска по сериализованным данным (возможно, я просто плохо искал) и нам пришлось реализовывать “костыльный” вариант №2 - создавать по таблице значений для каждой категории, но с появлением в Sphinx (с версии 2.2.x) возможности поиска по JSON столбцам решено было перевести поиск на Sphinx.
Кроме того, у Sphinx есть определённые преимущества:
- Sphinx быстрый и стабильный.
- Sphinx лёгкий в освоении. Официально рекомендуемым протоколом работы со Sphinx есть SphinxQL - протокол на базе SQL. Полнотекстовый поиск, поиск по JSON и даже манипуляции с RealTime индексами не составляют особого труда тем, кто уже знает SQL.
- Sphinx масштабируемый. Если в процессе роста проекта, загруженность поискового сервера будет подходить к пиковой - вы спокойно можете поднять поисковый кластер из нескольких машин и распределить нагрузку.
- Sphinx популярный. Следовательно, в сети полно документации, проект постоянно развивается и его уверенно можно использовать в коммерческих продуктах.
- Sphinx кроссплатформенный. У нас машины разработчиков работают, в основном, на Windows, а на сервере установлен Debian. Однако подобный “зоопарк” по зубам нашему монстру.
Итак, решено - будем использовать Sphinx. Так с чего же начать?
Установка Sphinx на Debian
В репозиториях Debian версии Sphinx обновляются достаточно редко, поэтому нам придётся устанавливать пакет вручную.
Шаг 1. Качаем отсюдава (http://sphinxsearch.com/downloads/release/). необходимый нам пакет.
Шаг 2. Устанавливаем библиотеки, необходимые для Sphinx:
$ sudo apt-get install mysql-client unixodbc libpq5
Шаг 3. Устанавливаем Sphinx из скачанного .deb пакета:
$ sudo dpkg -i sphinxsearch_2.3.1-beta-0ubuntu11~trusty_amd64.deb
Всё, наш монстр установлен. Теперь, прежде чем его запустить, нам необходимо сконфигурировать поисковые индексы и метод получения исходных данных.
Настройка Sphinx
Все настройи хранятся в файле sphinx.conf.
Исходная таблица
Я сильно упростил таблицу
Получение исходных данных
Конфигурация обычно начинается с секций source. Эти секции описывают данные, которые мы будем тянуть с SQL сервера.
Тут мы описываем тип подключения (mysql), параметры подключения, SQL запросы (самый главный - sql_query - для выборки данных).
Поскольку у нас нет необходимости искать по удалённым или неактуальным объявлениям - мы их попросту не будем вносить в индекс, чем убъём двух зайцев сразу - не будем перегружать индекс лишними данными и упростим поисковый запрос.
source src_classified_offers{
type = mysql
sql_host = 127.0.0.1
sql_user = *******
sql_pass = *******
sql_db = classified_test
sql_port = 3306 # optional, default is 3306
#pre-query
sql_query_pre = SET NAMES utf8
# main document fetch query
sql_query = \
SELECT `id`,`catid`,`vals`, \
`price`,`currency`, \
`name`,`description`, \
UNIX_TIMESTAMP(`published_from`) as published_from_ts \
FROM classified_ads \
WHERE ((`published`=1)AND(`id`>=$start)AND(`id`<=$end))
sql_query_range = SELECT MIN(id),MAX(id) FROM classified_ads
sql_range_step = 1000
sql_attr_uint = catid
sql_attr_json = vals
sql_attr_float = price
sql_attr_uint = currency
sql_attr_timestamp = published_from_ts
sql_ranged_throttle = 0
}
Параметры sql_query_range, sql_range_step и sql_ranged_throttle описывают “выборку частями” из нашей таблицы. Поскольку таблица будет большая, теоретически (в процессе выборки большого количества данных) нас может выбить по таймауту. Запросы частями решают данную проблему.
Как видите, в конце секции source, идёт описание типов некоторых полей. Например, sql_attr_json - это значения наших полей. Все неописанные поля добавляются в полнотекстовый индекс.
Выбор типа индекса
Далее нам необходимо создать секцию индекса. В Sphinx есть 2 типа индекса - обычный и realtime. Давайте выберем обычный индекс. Он проще в обслуживании и более производительный (минус - обновляется с запаздыванием).
index classified_offers{
source = src_classified_offers
path = /data/sphinx/classified_offers/
docinfo = extern
dict = keywords
mlock = 0
morphology = stem_enru
min_word_len = 1
html_strip = 0
}
Стоит отметить, что директория path должна существовать на момент запуска демона.
Запуск Sphinx и поиск
Перед стартом сервиса нам необходимо проиндексировать данные. При изменении структуры (добавили столбец в индекс или поменялся тип) необходимо вначале остановить сервис, а затем запускать полное индексирование командой:
$ sudo service sphinxsearch stop
$ indexer --all
Затем запускаем демон:
$ sudo service sphinxsearch start
Если ничего не изменилось - просто вызываем переиндексацию данных без остановки сервиса. Данную команду следует запихнуть в cron для поддержания валидных данных:
$ indexer --all --rotate
Подключение к серверу Sphinx в PHP (мы используем протокол SphinxQL) ничем не отличается от SQL. Вот только порт придётся использовать нестандартный (обычно 9306).
Для поиска по JSON указываем поле через точку. Например:
SELECT values.fuel_type from classified_ads where (values.fuel_type=4);
Для полнотекстового поиска используется ключевое слово MATCH:
SELECT * FROM classified_ads WHERE MATCH('my document');
Вот и всего-то.
Комментарии
Пока еще никто не комментировал эту запись. Вы можете быть первым!
Adding comments is temporarily disabled for unregistered users.