Думаю, про такой замечательный поисковый движок Sphinx слышали все или почти все. Наверняка многие уже применяют его, для поиска по сайту, для выборки похожих статей, новостей, товаров и т.д. Он отлично справляется с поисковыми задачами даже на очень больших количествах записей. Но, в своей статье я хочу рассказать про использование Sphinx не для поиска.
Так получилось, что я продолжил разработку сайта агрегатора новостей. Когда я приступил к нему — он «держал» в базе 40 — 60 тыс. новостей и жутко тормозил. Переписав все с нуля, переведя часть сайта на статику и введя кэширование удалось увеличить количество новостей, с которыми справлялся сайт, приблизительно до 500 тыс. Конечно же, для поиска по новостям я применил Sphinx, так как слышал много восторженых отзывов о нем от коллег. Надо сказать и меня он не разочаровал.
И вот, в один не такой уж прекрасный день, руководство поставило следующую задачу: показывать по одной последней новости из каждого источника. Говоря языком SQL — это сортировка по дате и группировка по источнику. Попытка реализовать соответствующую выборку из базы привела к тому, что сайт стал не справляться с нагрузкой.
Оказалось, что MySQL сначала выполняет группировку (при этом берет первую попавшуюся запись в таблице) и только потом сортирует выбранные записи. Решить проблемму «sort before group» можно либо с помощью подзапроса, либо испольуя агрегирующую функцию MAX(). А это — использование временной таблицы и очень медленно работающие запросы…
Альтернативным решением было использование возможностей Sphinx по группировке результатов. Прочитав документацию, я решил попробовать заменить MySQL в задаче выборки новостей для отображения на сайте на Sphinx.
Для этого пришлось установить сортировку по дате, группировку по числовому полю ID Источника, а в качестве запроса — имя категории новостей. Запрос выглядит следующим образом:
$sphinx = new SphinxClient();
$sphinx->SetServer('localhost', 3312);
$sphinx->SetMatchMode(SPH_MATCH_EXTENDED2);
$sphinx->SetSortMode(SPH_SORT_ATTR_DESC, 'date');
$sphinx->SetLimits($from,$count);
$sphinx->SetGroupBy('site_id', SPH_GROUPBY_ATTR, 'date desc');
$category = "@category_path {$cat['category_path']}";
$result = $sphinx->Query($category, 'news news_delta');
Результат превысил все мои ожидания! Не могу привести точные времена выполнения запросов с помощью MySQL и с помощью Sphinx, так как на радостях забыл записать их. Но скажу, что теперь у нас в базе более двух миллионов новостей и все работает достаточно шустро.
Для поддержки индекса в актуальном состоянии используется дельта-индекс, обновлемый каждые 10 минут, и основной индекс, обновляемый ежесуточно. Разгрузка MySQL помогла избежать блокировок таблиц во время выполнения длительных запросов, MySQL используется только для выборки данных по ID, которая происходит очень быстро.