Статья нацелена на тех кто уже знает что такое Sphinx и SphinxQL
Цель: Обеспечить непрерывность работы поиска по сайту с помощью Sphinx в момент проведения технических работ над одной из нод Sphinx кластера.
Sphinx отличный инструмент для организации поиска по сайту. В проекте в котором я участвую поиск объявлений происходит с помощью Sphinx. Объявления хранятся в бд в EAV модели а поиск по ним выполняет Sphinx затем объявления извлекаются по найденным сфинксом идентификаторам. Таким образом если Sphinx перестанет работать то это скажется на всём сайте.
Для работы используются rt индексы sphinx для моментального внесения изменений в поисковую выдачу если какое либо объявление будет отредактировано или забанено. Пока это работало на одной ноде всё было хорошо до тех пор пока не возникало необходимости внести изменения в саму структуру индексов. Для изменения списка атрибутов в поисковом индексе необходимо было править конфигурацию, перезапускать сфинкс и выполнять переиндексацию объявлений. Для того чтобы это производить без остановки работы сайта решено было построить кластер с одной главной нодой фактически выполняющей роль балансировщика и двумя дочерними нодами содержащими индекс и являющимися зеркальными между собой.
Для организации поискового кластера в Sphinx есть distributed индексы.
На главной ноде все индексы имеют следующий вид.
К слову есть разница между тем как описывать дочерние ноды, в предыдущем примере они описаны как зеркала, а в следующем они описаны как ноды хранящие две разные части одного индекса. Разница в том что в первом случае запрос select отправляется на одну из нод, а во втором примере select отправляется на все ноды и результат поиска от каждой из нод объединяется.
На дочерних нодах индексы описываются, как самые обычные real time индексы в Sphinx:
Всё с выборкой на кластере проблем нет.
Мне уже показалось что задача на этом решена, но не ту-то было. Выборка работает, а что на счёт REPLACE или INSERT запросов?
Как оказалось тут была засада — REPLACE и INSERT по умолчанию работают только на локальных индексах, а я использую распределенный.
Но не беда. Так как sphinx проект с открытым кодом я сделал свою сборку которая позволяет выполнять REPLACE запросы на distributed индексах.
Для её сборки надо скачать исходники и выполнить команду
Теперь запустив эту сборку с точно теме же настройками что и ранее запрос выполнится на всех нодах-зеркалах.
Я для проверки использовал два зеркала, и мы видим что затронуто 2 записи, то есть по одной записи на каждой ноде.
Цель: Обеспечить непрерывность работы поиска по сайту с помощью Sphinx в момент проведения технических работ над одной из нод Sphinx кластера.
Sphinx отличный инструмент для организации поиска по сайту. В проекте в котором я участвую поиск объявлений происходит с помощью Sphinx. Объявления хранятся в бд в EAV модели а поиск по ним выполняет Sphinx затем объявления извлекаются по найденным сфинксом идентификаторам. Таким образом если Sphinx перестанет работать то это скажется на всём сайте.
Для работы используются rt индексы sphinx для моментального внесения изменений в поисковую выдачу если какое либо объявление будет отредактировано или забанено. Пока это работало на одной ноде всё было хорошо до тех пор пока не возникало необходимости внести изменения в саму структуру индексов. Для изменения списка атрибутов в поисковом индексе необходимо было править конфигурацию, перезапускать сфинкс и выполнять переиндексацию объявлений. Для того чтобы это производить без остановки работы сайта решено было построить кластер с одной главной нодой фактически выполняющей роль балансировщика и двумя дочерними нодами содержащими индекс и являющимися зеркальными между собой.
Настройка секций indexer и searchd в целом обычная
indexer
{
}
searchd
{
listen = 127.0.0.1:3301 # Порт для Sphinx Api
listen = 127.0.0.1:3309:mysql41 # Порт для SphinxQL
log = ./sphinx-log-searchd.log
query_log = ./sphinx-log-query.log
pid_file = ./sphinx-log-searchd.pid
binlog_path = ./sphinx-binlog
read_timeout = 5
max_children = 30
max_matches = 1000
seamless_rotate = 1
preopen_indexes = 0
unlink_old = 1
workers = threads
}
Для организации поискового кластера в Sphinx есть distributed индексы.
На главной ноде все индексы имеют следующий вид.
index distributed_section_1
{
type = distributed
agent = 127.0.0.1:9301:rt_section_1|127.0.0.1:9302:rt_section_1
ha_strategy = nodeads
}
К слову есть разница между тем как описывать дочерние ноды, в предыдущем примере они описаны как зеркала, а в следующем они описаны как ноды хранящие две разные части одного индекса. Разница в том что в первом случае запрос select отправляется на одну из нод, а во втором примере select отправляется на все ноды и результат поиска от каждой из нод объединяется.
index distributed_section_1
{
type = distributed
agent = 127.0.0.1:9301:rt_section_1
agent = 127.0.0.1:9302:rt_section_1
ha_strategy = nodeads # стратегия распределения запросов между нодами. nodeads - отправляет запросы к не мёртвым нодам
}
На дочерних нодах индексы описываются, как самые обычные real time индексы в Sphinx:
index rt_section_1
{
type = rt
mlock = 1
morphology = stem_en, stem_ru
min_word_len = 3
min_infix_len = 1
index_exact_words = 1
dict = keywords
path = ./notices_rt_section_1
rt_field = title
rt_field = text
rt_attr_uint = date
rt_attr_uint = active
rt_attr_multi = location
}
Всё с выборкой на кластере проблем нет.
mysql> select * from rt_section_1;
+---------+------------+--------+----------+
| id | date | active | location |
+---------+------------+--------+----------+
| 185191 | 1398749772 | 1 | 145430 |
| 185234 | 1398749771 | 1 | 145425 |
+---------+------------+--------+----------+
2 rows in set (0.03 sec)
Мне уже показалось что задача на этом решена, но не ту-то было. Выборка работает, а что на счёт REPLACE или INSERT запросов?
Как оказалось тут была засада — REPLACE и INSERT по умолчанию работают только на локальных индексах, а я использую распределенный.
Но не беда. Так как sphinx проект с открытым кодом я сделал свою сборку которая позволяет выполнять REPLACE запросы на distributed индексах.
Для её сборки надо скачать исходники и выполнить команду
cmake . && make
Теперь запустив эту сборку с точно теме же настройками что и ранее запрос выполнится на всех нодах-зеркалах.
mysql> REPLACE INTO rt_section_130054 (id, `location`, `title`, `text`, `active`, `date`) VALUES ( 2435558, ( 145411 ) , 'Тестовый заголовок', 'Тестовая запись', 1, '1399529047');
Query OK, 2 rows affected (0.04 sec)
Я для проверки использовал два зеркала, и мы видим что затронуто 2 записи, то есть по одной записи на каждой ноде.