Catalyst, Sphinx и realtime индекс

Как создать realtime индекс Sphinx. Использование realtime индексов в Catalyst-приложении.

Эксперименты проводились на Sphinx версий 2.2.10 и 2.0.4.

Продолжаем развивать код из первой заметки про Sphinx. Возьмем его за основу и добавим в Catalyst использование realtime индекса.

Конфигурация Sphinx и Catalyst-приложения для создания realtime индекса

В данном случае, для коннекта мы будем использовать не Sphinx::Search, а DBI.

/www/myapp/conf/sphinx/searchd.conf :

Создаем описание realtime индекса. Список допустимых типов полей и атрибутов в realtime сильно отличается от тех, которые используются в обычных индексах. Типов полей меньше и они более «требовательные».

/www/myapp/conf/sphinx/users_rt.conf :

/etc/sphinxsearch/sphinx.conf :

Если все настройки произведены верно, то при запуске сервера Sphinx будет создан realtime индекс.

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

Теперь необходимо наполнить данными новый индекс. Существует 2 основных пути:

  • использовать ATTACH INDEX,
  • заполнение с помощью INSERT INTO и REPLACE INTO.

ATTACH INDEX

ATTACH INDEX позволяет взять заполненный дисковый индекс и перенести все данные в realtime индекс.

Преимущества этого метода:

  • Очень быстро создается готовый к использованию realtime индекс.
  • Если по каким-то причинам realtime индекс был испорчен и требуется срочно восстановить — ATTACH INDEX позволяет сделать это намного быстрее, чем по одной строке добавлять данные.

Недостатки:

  • Следует быть очень аккуратным при подготовке переноса данных. Возможны самые неожиданные проблемы. Не все типы полей совместимы. Например, если дисковый индекс содержал boolean-атрибуты, а realtime вместо них использует integer (потому что, boolean-тип в RT отсутствует, по крайней мере, в нужной мне версии Sphinx его еще не было), то перенос пройдет «благополучно». Вы сможете даже извлекать данные из заполненного индекса. Но при попытке добавить новые строки вас ждет сюрприз. Таких нюансов в Sphinx — тысячи.
  • На этапе разработки использование ATTACH INDEX удобно только на первый взгляд. Вы создали индекс, радостно заполнили его данными. Дисковый индекс при этом обнулился.
    А вам буквально через полчаса требуется изменить структуру RT индекса. В современных версиях Sphinx появилась возможность изменять структуру с помощью ALTER TABLE. Однако, на той версии, на которой тестировала свои наработки я, такой возможности еще нет, чтобы внести изменения в индекс надо полностью удалить все файлы которые к нему относятся, после чего пересобрать все заново. Заполнить данными второй раз не получится — дисковый индекс уже не существует. Либо можно заполнить данными дисковый индекс, потом перенести их — но в чем тогда разница?
    Лучше написать скрипт, который построчно заполнит realtime индекс, и это будет более гибкий инструмент.

Предположительно, ATTACH INDEX будет развиваться и в новых версиях Sphinx может работать совсем иначе. Рекомендуется внимательно ознакомиться с документацией перед использованием. Возможно, в вашей компании стоит самая новая версия Sphinx, и вы сможете избежать многих проблем.

INSERT INTO

Основные конструкции для заполнения индекса:

К сожалению, id — не автоинкрементно, следить за увеличением значения придется самостоятельно. Попытка вставить с помощью INSERT INTO строку с уже существующим id приведет к ошибке.

DELETE FROM можно использовать только с указанием конкретного id. Условие «WHERE id < 1" приведет к ошибке. Попытка вызвать DELETE без WHERE так же приведет к ошибке. Вот такая унылая ситуация, чтобы удалить пару миллионов записей, надо удалить все эти миллионы поочередно. Надеюсь, в новых версиях ситуация изменилась.

Почему я ссылаюсь на старые версии Sphinx? Потому что, попробовав установить более современные версии, поняла — такого врагу не пожелаешь. Сразу же нужно обновить модули Sphinx::Search, подобрать те, которые будут совместимы. Тянется куча зависимостей. Из-за обновления Sphinx и Sphinx::Search может перестать работать старый код. Ради добавления нового функционала — переписать тысячи и тысячи строк другого, уже работающего и оттестированного кода — дело не просто дорогое, а очевидно глупое. Поэтому, работаем с тем что есть. А есть у нас — не самая последняя версия Sphinx.

Использование realtime индекса в Catalyst-приложении

Изменяем код контроллера. Заменяем Sphinx::Search на DBI, и добавляем код для вставки в realtime индекс новых записей.

Теперь запускаем сервер.

Вводим в адресную строку браузера: http://192.168.33.10:3000/users . Получаем данные из индекса:

Если обратиться по адресу http://192.168.33.10:3000/users/update — в индекс будут добавлены новые данные и тут же получен результат:

С какими проблемами я столкнулась на данном этапе

Более строгое поведение строковых атрибутов и полей

Тип данных rt_attr_string позволяет просматривать данные, но не разрешает выполнять поиск по этим данным. Тип rt_field позволяет выполнять поиск, но не позволяет просматривать данные. Если вам нужно и то, и другое, придется добавить в индекс и атрибут, и поле.

Индексы без полнотекстовых полей запрещены

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

Можно создать пустое поле, не заполнять его данными.

Альтернатива count() в Sphinx

Для того, чтобы в web-интерфейсе отображать данные постранично, нужно предварительно выяснить общее количество записей в таблице, или количество записей, которые будут найдены с учетом некоторого условия. В обычном MySQL для этого используется функция count().

В Sphinx используется «SHOW META», которая выполняется сразу после основной выборки:

Попытка использовать count() приведет к ошибке:

Пример:

Проблема с поиском в большом индексе «offset out of bounds»

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

Для решения проблемы можно использовать опцию «max_matches» в запросах, и внести правки в конфиг.

Меняем значение «max_matches» в конфиге, в секции описания демона Sphinx:

При запросе используем «OPTION max_matches»: