Программирование, радиоэлектроника,
саморазвитие и частичка из моей жизни здесь...

Установка Sphinx под Linux - строим свой поисковый сервер

Sphinx Sphinx - поисковый сервер и открытым исходным кодом (Open Source Search Server) для индексации контента из баз данных и осуществления полнотекстового поиска, разработанный Андреем Аксеновым. В статье пошагово рассказано как установить и настроить поисковый сервер Sphinx на сервер под управлением операционной системы Linux (Ubuntu/Dedian). Приведены примеры, советы и ссылки которые будут полезны как администраторам так и разработчикам.

Прежде чем приступить к пошаговому мануалу по установке и настройке поискового сервера Sphinx хочу вам рассказать как я познакомился с данной системой и что послужило толчком к ее использованию.

Содержание:

Вступление

С самого начала, когда только начинал программировать на PHP и писал несложные сайты, я использовал собственный самописный поисковый движок с простой логикой. Он воспринимал одно и несколько слов в поисковой фразе и выдавал те записи в которых было обнаружено вхождение. Сначала делал с индексированием контента и кешированием индекса, потом делал полнотекстовый поиск на PHP.

Конечно же данная реализация не лишена недостатков, основной недостаток - это не достаточно релевантная поисковая выдача поскольку мой движок не учитывал морфологию слов, а искал только прямые вхождения поисковых фраз с простым анализом распределенности по статье. Писать мощный поисковый модуль со сложной логикой и оптимизациями "на стероидах" у меня не было ни времени ни особого желания.

Поиск от Яндекса и его минусы

Позже я узнал про поиск от Яндекса, который без особого труда можно прикрутить к своему сайту используя Yandex XML Search API. К моему движку был написан модуль для осуществления поиска используя Yandex XML Search API и казалось бы все просто супер но со временем меня начали не устраивать некоторые ограничения и неудобства при работе с Yandex XML Search.

Вот что мне показалось неудобным при использовании Yandex XML Search API:

  • ограничение на количество запросов в сутки, а потом еще сделали график ограничений для разного времени суток. Хотите уменьшить ограничения...отправьте СМС на номер...шутка, можно стать партнером Яндекса, для аккаунта зарегистрироваться в партнерской сети Яндекс Директ (РСЯ), связаться с поддержкой Яндекса и рассказать им зачем вам нужно большое количество запросов к Yandex XML Search API.
  • поиск производится только по контенту, который проиндексирован Яндексом. Если странички нет в индексе Яндекса - ее вы никогда не увидите в поисковой выдаче своего сайта.
  • привязка сайта к стороннему сервису, зависимость поиска от него.

Поисковый движок Sphinx

И тут я вспомнил что читал когда-то на Хабре и еще на каком-то блоге по HighLoad-проектам о мощном бесплатном поисковом движке с расширенным анализом и множеством возможностей по настройке и интеграции. Назывался этот движок Sphinx(сфинкс). Очень удачное название, хорошо запоминается и легко ищется в Google/Yandex.

Основные характеристики и возможности Sphinx:

  • индексирование:  до 10-15Мб на каждое ядро микропроцессора;
  • поиск: 150-250 поисковых запросов в секунду на каждое ядро при 1Млн документов в индексе;
  • высокая масштабируемость;
  • распределенный поиск;
  • поддержка до 32 и более полей при полнотекстовом поиске;
  • поддержка дополнительный атрибутов для каждого из индексируемых документов;
  • возможность использовать стоп слова;
  • поддержка однобайтовых (СЗ1251 и т.п) и двухбайтовых кодировок (UTF-8);
  • морфологический поиск с модулями для разных языков;
  • поддержка MySQL и PostgreSQL из коробки;
  • поддержка других ODBC совместимых баз данных;
  • кроссплатформенность;
  • готовые к использованию решения API для языков PHP, Python, Java.

Сфинкс используется на многих высоконагруженных проектах, к примеру это Хабрахабр, Викимапия и другие. Система себя зарекомендовала с наилучшей стороны, известно что существует поисковый кластер в котором проиндексировано около 3 миллиардов документов и который осуществляет более 50 миллионов поисковых запросов в сутки.

Полистав документацию и прочитав бегло несколько русских/английских статей по установке в уме быстро очертил себе четкий алгоритм действий. Со своим опытом установки и настройки Sphinx я хочу сейчас познакомить тебя, мой дорогой читатель.

Как это работает

На сервер ставится поисковый демон Sphinx, который индексирует через заданный промежуток времени статьи и контент вашего сайта - создает поисковый индекс.

На вашем сайте подключается Sphinx API, который есть для Java, PHP, Ruby (можно написать реализацию и для других языков), пишется поисковый модуль который делает несложные запросы к демону(сервису) Sphinx - searchd.

При запросе поисковой фразы с поисковой формы сайта через Sphinx API делается обращение к демону searchd, который нам возвращает ID записей с нужной сортировкой и фильтрацией. Дальше имея ID публикаций мы делаем один запрос к БД сайта и получаем всю информацию о наших статьях, остается только красиво вывести список найденных элементов.

Установка и настройка Sphinx на Linux

Предполагается что у вас уже установлена ОС Linux, на которой будет работать (или уже работает ) сайт или сервис, на котором нужно будет использовать Sphinx Search API.

Обновляем источники пакетов и ставим нужный софт:

apt-get update
apt-get install gcc make libmysqlclient15-dev libmysql++-dev g++

Идем по ссылке http://sphinxsearch.com/downloads/release/ и качаем архив с исходными кодами для последнего стабильного релиза ( Get Source tarball tar.gz ). Потом распаковываем архив и переходим в разархивированную папку. Вот к примеру я качал 2.1.3-release:

cd /tmp/
wget -c "http://sphinxsearch.com/files/sphinx-2.1.3-release.tar.gz" && ls
tar -xf sphinx-2.1.3-release.tar.gz
cd sphinx-2.1.3-release

Теперь компилируем наш Сфинкс с поддержкой MySQL (есть поддержка и других БД, смотри док.), поскольку весь полезный контент сайта хранится в БД MySQL:

./configure --with-mysql --prefix=/usr/local/sphinx 
make
make install

Копируем шаблон файла с настройками и редактируем его содержимое под наши нужды:

cp /usr/local/sphinx/etc/sphinx.conf.dist /usr/local/sphinx/etc/sphinx.conf
nano /usr/local/sphinx/etc/sphinx.conf

В конфигурационном файле достаточно много настроек и все они заключены в секции где для каждого источника индексируемых данных описываются свои специфические опции и данные для доступа.

Приводить полное описание всех настроек я не буду, в шаблонном файле все хорошо документировано, а также в официальной документации (смотри ссылки в конце статьи) все понятно изложено. Чтобы все изначально прояснить приведу здесь текст своего конфигурационного файла с подробными комментариями:

# Настройки источника "mysqlbasename" с которого будем брать контент
source mysqlbasename
{
 type = mysql # тип источника, в данном случае БД MySQL
 sql_host = localhost # Хост MySQL
 sql_user = dbusername # Пользователь MySQL
 sql_pass = dbuserpassword # Пароль MySQL
 sql_db = mysqlbasename # Название БД в MySQL
 sql_port = 3306 # Порт сервера MySQL
 sql_query_pre = SET NAMES CP1251 # Кодировка индексируемой таблицы
# Запрос которым делаем выборку всех записей, которые должны быть проиндексированы
 sql_query =  SELECT title, full_text  FROM posts WHERE publicated=1
# Используется только в CLI (при обращении через командную строку), можно оставить пустым
 sql_query_info = SELECT * FROM posts WHERE id=$id
}

# настройки построения индекса с использованием источника "mysqlbasename"
index mysqlbasename
{
 source = mysqlbasename # Имя источника данных
# Путь по которому будут сохранены файлы индекса, если индекс большой то можно 
#  перенести на отдельный раздел или жесткий диск.
 path = /usr/local/sphinx/var/data/mysqlbasename
# Настройка хранения значений атрибутов документов, extern - хранить раздельно по ID.
 docinfo = extern 
 mlock = 0 # Бдокировка памяти для кеширования данных, 0 - нет.
 morphology = stem_en, stem_ru, soundex # набор морфологическиз препроцессоров для обработки.
 min_word_len = 3 # минисмальная длина слова для индексации
 charset_type = sbcs #  Single Byte Character Set - кодировка, в данном случае для CP1251.
 html_strip = 1 # Чистить или нет HTML (1 - да)
}

