Как стать автором
Обновить

Как мы делали свой поиск в Ozon: эволюция архитектуры от SQL до O2

Время на прочтение16 мин
Количество просмотров25K
Всего голосов 56: ↑56 и ↓0+56
Комментарии25

Комментарии 25

Привет. Примерно год. С этим можно жить, но есть некоторые неудобства, типа:

  • Сложности при изменении топологии

  • Ручная очистка pvc

  • Параллельность раскатки через костыли в виде множества маленьких sts

И так далее.

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

Дополню Дениса.

Сам кубер достаточно давно, года 3-4, sts в кубере используем полтора года.

Основные недоубства, которые описал Денис, вызваны необходимостью использовать local storage, остальные решения не устраивают по перфу.

Kubernetes в Ozon появился несколько лет назад. Большинство прикладных сервисов (как например search-api) переехали в него сразу в рамках программы реновации. Инфраструктурные сервисы (как например elastic) обычно живут вне кубера, их администрируют специальные люди, разработчики доступов не имеют.

Поисковый движок не похож на типичный прикладной сервис из-за высоких нагрузок и довольно большой инсталляции (сейчас мы самые большие в инфраструктуре Ozon). Базовый поиск и мастер живут на собственных железках без соседей. Иногда нам задают вопрос "зачем вам вообще кубер?". Ответ банальный — выгоднее использовать стандартные механизмы (пока они нас устраивают), поддерживаемые командой платформы, и не тратить силы на инфру и CI/CD.

Хорошая статья, спасибо

У меня всегда к озону был вопрос в релевантности поиска.

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

Например, артикул 157e6b(ищем это)

Результат в выдаче озона показывает нам всякое, но только не искомое)

Это артикул производителя моторного масла кастрол, в банке 4 литра. При загрузке товара на озон этот параметр (артикул производителя) был вроде раньше обязателен к заполнению, я полагаю поиск должен быть произведён и по нему тоже? Но почему-то в выдаче поисковой такого товара нет, хотя товар наверняка есть)

Ушло некоторое время, чтобы разобраться. Нужный товар в выдаче есть (у меня в районе 15й позиции). Основной вопрос - почему же он не на первом месте.

Занятно, что L1 ранжирование (текстовая релевантность) даёт этому товару самый высокий score. Т.е. ML всё испортил :D. Основная проблема в том, что по запросу очень мало статистики, и модель начала смотреть на общие характеристики: цену (она у данного товара одна из самых высоких по запросу), отзывы, скорость доставки. В сумме накопилось достаточно, чтобы опустить его вниз.

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

Если будут ещё интересные примеры, можно присылать мне в телеграм.

Целый Ебей работает на эластике , разделение только по языкам.

Почему вы такие особенные и уникальные что позволили себе такую огромную сложную вондервафлю запилить?

Вопрос содержит две части: почему для ebay подходит elastic, и почему мы решили уйти с elastic на o2.

На вторую часть мне ответить проще. Некоторые причины я уже назвал в статье — отделение фазы L2 ранжирования от фазы поиска и L1 ранжирования, желание уметь оптимизироваться на любых уровнях и иметь доступ к коду. Даже во времена elastic мы использовали собственный fork, т.е. необходимость тюнить стандартные компоненты у нас была давно.

Что такого необычного именно в поиске Ozon? Один из примеров — определение геодоступности для товаров в выдаче: товары лежат на разных складах, которые подключены к разным логистическим провайдерам (Ozon Логистика, СДЭК, Почта России и т.д.), у каждого провайдера своя зона покрытия. В момент исполнения поискового запроса нужно определять какие товары доступны покупателю, а какие нет. Другой пример — необходимость при определённых условиях объединять разные товары в одну плитку (например iphone 12 red в вариантах 64gb и 128gb индексируются как отдельные документы, но на выдаче объединяются в одну карточку).

На первую часть вопроса мне ответить сложнее, т.к. мне неизвестна внутренняя кухня ebay. Я вообще до появления этого комментария считал, что ebay живёт на собственном решении, т.к. в их статьях (пример) об elastic упоминаний нет. Учитывая, что ebay основан в 1995, позволю себе предположить, что они долгое время использовали не elastic (появился после 2010). Если переход на elastic и правда произошёл, то причин можно придумать множество, вплоть до банальных — были сложности с собственным поиском и CTO принял волевое решение. В общем, фантазировать не буду.

