- PVSM.RU - https://www.pvsm.ru -

Sphinx: delta-индексы и несколько серверов поиска

Более-менее большой проект рано или поздно приходит к необходимости полнотекстового поиска по контенту.
Для этой цели был придуман поисковый движок Sphinx [1].

Когда база становится большой либо индексов много переиндексация начинает занимать довольно длительное время, что может иметь разные негативные последствия для проекта. В этот момент стоит задуматься об использовании delta-индексов [2].
Автор столкнулся с этой необходимостью в тот момент, когда переиндексация начала занимать более часа.

Но это все подробно описано в документации и делается довольно просто:

source src_mysql
{
    type                = mysql
    sql_host            = localhost
    sql_user            = sphinx
    sql_pass            = secret
    sql_range_step      = 1000
}

source src_news : src_mysql
{
    sql_db		= project

    sql_query_pre       = SET NAMES utf8
    sql_query_pre       = SET CHARACTER SET utf8
    sql_query_pre       = REPLACE INTO sph_counter SELECT 'src_news', MAX(id) FROM news
    sql_query_range     = SELECT MIN(id), MAX(id) FROM news
    sql_query = SELECT id as news_id, title, content FROM news WHERE id>=$start AND id<=$end
}

source src_news_delta : src_news
{
    sql_query_pre       = SET NAMES utf8
    sql_query_pre       = SET CHARACTER SET utf8
    sql_query_range     = 
    sql_query           = SELECT id as news_id title, content FROM news 
                            		WHERE id > ( SELECT max_value FROM sph_counter WHERE source = 'src_news')

    sql_query_post       = REPLACE INTO sph_counter SELECT 'src_news', MAX(id) FROM news
}

Теперь рассмотрим случай когда индексирующих серверов несколько. Причин для этого может быть несколько, но появляется проблема: один сервер произведя индексацию переписывает в БД значение последнего ID. При запуске индексации на следующем сервере в выборку не попадут записи проиндексированные предыдущим сервером. Возникают «пробелы» в индексах и поисковая выдача будет постоянно разная, причем будут упущены случайные результаты.

Что делать?

Нужно в таблице sph_counter дополнительно хранить ID индексирующего сервера.

Добавляем колонку hostname.

В итоге таблица будет иметь следующий вид:

CREATE TABLE IF NOT EXISTS `sph_counter` (
  `source` varchar(100) NOT NULL DEFAULT '',
  `max_value` bigint(20) NOT NULL,
  `hostname` varchar(50) NOT NULL DEFAULT '',
  PRIMARY KEY (`source`,`hostname`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

* Обратите внимание на индекс, он необходим для корректной работы REPLACE INTO

Далее в зависимости от вашей конфигурации варианта 2:

1. Процесс Sphinx работает на том же сервере что и БД, настроена master-master репликация

В такой ситуации задача решается довольно просто: используем глобальную переменную MySQL — 'hostname'
Наша конфигурация приобретает следующий вид:

source src_news : src_mysql
{
    sql_db		= project

    sql_query_pre       = SET NAMES utf8
    sql_query_pre       = SET CHARACTER SET utf8
    sql_query_pre       = REPLACE INTO sph_counter SELECT 'src_news', MAX(id), @@hostname FROM news
    sql_query_range     = SELECT MIN(id), MAX(id) FROM news
    sql_query = SELECT id as news_id, title, content FROM news WHERE id>=$start AND id<=$end

}

source src_news_delta : src_news
{
    sql_query_pre       = SET NAMES utf8
    sql_query_pre       = SET CHARACTER SET utf8
    sql_query_range     = 
    sql_query           = SELECT id as news_id, title, content FROM news 
                            		WHERE id > ( SELECT max_value FROM sph_counter WHERE source = 'src_news' AND hostname = @@hostname)

    sql_query_post       = REPLACE INTO sph_counter SELECT 'src_news', MAX(id), @@hostname FROM news
}
2. Сервера MySQL и Sphinx — разные, запросы идут в master

В такой ситуации использовать глобальную переменную hostname уже не получится — мы будем всегда получать имя сервера, на котором работает БД.

Выход: будем использовать информацию о соединении и пользовательские переменные.

source src_news : src_mysql
{
    sql_db		= project

    sql_query_pre       = SET NAMES utf8
    sql_query_pre       = SET CHARACTER SET utf8
    sql_query_pre       = SELECT @sphinx_instance:=IF(STRCMP(@sphinx_host:=SUBSTRING_INDEX(host,':',1),'localhost'),@sphinx_host,@@hostname) AS sphinx_instance 
FROM information_schema.processlist WHERE ID=connection_id();
    sql_query_pre       = REPLACE INTO sph_counter SELECT 'src_news', MAX(id), @sphinx_instance FROM news
    sql_query_range     = SELECT MIN(id), MAX(id) FROM news
    sql_query = SELECT id as news_id, title, content FROM news WHERE id>=$start AND id<=$end
}

source src_news_delta : src_news
{
    sql_query_pre       = SET NAMES utf8
    sql_query_pre       = SET CHARACTER SET utf8
    sql_query_pre       = SELECT @sphinx_instance:=IF(STRCMP(@sphinx_host:=SUBSTRING_INDEX(host,':',1),'localhost'),@sphinx_host,@@hostname) AS sphinx_instance 
FROM information_schema.processlist WHERE ID=connection_id();
    sql_query_pre       = REPLACE INTO sph_counter SELECT 'src_news', MAX(id), @sphinx_instance FROM news
    sql_query_range     = 
    sql_query           = SELECT id as news_id, title, content FROM news 
                            		WHERE id > ( SELECT max_value FROM sph_counter WHERE source = 'src_news' AND hostname = @sphinx_instance)

    sql_query_post       = REPLACE INTO sph_counter SELECT 'src_news', MAX(id), @sphinx_instance FROM news
}

Эта конфигурация будет работать в обоих случаях.

Автор: darken99


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/mysql/10687

Ссылки в тексте:

[1] Sphinx: http://sphinxsearch.com/

[2] delta-индексов: http://sphinxsearch.com/docs/current.html#delta-updates