31 июля 2009 г. cron MySQL Pyha PHP SMF SphinxSearch Поисковые системы

Sphinx. Для чего нужны дельта-индексы и как их готовить?

sphinx-delta

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

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

Поэтому мы делаем два индекса:

  • Полный - индексируются все данные целиком (запускаем раз в сутки)
  • Дельта - индексируется только малая часть данных, добавленных в течении дня (запускаем каждые 5-10 минут)

Но как определять с какого момента индексировать данные в дельта-индексе?

Нам необходимо помнить крайний проиндексированный ид-записи из полного-индекса, и начинать индексировать данные только с него, все очень просто :)

Для этого создадим еще одну таблицу в базе данных, чтобы хранить там ид крайней записи из полного-индекса:

CREATE TABLE `sphinx_delta_counter` (
    `index_name` ENUM( 'pyha_forum' ) NOT NULL,
    `last_post_id` INT UNSIGNED NOT NULL
) ENGINE = InnoDB;

Где,

  • "index_name" - имя индекса, например если у вас много индексов с разных таблиц (можно опустить, если у вас один индекс);
  • "last_post_id" - крайний номер записи для конкретного индекса.

Вставим для нашего индекса запись, чтобы потом можно было обновлять счетчик:

INSERT INTO `sphinx_delta_counter`
    SET `index_name` = 'pyha_forum', `last_post_id` = 0;

Вы конечно можете этот запрос и в индексе сфинкса использовать, предварительно переписав его на "on duplicate key" и добавив уникальный индекс на связку "index_name+last_post_id", однако я думаю это излишне.

Строим индексы в Sphinx

Привожу примерный конфигурационный файл, на примере предыдущей статьи /2009/smf-sphinx/.


# сорс для подключения к БД, далее его будем наследовать в других сорсах
source dbconnect
{
    type = mysql
    sql_host = localhost
    sql_user = pyha
    sql_pass = 
    sql_db = pyha
    sql_port = 3306
    sql_sock = /tmp/mysql.sock

}

# сорс полного-индекса
source pyha_forum : dbconnect
{

    sql_query_pre = SET NAMES utf8
    sql_query_pre = SET CHARACTER SET utf8

    # обновляем наш счетчик, добавляем туда крайний ид записи для дельта-индекса
    sql_query_pre = \
        update sphinx_delta_counter \
        set last_post_id = (select max(ID_MSG) from smf_messages) \
        where index_name = 'pyha_forum';

    # выбираем все данные для полного-индекса
    sql_query = \
        select ID_MSG, concat('user:',posterName) as posterName, subject, body \
        from smf_messages;

    sql_query_info = select ID_MSG, posterName, subject, body from smf_messages where ID_MSG = $id
    sql_ranged_throttle = 0
}

# сорс дельта-индекса
source pyha_forum_delta : dbconnect
{

    sql_query_pre = SET NAMES utf8
    sql_query_pre = SET CHARACTER SET utf8

    # выбираем только те данные, которые появились после проиндексации полного-индекса
    sql_query = \
        select ID_MSG, concat('user:',posterName) as posterName, subject, body \
        from smf_messages \
        where ID_MSG > (select last_post_id from sphinx_delta_counter where index_name = 'pyha_forum');

    sql_query_info = select ID_MSG, posterName, subject, body from smf_messages where ID_MSG = $id
    sql_ranged_throttle = 0
}

# полный-индекс
index pyha_forum
{
    source = pyha_forum
    path = /var/sphinxsearch/pyha_forum

    docinfo = extern
    mlock = 0
    morphology = stem_enru
    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
    min_infix_len = 2
    enable_star = 1
}

# дельта-индекс
index pyha_forum_delta : pyha_forum
{
    source = pyha_forum_delta
    path = /var/sphinxsearch/pyha_forum_delta
}

# настройки индексатора
indexer
{
    mem_limit = 32M
}

# настройки поискового демона
searchd
{
    address = 127.0.0.1
    port = 3312
    log = /var/sphinxsearch/log/searchd.log
    query_log = /var/sphinxsearch/log/query.log
    read_timeout = 5
    max_children = 30
    #pid_file = /var/sphinxsearch/log/searchd.pid
    max_matches = 1000
}

Ну вот и все, сфинкс настроен, далее поставим переиндексацию в крон.

Ставим в крон


0 1 * * * /usr/local/bin/indexer --config /usr/local/etc/sphinx.conf.sh pyha_forum --rotate > /dev/null
*/5 * * * * /usr/local/bin/indexer --config /usr/local/etc/sphinx.conf.sh pyha_forum_delta --rotate > /dev/null