Просьба не по теме поиска, но близкая. А можно в конце концов сделать нормальные фильтры? Озон стал самым неудобным в этом плане. Поиск обычно используется чтобы быстро найти некую категорию товаров, а дальше уже сужаешь перечень фильтрацией. А озон полагается только на поиск.

Для нормальной фильтрации нужно как минимум:

  • иметь возможность выделить несколько критериев в списке. Пробовали выбрать сразу несколько брендов в фильтре? Там после каждого выбора страница непременно обновляется. Чтобы выделить 3 из 30 нужно мучительно долго ждать и много тыкать мышкой. В некоторых случая несколько критериев вообще не выбираются. Грубо говоря, если нужно выбрать и товары с характеристикой А1 или с А2, фильтр считает что мне нужны только модели сочетающие в себе А1 и А2. Это не должно быть сложно в реализации.

  • для каждой категории нужно продумывать свои фильтры. Это уже сложно, большой объем работ. Сейчас каталог озон это помойка. Но разгребать то ее нужно.

У меня нет хорошего ответа, но я прокомментирую ситуацию, чтобы вопрос не повис в воздухе:

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

  • Все фильтры должны работать по логике ИЛИ (boolean OR). Мне известна обратная ситуация, когда тематика фильтра такова, что в нём хочется ожидать AND логики, но срабатывает стандартная OR. Пришлите пожалуйста конкретные примеры, это поможет разобраться действительно ли есть проблема или где-то недопонимание.

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

Проблема с постоянной перерисовкой фильтров при выборе нескольких значений действительно существует на web версии (в приложениях виджет фильтра обновляется быстро и никого не расстраивает)

Вы не правы. Расстраивает чуть менее чем каждого.

Вот допустим я ищу Гитары. Озон мне предлагает на выбор список брендов. По умолчанию видны только популярные бренды. Но я хитрый, мне нужны особые бренды, я раскрываю полный список из 100500 брендов и начинаю отмечать галочкой. И после первого же клика срабатывает поиск и идет перерисовка страницы поиска и фильтров. Теперь чтобы добавить еще бренд для поиска мне опять надо раскрыть весь список и еще найти то место, где я остановился с выбором. Чтобы выбрать несколько штук - нужно каждый раз испытывать эту боль.

В чем проблема увеличить задержку срабатывания? или добавить явную кнопку для начала поиска? Посмотрите на ui/ux поиска в ДНС - это прямо идеал. Но вы сами с усами, да.

Другой неочевидный момент поиска - это сортировка "Высокий рейтинг". Как оно работает? Это сортировка по полю "средняя оценка" видимого в карточке продукта или по какому-то набору полей? Потому что поиск "Высокий рейтинг" вполне может выдать вначале продукты с оценкой 4 звезды, а потом уже продукты с оценкой 5. И мне как рядовому пользователю это совсем неочевидно.

Вы тоже пишете о web версии — там действительно происходит перерисовка странице при клике. Эту проблему мы приняли и будем исправлять. В приложениях (ios, android) можно выбрать несколько брендов из полного списка, в конце нужно нажимать кнопку Применить, — моргания не будет.

Фильтр "Высокий рейтинг" сортирует по взвешенной сумме отзывов на товар. Формула напоминает рейтинг IMDB для фильмов, пример можно посмотреть тут: https://stackoverflow.com/questions/1411199/what-is-a-better-way-to-sort-by-a-5-star-rating/1411268#1411268. По ситуации, где товары с 4 звездами находятся выше товаров с 5 звездами — пришлите пожалуйста пример, разберемся и ответим.

Интересно, в какой момент Solr сдал позиции Elastic-у? И что было причиной?

Куда ни пойдешь, везде Elasticsearch.

Есть хороший доклад одноклассников почему они запили свой поисковой движок: https://youtu.be/7UAR1Vl_5Iw . Там в частности Solr разбирают. Solr рассчитан на маленькие нагрузки и удобный деплой в облаках. Скажем Solr запросы обрабатывает в один поток. Это обеспечивает высокую эффективность при маленьких нагрузках и большинству проектов этого достаточно.

Правильно ли я понимаю, что вы пожертвовали корректностью работы пагинации и сортировок (не по популярности) на больших оффсетах?

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

Отвечу про анализаторы.

Анализаторы у нас примерно также сделаны как в elastic search (и как в любом поисковом движке на базе lucene, да и не только в нем). Есть цепочка фильтров, среди которых замены, нормализация, синонимы, морфология/стемминг, выкидывание стоп слов и так далее. Тут нет какого-то rocket science, просто аккуратная кропотливая работа.