# настройки индексатора
indexer
{
 mem_limit = 32M # лимит памяти для работы индексатора
}

# настройки поискового демона (SearchDaemon, searchD)
searchd
{
 listen = 9312 # порт для работы через API
 listen = 9306:mysql41 # порт для комуникаций с MySQL
 log = /usr/local/sphinx/var/log/searchd.log # логи демона
 query_log = /usr/local/sphinx/var/log/query.log # логи поисковых запросов
 read_timeout = 5 # таймаут чтения в секундах
 client_timeout = 300 # таймаут поддержания соединения, между запросами в секундах
 max_children = 30 # Максимально допустимое количество порождаемых процессов
 pid_file = /usr/local/sphinx/var/log/searchd.pid # PID(ProcessID) файл родительского процесса
 max_matches = 1000 # Максимальное количество полученных результатов
 seamless_rotate = 1 # предотвращает паралич search при работе с большим количеством кешируемых данных
 preopen_indexes = 0 # Откривать все индексы при запуске, 0 - нет
 unlink_old = 1 # чистка старых индексов, 1 - да
 mva_updates_pool = 1M # Резервируемый размер пула в ОЗУ для обновлений
 max_packet_size = 8M # Максимальный размер сетевого пакета для обмена данными
 max_filters = 256 # Максимальное количество фильтров что можно использовать на один запрос
 max_filter_values = 4096 # Макс. количество значений для одного фильтра
}
# --eof--

Запускаем индексацию наших данных:

/usr/local/sphinx/bin/indexer --all --verbose

В случае успешного индексирования получим примерно вот такие строки:

Sphinx 2.1.3-dev (r4319)
Copyright (c) 2001-2013, Andrew Aksyonoff
Copyright (c) 2008-2013, Sphinx Technologies Inc (http://sphinxsearch.com)

using config file '/usr/local/sphinx/etc/sphinx.conf'...
indexing index 'mysqlbasename'...
WARNING: Attribute count is 0: switching to none docinfo
collected 518 docs, 3.2 MB
sorted 0.4 Mhits, 100.0% done
total 518 docs, 3195655 bytes
total 0.637 sec, 5013995 bytes/sec, 812.74 docs/sec
total 2 reads, 0.000 sec, 745.7 kb/call avg, 0.3 msec/call avg
total 8 writes, 0.001 sec, 390.4 kb/call avg, 0.2 msec/call avg

Запускаем поискового демона:

/usr/local/sphinx/bin/searchd

В случае успешного запуска должны увидеть примерно вот такой вывод:

Sphinx 2.1.3-dev (r4319)
Copyright (c) 2001-2013, Andrew Aksyonoff
Copyright (c) 2008-2013, Sphinx Technologies Inc (http://sphinxsearch.com)

using config file '/usr/local/sphinx/etc/sphinx.conf'...
listening on all interfaces, port=9312
listening on all interfaces, port=9306
precaching index 'mysqlbasename'
precached 1 indexes in 0.000 sec

Делаем тестовый поисковый запрос, где YOUR_QUERY_STRING - ваша поисковая фраза:

/usr/local/sphinx/bin/search --config /usr/local/sphinx/etc/sphinx.conf YOUR_QUERY_STRING

После запуска будет выведена длинная простыня записей что отвечают критериям поиска.

Проверяем работу поиска с использованием PHP API. Внимание: PHP должен быть установлен на сервере! "YOUR_QUERY_STRING" - ваш поисковый запрос

cd /tmp/sphinx-2.1.3-release/api/
php test.php YOUR_QUERY_STRING

Надеюсь у вас все получилось.

Скрипт автозапуска searchd для init.d

Теперь нужно сделать так чтобы демон searchd стартовал при загрузке/перезагрузке операционной системы. Ниже приведен пример создания и настройки скрипта для Debian/Ubuntu GNU/Linux без использования systemd.

Откроем для редактирования новый скрипт инициализации в папке "/etc/init.d":

nano /etc/init.d/searchd

Копируем и вставляем следующий текст (CTRL+C, CTRL+V) в скрипт /etc/init.d/searchd :

#!/bin/bash

#!/bin/sh
### BEGIN INIT INFO
# Provides:          searchd
# Required-Start:    $all
# Required-Stop:     $all
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# X-Interactive:     true
# Short-Description: Stop/Start SphinxSearch Daemon
### END INIT INFO

case "${1:-''}" in

'start')
/usr/local/sphinx/bin/searchd
;;

'stop')
/usr/local/sphinx/bin/searchd --stop
;;

'restart')
/usr/local/sphinx/bin/searchd --stop
sleep 1
/usr/local/sphinx/bin/searchd
;;

*)
echo "Usage: $SELF start|stop|restart"
exit 1
;;

esac

Установим права на скрипт запуска и зарегистрируем его в автозапуске:

chmod +x /etc/init.d/searchd
update-rc.d searchd defaults

Для надежности можете перезагрузить свой сервер и проверить запустился ли демон searchd:

reboot
ps ax | grep searchd

Скрипт автозапуска searchd для systemd

В более новых версиях GNU/Linux уже присутствует такой мощный набор управления системой как systemd. Чтобы добавить наш демон searchd в автозапуск системы с использованием systemd нужно проделать несколько несложных операций.

Откроем для редактирования новый файл будущего скрипта для управления запуском searchd:

nano /usr/local/sbin/systemd-searchd.sh

Копируем в редактор следующий текст скрипта:

#!/bin/sh
# Sphinx init script for searchd daemon.

case $1 in
    start)
        /usr/local/sphinx/bin/searchd
        echo "Sphinx searchd started."
        ;;
    stop)
        /usr/local/sphinx/bin/searchd --stop
        echo "Sphinx searchd stoped."
        ;;
    restart)
        /usr/local/sphinx/bin/searchd --stop
        sleep 1
        /usr/local/sphinx/bin/searchd
        echo "Sphinx searchd restart complete."
        ;;
    *)
        echo "Usage: systemctl {start|stop|restart} searchd.service"
        exit 1
esac
 
exit 0

Устанавливаем права на запуск скрипта:

chmod +x /usr/local/sbin/systemd-searchd.sh

Создаем конфигурационный файл для нашее службы (демона):

nano /etc/systemd/system/searchd.service

Копируем в редактор следующее содержимое:

[Unit]
Description=Sphinx searchd daemon
After=network.target
 
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/systemd-searchd.sh start
ExecStop=/usr/local/sbin/systemd-searchd.sh stop
ExecReload=/usr/local/sbin/systemd-searchd.sh restart
RemainAfterExit=yes
 
[Install]
WantedBy=multi-user.target

Все должно работать!

Настройка автоматической переиндексации

Добавляем в системный планировщик заданий (крон, crontab) запуск индексатора через каждые сутки:

nano /etc/crontab

Добавляем в конец содержимого файла следующую строчку:

0  0    * * *   root    /usr/local/sphinx/bin/indexer --rotate --all --config /usr/local/sphinx/etc/sphinx.conf

Все готово! Вы установили и настроили поисковый сервер .

Защита служебных портов

После настройки и запуска поискового сервера Sphinx важно помнить что его демн открывает порты 9312, 9306 (указанные в настройках) и делает их доступными на всех интерфейсах. Настоятельно рекомендую закрыть их от внешнего мира при помощи файрвола на внешнем интерфейсе (интернет). Вот два простых правила для IPTABLES:

iptables -A INPUT -p tcp -i eth0 --dport 9306 -j DROP
iptables -A INPUT -p tcp -i eth0 --dport 9312 -j DROP

Где eth0 - имя сетевого интерфейса, который смотрит в интернет.

Пример взаимодействия со Sphinx на PHP

Итак, сервер настроен все проиндексировано, работоспособность проверена. Качаем себе Sphinx API класс для работы в PHP:

https://code.google.com/p/sphinxsearch/source/browse/trunk/api/sphinxapi.php?r=2014

Помещаем скрипт sphinxapi.php в свой проект и подключаем этот скрипт при помощи include/require.

Вот простая реализация подключения и выборки при помощи PHP(код для примера):

$cl = new SphinxClient();
$page = 3; // our results page number
$items_per_page = 10; // show 10 results per page
$index_name = 'mysqlbasename '; // Index name to fetch from
$request = 'купить носки';