Где,

  1. Индексировать полный-индекс каждый день в час ночи

  2. Индексировать дельта-индекс каждые пять минут

Не понятно что за конфиг "/usr/local/etc/sphinx.conf.sh"?
Тогда, читаем статью Инклуд конфигов Sphinx.

Добавим поиск по дельта-индексу в код

Так как я использую SphinxSE, то добавить надо так:

`query` = "поисковый запрос;index=pyha_forum,pyha_forum_delta"

Тогда будет производится поиск по полному- и дельта-индексам!

Встраиваем в SMF

Далее, раз мы разбираем пример на основе статьи /2009/smf-sphinx/, то снова найдем код в "/Source/Search.php" (строка ~1377):

$request = db_query(
   "SELECT `sm`.`ID_TOPIC`, `ssm`.`id` AS `ID_MSG`, 1000 AS `relevance`, 0 AS `num_matches`
    FROM `sphinx_smf_messages` AS `ssm`
    LEFT JOIN `smf_messages` AS `sm`
        ON `ssm`.`id` = `sm`.`ID_MSG`
    WHERE `ssm`.`query` = '".$context['search_params']['search'].";weights=1,2,3'
    LIMIT " . (int) $_REQUEST['start'] . ", $modSettings[search_results_per_page]", __FILE__, __LINE__);

заменим на

$request = db_query(
   "SELECT `sm`.`ID_TOPIC`, `ssm`.`id` AS `ID_MSG`, 1000 AS `relevance`, 0 AS `num_matches`
    FROM `sphinx_smf_messages` AS `ssm`
    LEFT JOIN `smf_messages` AS `sm`
        ON `ssm`.`id` = `sm`.`ID_MSG`
    WHERE `ssm`.`query` = '".$context['search_params']['search'].";weights=1,2,3;index=pyha_forum,pyha_forum_delta'
    LIMIT " . (int) $_REQUEST['start'] . ", $modSettings[search_results_per_page]", __FILE__, __LINE__);

Ну вот и все :)

Комментарии

Что вы так боитесь использовать слово последний? суеверия =)

Я служил в Военно-Морской Авиации, там слово "последний" - нет, потомучто "последний вылет". Вот там из меня и выбили это слово ;)

а ларчик просто открывался.. Спасибо за информацию))

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

Хочу посоветоваться. Есть ли смысл создавать дельта индексы, если полная индексация системы происходит примерно за 3-4 минуты?

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

У вас ротейт идет 3-4 минуты?

Доброе время суток, я вот делал по такому алгоритму строю дельта индекс, там появились новые индексы, потом добавляю их в основной индекс с помощью --merge , в принципе все отлично, но хотел бы Вас спросить можно ли как то удалять индексы из дельта индекса после слияния в основной ? заранее всех благодарю!

Спасибо разобрался! :)

Elvis, как решили?

Разве счётчик нада обновлять не в сорсе дельта индекса ?

Grind, а на основании чего вы так решили?

Предпологал, что после каждой дельта индексации происходит изменение индекса, для последующей индексации от этого id.

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

А вот после индексации основного-индекса запоминается максимальный проиндексированный ид, и с него начинает дельта-индекс индексировать данные

Осилил не на 100%, у меня вопрос: как поступать с отредактированными постами. Получается недостаточно знать id "крайнего" сообщения, не лучше ли вместо него использовать таймстамп последнего редактировани?

Да, можно и так, главное сделать выборку в БД данных которые тебе нужны.
зы. сейчас есть real-time индексы

Доброе время суток.
Работаю со Sphinx 2.0.1-beta на Win7 SP1.
В процессе приготовления дельта-индексов возникли вопросы:
1. Ошибка при переиндексации
Смотрим в searchd.log:
[WARNING: rotating index 'ix_name': rename 'C:../ix_name.spd' to 'C:../ix_name.old.spd' failed: Broken pipe]
[WARNING: rotating index 'ix_name': rename to .old failed; using old index]
[rotating finished]
Рещение: sphinx.conf.in => searchd settings => preopen_indexes = 0 (defualt - 1)
2. Параметр seamless_rotate (default - 1)
После переиндексации смотрим в searchd.log:
[rotating indices (seamless=0)]
[rotating index 'ix_name': success]
[rotating finished]
Обнаруживаем что seamless=0, но ведь по умолчанию seamless_rotate=1!
Решение: sphinx.conf.in => searchd settings => seamless_rotate = 1
Прошу прокомментировать если что-то нет так.
Интересно бы узнать назначение preopen_indexes, а так же как лучше обновлять главный индекс: перестроением(--rotate) или слиянием главного с дельтой (--merge)

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

Markdown