За чистый и ясный код!

Статьи на тему программирования под веб, используя PHP, MySQL, Jquery и многое другое

Работа с realtime индексами (RT) в поисковом движке Sphinx

Сентябрь18
Добро пожаловать на форум про php, интерсные статьи, задачи и обзоры.

Кто не знает что такое Sphinx

Сфинк — это бесплатный полнотекстовый поисковый движок. С быстрой индексацией документов, стоп-словами, умеет интегрироваться с самыми известными БД Mysql, MsSql, Oracle и тд, также имеет АПИ с популярными языками программирования PHP, Perl, Java и тд. Также имеет поддержку SQL синтаксиса. Более подробнее тут.

Версии поисковика

На сегодня последняя версия 2.0.1, не смотрите что она бета, на этом проекте беты очень стабильны.

Также есть версия 1.10, из неё родилась версия выше, а эту отправили в утиль архив.

К стабильной версии относится 0.9.9

Я к сожалению не знаток всех этих версий в полной мере, и не могу что-то особо сильно утверждать, поэтому дам сугубо поверхностную оценку различий версий.

Начиная с версии 1.10 появились relatime индексы. Это индексы которые изменяются «на лету», т.е. не нужно делать промежуточные индексы и через какое-то время сливать их с главным индексом.

Во всех версия появилась возможность создавать таблицу в MySQL 5.x с движком SPHINX (Sphinx storage engine), т.е. появилась возможность обращаться к данным поискового индекса через SQL запросы к БД MySQl, более подробно тут

Также во всех версиях доступен API.

Более подробно обращайтесь к разным блогам, документации поисково движка.

Какие бывают индексы

Есть обычный индекс с ним неотъемлемо связан дельта индекс, который затем сливается с индексом (merge).
Чем мне этот тип индекса не понравился. Например, у вас есть БД с огромным кол-во записей (новостная система) и каждый день добавляется по 500-1000 новостей, вот тут необходимо производить множество операций, создавать описание главного индекса, также создавать описание вспомогательного индекса, который будет потом добавлять в главный индекс, т.е. небольшое кол-во данных которые пришли за указанное вами время. Затем необходимо запускать процедуру объединения дельта индекса и главного индекса. Получается ситуация, что добавили новость в БД, а она появится в поисковом индексе только когда произойдет объединение индексов, время конечно задаёт программер, но все таки это какой-то промежуток + плюс куча операций.

С версии 1.10 появились «realtime» (RT) индексы, они работают только с SphinxQL, т.е. заполнение данных в индекс, удаление, изменение происходит по средству SQL запросов (очень хорошо описано тут). Самая ВАЖНАЯ особенность этого типа индекса, то что данные попадают в тот же момент, когда они попадают в БД (вам нужно просто создать два запроса, один в БД, а другой в индекс Сфинкса). Этот индекс мне очень понравился и я его с успехом применил в одном проекте. Вот как с ним работать я бы и хотел поведать в этой статье.

Работа с RealTime Index

Лучше всего показывать на реальном примере, дабы сразу видеть результаты.

Создаем БД MySQL и в ней таблицу новостей

CREATE TABLE news (
	id INT(11) NOT NULL AUTO_INCREMENT,
	date TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
	title VARCHAR(500) NULL DEFAULT NULL COLLATE 'utf8_unicode_ci',
	descr VARCHAR(2000) NULL DEFAULT NULL COLLATE 'utf8_unicode_ci',
	keywords VARCHAR(200) NULL DEFAULT NULL COLLATE 'utf8_unicode_ci',
	text VARCHAR(16000) NULL DEFAULT NULL COLLATE 'utf8_unicode_ci',
	`show` TINYINT(1) NOT NULL DEFAULT '0',
	PRIMARY KEY (id)
)
COMMENT='Новости'
COLLATE='utf8_unicode_ci'
ENGINE=MyISAM
AUTO_INCREMENT=1;

Далее создадим конфиг для поисковика для индекса новостей.

index rt_news
{
	type 					= rt
	path 					= f:/sphinx/data/rt_news
	rt_field				= title
	rt_attr_timestamp		= date
	rt_field				= descr
	rt_field				= keywords
	rt_field				= text
	rt_attr_uint			= view

	rt_attr_string			= title
	rt_attr_string			= descr
	rt_attr_string			= keywords
	rt_attr_string			= text

	docinfo					= extern
	morphology				= stem_enru
	min_word_len			= 1
	html_strip				= 1
	charset_type			= utf-8
	enable_star				= 1
	rt_mem_limit			= 256M
}

