Загадки «Сфинкса»

топ 100 блогов Bolk site — 18.09.2013 Сейчас много текста будет, напрягитесь.

«Сфинкс» — это довольно известный поисковый движок, разработанный Андреем Аксёновым. Время от времени мне приходилось использовать его в своих проектах и каждый раз качеством получившегося решения я был доволен, тем более, что за проходившее время он обрастал новыми интересными возможностями.

В первый раз сталкиваюсь с проектом, где использование «Сфинкса» возможно только с некоторым количеством костылей, которыми решение обрастает и меня всё меньше устраивает получившееся. Хочу описать проблемы, с которыми я столкнулся, возможно кому-то в момент выбора поисковой технологии это будет небезынтересно.

«Сфинкс» хорош поиском по одной сущности, это, пожалуй всё. Богатство, описанное документации может создать обманчивое ощущение, что он может больше, но это не так. Моей ошибкой было решение использовать его в поиске по связанным сущностям. Это то, что «Сфинкс» умеет плохо. И хотя то, что получилось работает по скорости выше предыдущего решение, прокручивая в голове исходный код, я пла́чу мысленными кровавами слезами.

Так получилось, что искать мне нужно по наборам сущностей, связанных между собой. Получилось аж пять индексов. Объём данных не даёт надежды упихать всё в плоскую структуру — избыточность ужасает. Например, есть документы (Д) и резолюции к ним (Р), отношение один ко многим. Если попробовать положить эту структуру в одну таблицу, избыточность будет на уровне Д×Р. Причём «Д» на уровне миллионов. И это только две сущности. Немаловажно, что «Д» и «Р» — сущности с целой кучей полей, в том числе полнотекстовых.

Выход есть. В поиске мы смотрим по каким сущностям надо искать, ищем по первой, берём оттуда найденные идентификаторы, подставляем во второй поисковый запрос вместе с критериями и так далее. Это костыль номер раз. Форма поиска устроена таким образом, что использование любого фильтра, как правило, ограничивает количество данных в выборке очень сильно, и всё равно это тысячи айдишников.

Второй костыль нужен, чтобы обойти принципиальное ограничение «Сфинкса» — у него есть параметр (указывается в конфиге), ограничивающий максимальное количество возвращаемых данных. Причём в миллионы (наш объём) его поставить нельзя — максимум в десятки тысяч, не рассчитан «Сфинкс» на большее, да и памяти не напасёшься. Всё бы ничего, но когда мы ищем пересечения сущностей (Д×Р), то, что нам вернул «Сфинкс» со своим лимитом, может и не иметь пересечений — они могут быть за горизонтом выдаваемых данных.

Костыль номер два я походя описывал — если мы обнаруживаем, что айдишники вернулись не все, то делаются ещё запросы, которые я назвал «докачивающими», делается тот же самый поисковый запрос, за исключением айдишников, которые уже попали. Работает, естественно, только с данными отсортироваными по ID.

Как только появились «докачивающие» запросы, то минус один выходной ушёл у меня на костыль номер три — я сделал сбор статистики запросов и оптимизацию порядка выполнения запросов. Вот как всё работает.

У нас, как я уже сказал, может быть от одного до пяти задействованных индексов, в зависимости от того что пользователь в форму введёт. В запрос к каждому следующему передаются идентификаторы из предыдущего. Как только один из запросов вернул пустоту, дальше можно не продолжать — пересечение с пустотой даёт пустоту.

Теперь следите за мыслью. Не знаю очевидно или нет, но логически всё равно в каком порядке будут опрашиваться индексы — результат будет тот же. Например, Р ∩ Д = Д ∩ Р. Зато если впереди поставить запросы, которые вернут меньше айдишников, следующим будет полегче. Скажем, на запрос пользователя у нас вернулось 80 тысяч документов и пять тысяч резолюций. Расточительнее сделать запрос к документам первым и подставить потом 80 тысяч айдишников во второй запрос, чем выбрать сначала пять тысяч резолюций, а потом передать их в запрос к документам.

Но как узнать какой запрос надо выполнить первым? Поможет сбор статистики.

Я рассуждал так. Поскольку у нас в поисковой форме все атрибуты запрашиваются с критерием «И» (например: год=2011 И организация=5), то чем больше разнообразных критериев запрошено, тем меньше объём выборки, значит надо как минимум запомнить какие атрибуты интересовали пользователя, тем более, что они по-разному влияют на объём получающейся выборки. Значения у атрибута ищутся с критерием «ИЛИ» (например: год=2011 ИЛИ 2012), чем больше указано значений атрибута, тем больше результатов вернётся, значит имеет смысл запоминать сколько значений атрибута указано.

В итоге при каждом поиске пользователя я кладу его запрос в специальный стек, плюс указываю какие атрибуты в каком количестве он запрашивал. Эта информация может выглядеть как-то так: год=3, организация=1, авторы=4. Значит, что использовалось три атрибута для поиска, причём пользователь в графе «год» указал три каких-то года, выбрал одну какую-то организацию и четыре каких-то автора. Айдишники из предыдущего запроса не учитываются, они мне не нужны.

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

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

Массовых испытаний ещё не было, но в тех частных случаях, которые я рассмотрел, ускорение бывает до двух раз (чаще всего, конечно, прирост не столь значителен или его нет). Больше всего выигрыш получается, если удаётся избавиться от «докачивающих» запросов.

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

Загадки «Сфинкса»

Загадки «Сфинкса»

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

Архив записей в блогах:
Совершенно тупое название для поста, а для статьи, думаю, вполне сгодится. Хорошо, что я умею писать и хоть немноожечко анализировать. Со вступлениями и заключениями у меня, правда, обычно туго, но я постараюсь. Во первых строках своего письма ...
Этой новости украинские СМИ пока не уделили должного внимания. Возможно, сейчас происходит оценка, а заодно и выработка единой информационной стратегии её освещения. Объявленный 13 октября отказ российской стороны (конкретно АК "Транснефтепродукт") от дальнейшего использования ...
Накидайте, плиз, хороших мемуаров танкистов. Пока скачал только "Тигры в грязи" немца какого-то. Хочется мемуаров немецких, советских, союзных танкистов и танковых генералов. Немцефилией, советопоклонением и пиндосодрочерством не страдаю, хочу почитать ...
Президент ФРГ призвал Россию к покаянию . Кульминацией германо-российских "Потсдамских встреч" в этом году стала беседа их участников с президентом ФРГ. Во дворце Bellevue состоялся острый обмен мнениями, за которым наблюдал и корреспондент DW Никита Жолквер. В России, по словам през ...
Приму в дар стол или куплю недорого.icq 365-497-660к.т. ...