Ищем втрое быстрее: мульти-запросы и фасеточный поиск

    В сегодняшней статье расскажу про фичу Sphinx под названием мульти-запросы: встроенные в нее оптимизации, реализацию тн. фасеточного поиска, и вообще как иногда можно с ее помощью сделать поиск втрое быстрее.

    Но сначала 15 секунд политинформации (сам себя не похвалишь, никто не похвалит). В этом году Sphinx прошел во второй тур конкурса Sourceforge Awards 2009 в номинациях SysAdmins и Enterprise (говорят, в номинации Developers не добрали совсем чуть-чуть). Голосование продлится еще неделю (до 20го числа). Кроме рабочего email адреса, ничего не нужно. Заранее спасибо всем, кто не даст нам пропасть!

    И обратно к разработке. Что вообще такое мульти-запросы, и откуда берется обещанное втрое быстрее?

    Мульти-запросы (multi-queries) — это механизм, который позволяет отослать несколько поисковых запросов одним пакетом.

    Методы API, реализующие механизм мульти-запросов, называются AddQuery() и RunQueries(). (Кстати, «обычный» метод Query() внутри использует их же: один раз вызывает AddQuery(), и затем сразу RunQueries()). Метод AddQuery() сохраняет текущее состояние всех настроек запроса, выставленных предыдущими API вызовами, и запоминает запрос. Настройки уже запомненного запроса более меняться не будут, любый вызовы API их не тронут, соотв-но для последующих запросов можно использовать любые другие настройки (другой режим сортировки, другие фильтры, итп). Метод RunQueries() фактически отсылает все запомненные запросы одним пакетом и возвращает несколько результатов. На участвующие в пакете запросы никаких ограничений не накладывается. Количество запросов на всякий случай ограничено директивой max_batch_queries (добавлена в 0.9.10, ранее фиксированным числом 32), но это в общем-то только проверка против битых пакетов.

    Зачем использовать мульти-запросы? Вообще говоря, все сводится к производительности. Во-1х, отсылая запросы к searchd одним пакетом, всегда экономим немножко ресурсов и времени на том, что отсылаем туда-сюда меньше сетевых пакетов. Во-2х, что значительно более важно, searchd получает возможность провести некоторые оптимизации над всем пакетом запросов. Со временем постепенно добавляются новые оптимизации, поэтому имеет смысл всегда, когда можно, отсылать запросы пакетами — тогда при обновлении Sphinx новые пакетные оптимизации включатся полностью автоматически. В случае, когда никаких пакетных оптимизаций применить нельзя, запросы просто будут обработаны по одному, без каких-либо видимых отличий для приложения.

    Зачем (точнее когда) мульти-запросы НЕ использовать? Все запросы в пакете должны быть независимы, но иногда это не так, и запрос Б может зависеть от результатов запроса А. Например, можем хотеть показывать результаты поиска из дополнительного индекса только тогда, когда в главном индексе ничего не нашлось. Или просто выбирать разное смещение во 2й набор результатов в зависимости от количества совпадений в 1м наборе. В таких случаях придется использовать отдельные запросы (или отдельные пакеты).

    Есть две важные пакетные оптимизации, про которые стоит знать: оптимизация общих запросов (доступна начиная с версии 0.9.8), и оптимизация общих поддеревьев (доступн начиная с находящейся в разработке версии 0.9.10).

    Оптимизация общих запросов работает так. searchd выбирает из пакета все запросы, у которых отличаются только настройки сортировки и группровки, а полнотекстовая часть, фильтры итп совпадают — и проводит поиск только один раз. Например, если в пакете 3 запроса, текстовая часть у всех «ipod nano», но 1й запрос выбирает 10 самых дешевых результатов, 2й группирует результаты по ID магазина и сортирует магазины по рейтингу, а 3й запрос просто выбирает максимальную цену, поиск «ipod nano» отработает только один раз, но из его результатов будут построены 3 по-разному отсортированных и сгруппированных отклика.

    Так называемый фасеточный поиск является частным случаем, для которого применима данная оптимизация. В самом деле, его можно реализовать, запустив несколько поисковых запросов с разными настройками: один для основных результатов поиска, еще несколько с таким же поисковым запросом, но разными настройками группировки (top-3 авторов, top-5 магазинов, итп). Когда все, кроме сортировки и группировки одинаковое, оптимизация включается и скорость неплохо растет (пример ниже).

    Оптимизация общих поддеревьев еще более интересная штука. Она позволяет searchd использовать сходства между разными запросами внутри пакета. Внутри всех пришедших отдельных — разных! — полнотекстовых запросов выявляются общие части, и если такие есть, промежуточные результаты расчета кешируются и разделяются между запросами. Например, вот в таком пакете из 3 запросов

    barack obama president
    barack obama john mccain
    barack obama speech
    


    есть общая часть из 2х слов («barack obama»), которую можно для всех трех запросов вычислить ровно один раз и закешировать. Именно этим оптимизация общих поддеревьев и занимается. Максимальный размер кеша на каждую пачку жестко ограничивается директиваи subtree_docs_cache и subtree_hits_cache, так что если общая часть «i am» найдется в ста миллионах документов, память у сервера внезапно таки не кончится.

    Вернемся обратно к оптимизации про общие запросы. Вот пример кода, который запускает один и тот же запрос, но с тремя разными режимами сортировки:
    sorting modes:

    require ( "sphinxapi.php" );
    $cl = new SphinxClient ();
    $cl->SetMatchMode ( SPH_MATCH_EXTENDED2 );
    
    $cl->SetSortMode ( SPH_SORT_RELEVANCE );
    $cl->AddQuery ( "the", "lj" );
    $cl->SetSortMode ( SPH_SORT_EXTENDED, "published desc" );
    $cl->AddQuery ( "the", "lj" );
    $cl->SetSortMode ( SPH_SORT_EXTENDED, "published asc" );
    $cl->AddQuery ( "the", "lj" );
    $res = $cl->RunQueries();
    


    Как узнать, сработала ли оптимизация? Если сработала, в соответствующих строчках лога будет поле с «мультипликатором», который показывает, сколько запросов было обработано вместе:

    [Sun Jul 12 15:18:17.000 2009] 0.040 sec x3 [ext2/0/rel 747541 (0,20)] [lj] the
    [Sun Jul 12 15:18:17.000 2009] 0.040 sec x3 [ext2/0/ext 747541 (0,20)] [lj] the
    [Sun Jul 12 15:18:17.000 2009] 0.040 sec x3 [ext2/0/ext 747541 (0,20)] [lj] the
    


    Обратите внимание на «x3», это именно оно — означает, что запрос был оптимизирован и обработан в числе пакета из 3 запросов (включая этот). Для сравнения, вот так выглядит лог, в котором те же самые запросы были отправлены по одному:

    [Sun Jul 12 15:18:17.062 2009] 0.059 sec [ext2/0/rel 747541 (0,20)] [lj] the
    [Sun Jul 12 15:18:17.156 2009] 0.091 sec [ext2/0/ext 747541 (0,20)] [lj] the
    [Sun Jul 12 15:18:17.250 2009] 0.092 sec [ext2/0/ext 747541 (0,20)] [lj] the
    


    Видно, что время поиск на каждый запрос в случае с мульти-запроса улучшилось от 1.5 до 2.3 раз, в зависимости от режима сортировки. На самом деле, это не предел. Для обоих оптимизаций известны случаи, когда скорость улучшалась в 3 и более раз — причем не на синтетических тестах, а вполне себе в продакшне. Оптимизация общих запросов довольно хорошо ложится на вертикальные поиски по товарам и онлайн магазины, кеш общих поддеревьев соовт-но на data mining запросов; но, разумеется, строго этими областями применимость не ограничивается. Например, можно делать поиск вообще без полнотекстовой части и считать несколько разных отчетов (с разной сортировкой, группировкой итп) по одинаковым данных за один запрос.

    Каких еще оптимизаций можно ожидать в будущем? Зависит от вас. Пока что в долгосрочном плане записана понятная оптимизация про одинаковые запросы с разными наборами фильтров. Знаете другой частый паттерн, котороый можно ловко соптимизировать? Присылайте!
    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 20

    • UFO just landed and posted this here
        0
        Проверил только что, меня по ссылка на SF Awards бросает прямо на голосование. Браузер FF 3.0.11
        • UFO just landed and posted this here
        +1
        В заголовке «втроем».
        Я уж начал придумывать технологии нового поиска сразу трех запросов о_О
          +1
          Самые незаметные ошибки всегда на самом виду.
          Поправил!!!
          0
          Жаль что GPLv2. Придется писать самому =)
            0
            Код весь наш, так что можем поговорить про другие режимы лицензирования.
            sphinxsearch.com/contacts.html
            0
            Куда быстрее?
            Со скоротью поиска в проекте со Sphinx у меня никогда проблем не было, обычно они начинались с индексацией миллионов строк данных с завидной частатой :)
              0
              Ну когда 10 запросов на страницу и миллионы страниц в сутки — бывает хочется быстрее иногда :)
              0
              Восхищаюсь проделываемой вами работой. Использую sphinx в своих проектах и не нарадуюсь.
              P.S. Давно хочу спросить, есть ли какая-то возможность использовать украинскую морфологию? Спасибо.
                0
                Можно зацепить какой-нибудь украинский словарь, видимо.
                Я сам никогда не делал, существуют ли словари, не знаю :(
                  0
                  Хм, понял. Тогда у меня еще вопрос, если позволите. Можно ли на этапе индексации выбирать словарь для проведения морфологии? Т.е. данные в таблице вперемешку на разных языках, но есть поле определяющее язык записи.
                    0
                    Для каждого отдельного индекса можно назначить свой словарь.
                    В пределах индекса выбирать нельзя, словарь ровно один.

                    Соотв-но для перемешанных данных я бы делал несколько источников и индексов.
                    И к каждому привязывал свой словарь.
                      0
                      Спасибо вам большое за помощь, так и поступлю.
              • UFO just landed and posted this here
                  0
                  За этим, наверное, лучше не ко мне.
                  Те. C# я понимаю достаточно, чтобы суметь API портировать когда-нибудь.
                  Но еще не портировал во-1х плюс в бою не использую никак во-2х.
                  А в комментариях (к другим статьям) вроде как отмечались боевые .NET пользователи.
                    0
                    Есть собственная реализации поискового клиента на C#, c поддержкой фич на уровне PHP клиента. Планирую зарелизить его в open source через некоторое время (сейчас идет обкатка на проекте). Как выложу отпишусь
                    • UFO just landed and posted this here

              Only users with full accounts can post comments. Log in, please.