searchd
{
	listen			= 127.0.0.1:9306:mysql41
	log				= f:/sphinx/searchd.log
	query_log		= f:/sphinx/query.log
	pid_file		= f:/sphinx/searchd.pid
	read_timeout	= 5
	max_children	= 30
	max_matches		= 1000
	workers			= threads # for RT to work
}

В конфиге есть две обязательные секциии index нужна для описания индекса и searchd тут описываются настройки самого демона (службы) поисковика.

Разберем.

После названия секции index идет название индекса (произвольное [a-zA-Z0-9_] возможно есть еще знаки, но думаю этого достаточно для описания), далее идет описание самого индекса, указывается тип индекса, путь где будет хранится индекс, лучше пусть путь будет на латинице, т.к. используется кодировка UTF-8 и под виндой русское название будет в кракозябрах. Далее идет описание полей и их тип подробнее о различных типах тут, далее идут управляющие команды индексом, какую морфологию использовать, сколько памяти отводить, какая кодировка и тд, есть еще куча настроек, но она специфична и зависит уже от поставленных задач, обращайтесь к документации.

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

Имея конфиг запустим поисковик, буду показывать все в командной строки для наглядности

F:\sphinx\bin>searchd.exe --config f:\Sphinx\sphinx.conf
Sphinx 2.0.1-beta (r2792)
Copyright (c) 2001-2011, Andrew Aksyonoff
Copyright (c) 2008-2011, Sphinx Technologies Inc (http://sphinxsearch.com)

using config file 'f:\Sphinx\sphinx.conf'...
listening on 127.0.0.1:9306
precaching index 'rt_news'
precached 1 indexes in 0.011 sec

Служба запущена и показывает что слушает Mysql на порту 9306 и создан один индекс с названием rt_news.

Добавление данных в БД и индекс

Пришло время заполнить БД и индекс данными. Подключаемя к поисковику через mysql на порту 9306

F:\Zend\MySQL 5.1.52\bin>mysql -P9306
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 1
Server version: 2.0.1-beta (r2792)

Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
This software comes with ABSOLUTELY NO WARRANTY. This is free software,
and you are welcome to modify and redistribute it under the GPL v2 license

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>

Запишем просто одну запись в БД (в примере только запрос, как его исполнять решать вам) и одну запись в поисковый индекс.

# В БД
INSERT INTO `news` (`id`, `date`, `title`, `descr`, `keywords`, `text`, `show`) VALUES (1, '2011-09-17 12:02:49', 'Название новости', 'Описание новости', 'Ключевые слова', 'Текст новости ', 1);
# В поисковый индекс
F:\Zend\MySQL 5.1.52\bin>mysql -P9306
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 1
Server version: 2.0.1-beta (r2792)

Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
This software comes with ABSOLUTELY NO WARRANTY. This is free software,
and you are welcome to modify and redistribute it under the GPL v2 license

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> INSERT INTO `rt_news` (`id`, `date`, `title`, `descr`, `keywords`, `text`, `view`) VALUES (1, '1316250169', 'Название новости', 'Описание новости', 'Ключевые слова', 'Текст новости ', 1);
Query OK, 1 row affected (0.03 sec)

mysql>

Обратите внимание, что вставлять в индекс надо с указанием primary key, по умолчанию это поле id, также необходимо указывать название индекса куда вставляются данные.

Теперь в БД и индексе есть данные, можно с ними работать.

Извлечение данных из индекса

mysql> SELECT * FROM rt_news WHERE view=1\G;
*************************** 1. row ***************************
      id: 1
  weight: 1
    view: 1
    date: 1316250169
   title: Название новости
   descr: Описание новости
keywords: Ключевые слова
    text: Текст новости
1 row in set (0.00 sec)
# Параметр \G позволяет вывести данные вертикально

Изменение данных в БД и индексе

# Для БД
UPDATE `news` SET `title`='Name of news' WHERE  `id`=1 LIMIT 1;

 

# Для индекса
mysql> REPLACE INTO `rt_news` (`id`, `date`, `title`, `descr`, `keywords`, `text`, `view`) VALUES (1, '1316250169', 'Name of news', 'Описание новости'
, 'Ключевые слова', 'Текст новости ', 1);
Query OK, 1 row affected (0.03 sec)

# просмотр изменений для индекса
mysql> SELECT * FROM rt_news WHERE view=1\G;
*************************** 1. row ***************************
      id: 1
  weight: 1
    view: 1
    date: 1316250169
   title: Name of news
   descr: Описание новости
keywords: Ключевые слова
    text: Текст новости
1 row in set (0.00 sec)

Удаление данных из БД и индекса

# Для БД
DELETE FROM `news` WHERE  `id`=1 LIMIT 1;

 

# Для индекса
mysql> DELETE FROM `rt_news` WHERE  id=1;
Query OK, 0 rows affected (0.00 sec)

# просмотр данных из индекса
mysql> SELECT * FROM rt_news WHERE view=1\G;
Empty set (0.01 sec)

Заключение

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

Слышал мнение, что RT индексы сильно медленно работаю по сравнению с обычными индексами, но данная статья это опровергает.

Будут вопросы задавайте.

ЗЫ Еще один момент, все запросы которые относятся к БД я делал через обращение к mysql через порт 3306 (дефолтный), а запросы которые относятся к индексу через порт 9306, который указан в конфиге.

Статья просмотренна 723616 раз, зашло посетителей 75587

рубрика: Песочница

35 комментариев в “Работа с realtime индексами (RT) в поисковом движке Sphinx”

  1. Avatar
    Иван пишет:

    Большое спасибо за статью. Очень интересно. Добавил в закладки. Надеюсь буду использовать в своих проектах


  2. Avatar
    maxnag пишет:

    Пожалуйста, да я сам просидел неделю над этой задачей, сначала я думал, что это какое-то дополнение к основному индексу и не мог понять как с ним работать…. потом я пошерстив в инете понял, что я не прав и переписал используя только RT индексы, но и тут была лажа, данные добавляются, удаляются, редактируется, а в итоге ничего не показывается (нужные поля), только Id, weight… Оказывается надо указывать тип полей и их название которое надо вывести, те..

    rt_attr_string = title
    rt_attr_string = descr
    rt_attr_string = keywords
    rt_attr_string = text

    и только тогда я смог увидеть их содержание, хотя в примерах, даже на основном сайте такого не написано. Возможно и не так получают эти данные, но во всяком случае все заработало!
    Спасибо Вам за каммент!


  3. Avatar
    dazzzed пишет:

    Так вы делаете не совсем правильно. Нужно из полученных ID, обращаться запросом через обычный MySql. Выборка по id происходит быстро.


  4. Avatar
    maxnag пишет:

    Ну это я и увидел что отдает только ид и вес. Я просто привык что в обычных индексах отдается больше и хотел такое же от этих.
    Да и мне отдадут ИД, а мне потом делать еще запрос на получение данных.
    Где-то и вы правы и я. Я предложил такое решение.


  5. Avatar
    Иван пишет:

    У меня в конфиге
    index rt_docs
    {
    type = rt
    path = /usr/local/etc/sphinx/data/rt_docs
    rt_field = title
    rt_attr_string = title
    }
    Но при SELECT только id. Как быть?


  6. Avatar
    maxnag пишет:

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


  7. Avatar
    Артур пишет:

    А что прописывать в конфиге секцию source с подключением к бд c sql_query и прочими настройками не нужно?


  8. Avatar
    maxnag пишет:

    Это при использовании старых индексов, а при этих не надо, просто в коде где идет изменения данных, например к новостям, тоже надо делать запросы для Сфинкса.
    Тут получается как 2 БД, одна на мускуле, а другая на Сфинксе. Может несколько не удобно, что надо делать 2 запроса, но удобнее по попаданию данных в индекс, и получению данных при поиске, просто SQL запросы. Мне это реализация очень понравилась.


  9. Avatar
    Артур пишет:

    при вставке индекса INSERT INTO rt_test1 (id,title,content) VALUES (10,'test','test')
    появляется ошибка unknown column: 'title'. Что это может быть?

    пример моего конфига :
    index rt_test1
    {
    type = rt
    path = /var/lib/sphinx/rt_test1

    rt_field = title
    rt_field = content
    rt_attr_timestamp = added
    rt_attr_uint = salary

    docinfo = extern
    mlock = 0
    enable_star = 1
    morphology = stem_enru, soundex, metaphone
    min_word_len = 2
    min_infix_len = 2
    charset_type = utf-8
    html_strip = 1
    rt_mem_limit = 256M
    }


  10. Avatar
    maxnag пишет:

    Привет, у меня конфиг немного не такой, добавь в свой rt_attr_string = title , именно из-за этого я и просидел несколько часов)). И почитай ответ на первый камент к статье.


  11. Avatar
    Alex пишет:

    А че за — rt_attr_uint = view?
    Почему он не определен как rt_field?


  12. Avatar
    maxnag пишет:

    Добрый день. Вы дальше вниз по статье пробежались? Вы увидите, что поле view имеет тип — integer, это флаг который показывает что новость показывается или нет. В самой БД это аналог поля show, собственно и тип такой, советую почитать еще документацию.


  13. Avatar
    Сергей пишет:

    `show` TINYINT(1) NOT NULL DEFAULT '0',

    ну никак `show` не похожа на `view`


  14. Avatar
    Alex пишет:

    Это я видел, но я не могу понять как атрибут view связан с РТ индексом. Я так понимаю что атрибуты вешаются на поля, а rt_field определяют поля. Или я не правильно понял? Я только недавно познакомился со Сфинксом и наверно не до конца понимаю.


  15. Avatar
    Сергей пишет:

    Здравствуйте!
    Очень заинтересовался описанным в статье решением! Даже если rt-индексы работают медленнее обычных, то всё равно, удобство rt-индексов того стоят.
    Но вот у меня возник один вопрос: допустим я создал базу, настроил rt-индексы, запустил демона… Далее, к примеру, пользователи весь день добавляют статьи на сайт. Но в середине дня, Сфинкс по неизвестной причине "отвалился". Например, сервер перезапустили, а Сфинкса забыли… В конце дня администратор замечает, что Сфинкс не работает и запускает его. И индексация продолжает работать нормально. Но те статьи, которые были добавлены в момент "отсутствования " Сфинкса, никогда не смогут быть найдены поиском по сайту.
    Как сделать так, чтобы данные для индексации не пропадали? Может же быть так, что какие-то данные не были добавлены в индекс и об этом не будет известно. Например, если запрос в базу был выполнен, а перед запросом добавления в индекс вылетела ошибка и скрипт перестал исполняться…
    Есть ли смысл так же как и при обычной индексации, полностью обновлять rt-индексы раз в сутки?


  16. Avatar
    maxnag пишет:

    Добрый день Сергей!
    Да, вопрос хороший, таких штатных инструментов нет, которые бы отслеживали актуальность индекса.
    1 Можно написать крон-скрип, который будет просто за сутки или все синхронизировать, занимает это ну вообще понт по времени, я не беру гигабайтную текстовую базу, а 100тыс доков, очень быстро.
    2 Т.к. используется интерпретатор MySQL, т.е. запрос выглядит одинаково, просто на другой порт, то я блок CUD (create, up and del) вставил в блок перехвата ошибок, если что-то отвалилось, то можно выслать мыло, СМС или еще что-то, чтобы сигнализировать об ошибке и предпринять действия.

    Фантазия может разыграться еще на многие вещи, дерзайте.

    ЗЫ в конце статьи есть ссылка на тесты по производительности RT indexes.


  17. Avatar
    Сергей пишет:

    У меня возникла ещё один вопрос. Пробился над ним весь день. Работает ли SphinxApi при данной конфигурации? Дело в том, что раньше я использовал команду port=9306, а не listen = localhost:9306:mysql41. В первом случае работает SphinxApi, но не работает mysql -P9306, Ошибка такая: ERROR 2013 (HY000): Lost connection to MySQL server at 'reading initial communication packet', system error: 0.
    (В Линуксе приходится писать только с указанием хоста: mysql -h 127.0.0.1 -P9306). Во втором же случае (когда через listen) работает mysql -P9306, но не работает SphinxApi. Когда делаю дамп результатов, получаю абсолютно пустой экран, даже не пустой массив. Пожалуйста, подскажите выход из этой ситуации.


  18. Avatar
    maxnag пишет:

    Вот тут уже я не подскажу, я как-то не юзал его апи.


  19. Avatar
    jurchiks пишет:

    :mysql41 говорит searchd, что надо запускать SphinxQL. Без этого не к чему подсоеденится.
    Если нужны оба SphinxQL и SphinxApi, то ставь так:
    listen = localhost:9312:sphinx # SphinxApi
    listen = localhost:9306:mysql41 # SphinxQL
    Соответственно, подсоеденятся к SphinxQL надо через порт 9306 (в консоли -P9306):
    $connection = new mysqli('127.0.0.1', '', '', '', 9306);
    $rset = $connection->query('SELECT * FROM rt_index WHERE …');
    а к SphinxApi:
    $cl = new SphinxClient();
    $cl->SetServer('localhost', 9306);

    $result = $cl->Query('text to search', 'rt_index');


  20. Avatar
    maxnag пишет:

    Спасибо!


  21. Avatar
    Professor пишет:

    Добрый день. Спасибо за статью.
    Подскажите пожалуйста:
    Насколько понял, они довольно плохо работают с MVA или вообще не работают?
    Каким образом инициализируется обновление?
    Насколько затратно по ресурсам в отличие от варианта смерживания дельта индекса?


  22. Avatar
    maxnag пишет:

    Сказать про MVA ничего не могу, т.к. не разбирался в этом вопросе, в вот насчет ресурсов — RT на большом кол-ве документов свыше 2 млн начинают проигрывать обыным индексам, можно сделать комбинацию одного и другого)


  23. Avatar
    Николай пишет:

    Спасибо за статью!

    Много ответов на свои вопросы в ней нашел.

    Добавил в закладки.


  24. Avatar
    maxnag пишет:

    Рад, что помог!


  25. Avatar
    Николай пишет:

    Возник такой вопрос:
    В обычных индексах я в sql_query делал запрос на выборку из двух таблиц.
    А вот как сделать в rt индексах что бы данные из разных таблиц индексировались?


  26. Avatar
    BOPOH пишет:

    >> Получается ситуация, что добавили новость в БД, а она появится в поисковом индексе только когда произойдет объединение индексов

    А зачем искать только по основному индексу? Что мешает искать по основному и delta?

    Тогда данные попадут сразу же как обновится delta-индекс, а он обновляется часто. Даже если раз в 30 минут — думаю это вполне нормальное время для отображения новости.

    И как быть с инфиксным поиском? rt-индексы его же не поддерживают (разве что экспериментально)


  27. Avatar
    maxnag пишет:

    Вижу Ваши знания куда более глубокие, чем у меня. Но настраивать это все, дельты, мержи и тд — время. С RT-индексами куда всё проще, делаем добавление в БД и второй командой в Сфинкс и запись уже там, также и с редактирование и удалением. Программировать это все стало куда проще. Возможно некоторые недостатки уже убрали в текущих версиях (2.1.5-stable, 2.2.1-beta) да и статья была об RT индексах.


  28. Avatar
    Алекс пишет:

    подскажите пожалуйста ,возник такой вопрос:
    В обычных индексах я в sql_query делал запрос на выборку из двух таблиц.
    А вот как сделать в rt индексах что бы данные из разных таблиц индексировались?


  29. Avatar
    maxnag пишет:

    Как написано в документации

    RT index can be accessed using MySQL protocol. INSERT, REPLACE, DELETE, and SELECT statements against RT index are supported.

    следует, что нужно сделать табличку в БД которая будет содержать информацию нужную для поиска, а заполняться(CRUD operations) она будет по логике вашего скрипта, возможно через триггеры или иным способом. И потом настроить сфинкс на работу с этой таблицей.

    Вот ссылка на документ http://sphinxsearch.com/docs/2.1.9/rt-overview.html


  30. Avatar
    Андрей пишет:

    Приветствую!

    Есть единственный вопрос, на который я не могу найти ответ: если мне нужно обновить одновременно несколько записей в RT индексе в одном запросе, как это сделать?

    Спасибо!

    С уважением, Андрей.


  31. Avatar
    maxnag пишет:

    Привет! Насколько я помню там нет мультизапроса, приходится обновлять по записи, но думаю лучше почитать доку, т.к. я уже года 3-4 с этим не сталкивался


  32. Avatar
    Андрей пишет:

    Я использую sphinxql, там есть возможность делать мультизапросы, но только на селекты или другие примитивные операции. А вот update — не канает. И если мне надо обновить 5000 записей, на это уходит 7-8 секунд, в цикле. Неужели никак не обойти? Или может как-то хирто затерать записи или что-то еще.. Если бы ты с этим столкнулся, что бы делал? Просто идея..

    Спасибо.


  33. Avatar
    maxnag пишет:

    А что если вы попробуете REPLACE ????


  34. Avatar
    Андрей пишет:

    Большое спасибо, с replace получилось.

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


  35. Avatar
    maxnag пишет:

    У меня вот было так. Я делаю новую запись в основную БД, также доп запрос на создание индекса, если я удаляю, также из индекса и тоже при апдейте. Вы конечно используете не RT индексы… Как там я не пробовал, может напишите скрипт который прогонить всю БД в сфинкс, а затем просто правильно делайте инсерт, делете и апдейт.


не публикуется

пример

Оставить комментарий или два:

  

Облако тегов

cli csv dump events form Kohana locale models MySQL mysqldump orm PHP tools trigger validate газ газовый счетчик итоги кеширование переменные

Облако тегов плагина WP Cumulus для WordPress требует для просмотра Flash Player 9 или выше.

Я на твиттере!

  • у твиттера тоже бывают перерывы...

Календарь

Сентябрь 2011
Пн Вт Ср Чт Пт Сб Вс
« Авг   Окт »
 1234
567891011
12131415161718
19202122232425
2627282930  

Сейчас на сайте