$cl->SetServer( "localhost", 9312 ); // server, port
$cl->SetConnectTimeout( 1 ); // max connect timeout
$cl->SetMaxQueryTime(1000); // max query timeout
$cl->SetMatchMode( SPH_MATCH_ALL  ); // ALL, ANY, PHRASE, EXTENDED...
$cl->SetLimits(intval($page*$items_per_page), intval($items_per_page), 1000);
$result = $cl->Query( $request, $index_name );
if ( $result === false ) {
 if( $cl->GetLastWarning() ) { // check for warnings
  echo 'WARNING: '.$cl->GetLastWarning();
  exit;
 }
 // no warnings found, result=FALSE
 exit('Cannot connect etc.');
}

echo 'Total results found: '.$result['total'];
// Compose comma separated string with posts IDs
foreach ($result['matches'] AS $key => $row)
		$ids_arr[] = intval($key);
	$ids_csv = join(',', $ids_arr);
// fetch posts from DB
$mysql_result = mysql_query(SELECT id, title, text FROM `posts` WHERE id IN (".$ids_csv.") AND publicated=1);
while ( $mysql_object = mysql_fetch_object($mysql_result) ) {
...
}
...

Заключение

Ниже привожу полезные ссылки по поисковому серверу Sphinx. Этой информации с лихвой достаточно чтобы во всем разобраться и  настроить под себя.

Полезные ссылки:

UPD: последняя версия Sphinx с открытым исходным кодом - 2.3.2-beta. В последующих версиях код автором теперь не предоставляется, есть только готовые сборки с бинарными файлами.

Рекомендую обратить свое внимание на проект Manticore Search, который базируется на форке последней Open Source версии движка Sphinx и имеет множество улучшений и фич!

Если статья оказалась полезной - помочь проекту можно тут: 👍 ПОМОЩЬ, 🎁 DONATE
1 10666 Linux
Комментарии к публикации (6):
fregat #1fregat
20 Ноябрь 2015 08:56

Доброго времени суток, Total results found показывает что 24, $result['matches'] отсутствует в массиве $result. Как проблему побороть?

0
ph0en1x #2ph0en1x
20 Ноябрь 2015 11:21

Здравствуйте. Статья писалась на момент выхода версии sphinx-2.1.3-release, с этой версией все проверено и прекрасно работает. Если же у вас установлена данная версия то следует пересмотреть настройки сервера Sphinx, проверить все ли хорошо с кодировкой для вашего текста и с кодировкой по умолчанию для версии PHP, которая работает на вашем сервере приложений.

Если у вас другая, более свежая версия сервера SphinxSearch (например 2.2.10-release) то здесь нужно использовать более новый SphinxSearch PHP API - https://code.google.com/p/sphinxsearch/source/browse/trunk/api/sphinxapi.php
Также в данном случае более целесообразно использовать новый инструмент для запросов к серверу SphinxQL Query Builder for PHP, который является более производительным и позволяет задавать условия выборки результатов используя синтаксис подобный до языка SQL - http://sphinxsearch.com/blog/2014/10/20/sphinxql-query-builder-for-php/

0
Игорь #3Игорь
14 Июнь 2016 12:12

добрый день,
я бы заказал у вас установку на сервер сфинкса.
бд с 3 млн товаров.
ответьте, если есть желание.

+1
ph0en1x #4ph0en1x
05 Апрель 2017 18:38

Статья немножко переработана, добавлено содержание с навигацией, а также пример создания скрипта автозапуска службы searchd под systemd для GNU/Linux.

0
oO #5oO
20 Июнь 2017 01:52

На счет - Скрипт автозапуска searchd для systemd
Всего то нужно включить юнит в автозапуск

sudo systemctl enable sphinxsearch.service

Debian 8.x Jessie - Sphinx 2.2.11

0
ph0en1x #6ph0en1x
20 Июнь 2017 12:40

Верно, в новых версиях Sphinx все что нужно для автозапуска уже идет в комплекте если устанавливать поисковый движок из пакетов (*.deb к примеру). При сборке Сфинкса из исходных кодов, скрипты автозапуска придется настраивать вручную.

+1