Много вопросов разной тематики смешалось, отвечу по порядку:

Про корректность сортировок и пагинации: влияние несинхронного обновления индексов на серверах o2-base на консистентность минимальна, т.к. один раз получив ответ от движка, мы сразу кешируем наперёд несколько страниц. Этот трюк в своё время заметно нарастил нам hit rate кешей. Но в вопросе есть уточнение про большие оффсеты. Это верное замечание  — когда мы подбираемся к концу окна LTR рескоринга (сейчас это 2000й документ, т.е. примерно 55 страниц) пагинация на границе перестаёт быть консистентной, т.к. ML ранжирование заканчивается. Но это только в сортировке по популярности, в других сортировках такой проблемы нет. В целом мы об этом не переживаем, т.к. настоящие покупатели так далеко выдачу не скроллят.

Как устроен анализатор: мы используем jmorphy и доделываем его под себя. У нас есть синонимы, специальные правила и морфология, всё как обычно. Контент селлеры заполняют неидеально, это правда. В этом направлении мы тоже копаем, т.к. от качества контента зависят целевые метрики поиска.

Про тестирование ранжирования: сейчас мы опираемся только на метрики, оффлайн и онлайн. Специальных тестов на искуственных примерах у нас нет. Про чёрный ящик: интерпретация работы ML это сложная тема. У нас есть внутренний инструмент Explainer поискового ранжирования — он для товаров в выдаче показывает значения фичей и финальные скоры, можно разобраться как повлияли разные факторы и сделать выводы. Сейчас делаем упрощенную версию Explainer для селлеров, чтобы они могли анализировать выдачу и предпринимать какие-то действия для продвижения своих товаров.

Большое спасибо за отличную статью и комментарии! А вы пробовали делать смысловой поиск, не только по термам, но и по похожести эмбеддингов документов и запроса/профиля соискателя? И ещё вопрос, есть ли у вас аннотационные признаки, если да, то как устроен их пайплайн и храните ли вы их прямо в индексе?

Про смысловой поиск: в Lucene 9 появилась реализация ANN алгоритма — HNSW (хотя судя по док-ии буквы H там пока нет). Мы её попробовали в тестовом режиме и убедились, что на семпле нашего индекса всё работает, действительно можно найти близкие по смыслу запроса товары. Но в проде у нас пока ничего на эту тему нет, т.к. O2 ещё живёт на Lucene 8. DSSM сейчас есть только в L2 ранжировании.

Про аннотационные признаки: в проде сейчас ничего на эту тему нет. Есть желание затащить в документ ключевые поисковые запросы для товара (с их статистикой) и, возможно, отзывы. Коллеги из поиска Mail рассказывали, что в своё время получили какие-то профиты от обогащения документа текстами из близких по смыслу документов. Пайплайны доставки фичей у нас один — DS команда готовит признаки на хадупе, дальше они либо попадают в документы при индексации (если храним эти признаки в индексе), либо через кафку заливаются в L2 feature store (redis).

Статья помогла немного глубже погрузиться в то, что у поиска под капотом. Спасибо.

Вопрос: Вы упомянули в конце статьи про персонализацию, а как работает у вас персонализация для пользователей без регистрации/авторизации и на новом устройстве? Есть ли она?

В текущей версии персонализация поисковой выдачи есть только для залогиненных покупателей – т.к. ML фичи опираются на пользовательскую активность в прошлом. Смена устройства не влияет на наличие статистики по пользователю, но операционную систему устройства можно использовать как ML фичу. Разумеется не так, что "если зашёл с айфона, то цены выше", а просто предоставить модели решать, что делать. Например на web версии покупатели склонны дольше рассматривать товары на большом экране.
Когда мы закончим с самыми перспективными фичами персонализации из беклога, посмотрим как можно распространять статистику на незалогиненных покупателей.

В поиске есть свой язык для уточнения запросов? Например чтобы "вода -минеральная" исключал товары содержащие слово "минеральная"? Или кавычки для поиска точного совпадения.

У нас нет поддержки подобных фичей. В первую очередь потому, что никогда не было подобных запросов (feature request) от пользователей. Для yandex/google подобное имеет смысл, т.к. поиск это единственный и основной продукт, а для ecommerce, на мой взгляд, это совершенно невостребованно.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий