Sphinx, часть 3. Real-Time индексы

Date November 8th, 2010 Author Vitaly Agapov

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

— Терри Пратчетт «Цвет Волшебства»

Минувшим летом команда Андрея Аксёнова осчастливила комьюнити новой версией Sphinx за нумером 1.10-beta. Среди прочих полезных нововведений вроде строковых атрибутов и многопоточного поиска была фича, название которой заставило моё сердце биться быстрей – RT-индексы. При более подробном изучении документации возбуждение поугасло, ибо ожидания мои, видимо, были сильно завышенными. Но тем не менее шаг вперёд сделан. И какой шаг! Это то, чего старому Сфинксу очень не хватало, отчего до сих пор в некоторых задачах приходилось пользоваться старым-добрым, но сильно тормознутым LIKE.

Итак, мы получили быстрый поиск по индексу, обновляющемуся в реальном режиме времени. Но кармические законы вселенной подсказывают, что необходимо при этом что-то и отдать и от чего-то отказаться. А отказаться придётся от префиксов и инфиксов (будем надеяться на будущие версии), от MVA-атрибутов (аналогично), а также подготовиться к тому, что индекс обновлять нам придется вручную. Последний пункт означает, что при добавлении записи в базу надо сделать INSERT в индекс, при удалении из базы – DELETE из индекса, а при изменении записи в таблице – REPLACE всё в тот же индекс. Неудобно, но за всё надо платить. Раньше в такой ситуации пришлось бы переиндексировать весь индекс целиком или хотя бы дельту.

Посмотрим более детально.

Создание RT-индекса

Лезем в конфиг sphinx.conf и добавляем новую секцию index:

index rt
{
type = rt
path = /var/data/sphinx/rt
rt_mem_limit = 1G
rt_field = bname
rt_field = aname
rt_field = oname
rt_field = altname
docinfo                    = extern
mlock                      = 0
min_word_len            = 2
charset_type            = utf-8
charset_table           = 0..9, A..Z->a..z, _, a..z, U+410..U+42F->U+430..U+44F, U+430..U+44F
html_strip                = 0
}

Сюда же при необходимости можно добавить атрибуты, но можно не пытаться добавлять директивы min_infix_len, enable_star, source и иже с ними.

Дальше в секции searchd надо добавить директивы:

workers = threads
listen = localhost:9306:mysql41

Если не будет первой, то при запуске searchd ругнётся:

WARNING: index ‘rt': RT index requires workers=threads – NOT SERVING

Если не будет второй, то до RT-индекса нельзя будет достучаться по протоколу MySQL. Пока что это, кстати, единственный протокол для работы с RT-индексами, но разработчики обещают реализовать в будущем и другие.

Перезапускаем sphinx, после чего можем немного поиграться:

$ mysql -h 127.0.0.1 -P 9306
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 1
Server version: 1.10-dev (r2153)

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

mysql> INSERT INTO rt VALUES ( 1, 'first', 'test', 'test', 'test' );
mysql> SELECT * FROM rt WHERE MATCH('test');
+------+--------+
| id   | weight |
+------+--------+
|    1 |   1643 |
+------+--------+

Заполнение индекса

Для начала нам потребуется создать индекс для уже имеющихся в базе данных. Это уже потом мы сможем манипулировать записями в этом индексе по мере изменения данных в рабочей БД.  Задача вроде бы не сложная, так что я уже, закатав рукава, собирался было написать соответствующий скрипт, но вовремя погуглил и нашёл готовый инструмент, так что новый велосипед не понадобился.

Найти этот инструмент можно здесь. Это php-скрипт, создающий RT-индекс в соответствии с конфиг-файлом, очень напоминающим секцию source в файле sphinx.conf. В самом простом случае без использования дельта-индексов конфигурационный файл sources.ini может выглядеть так:

[source_for_rt_index]

sql_host                                = localhost
sql_user                                = <user>
sql_pass                                = <pass>
sql_db                                  = <db_name>
sql_port                                = 3306

sphinx_host                             = 127.0.0.1
sphinx_index                            = rt
sphinx_port                             = 9306

sql_query_pre[]   = SET NAMES utf8

sql_query = "SELECT book.id AS id, book.name AS bname, book.orig_name AS oname, book.alt_name AS altname, GROUP_CONCAT(author.name) AS aname
FROM book LEFT JOIN aob ON book.id=aob.book_id LEFT JOIN author ON aob.author_id=author.id
WHERE book.id>=$start and book.id<$end
GROUP BY book.id"
sql_query_range         = "SELECT min(id) , MAX(id) FROM book"
sql_range_step          = 10000

После этого стартуем сам скрипт и радуемся жизни:

php indexer.php

Теоретически, если правильно организовать обновление индекса “на лету”, то этот скрипт более не понадобится. Но я для собственного спокойствия добавил его в cron для еженощного запуска – так всегда уверен, что индекс актуален, и в нем не накапливаются расхождения с боевой базой данных.

Связанные статьи

Ссылки

Tags: , ,
Category: MySQL, Web-dev | 3 Comments »

Comments

3 комментариев на “Sphinx, часть 3. Real-Time индексы”

  1. Иван

    Не могли бы вы пояснить зачем для rt_index указывать source. Вроде бы его фишка в том, что не надо указывать source и он будет игнорировать это.

  2. Vitaly Agapov

    Не понял вопроса. В тексте чётко написано, что “можно не пытаться добавлять директивы min_infix_len, enable_star, source и иже с ними”.

    То есть никакого source в конфигурации индекса нет.

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

  3. Igor

    Виталий, спасибо за ценный пост. Потратил кучу времени на RT и только у вас прочёл про этот скрипт. Буду пробовать.

Leave a comment

 Comment Form