✨ Как мы экономим сотни тысяч CPU-ядер в РекламеРекомендательный движок Яндекса обрабатывает свыше миллиона запросов в секунду. В наших масштабах даже 1% экономии в конкретном сервисе превращается в тысячи процессорных ядер. А за последние три года мы ежегодно высвобождали аж по 200 000 единиц.
На связи Антон Полднев, руководитель инфраструктуры Яндекс Рекламы. Сегодня я расскажу, как нам удалось добиться этого результата и какие решения лежат под капотом нашего движка рекомендаций.5–6 лет назад наша система была единым монолитом с шардированием по данным. При пиковой нагрузке мы могли только отключать шарды, а это вело к финансовым потерям. Мы оптимизировали код по флеймграфам и внедряли простые ML-модели, чтобы отсекать кандидатов, но достигли локального оптимума. В итоге негибкая архитектура не выдерживала нагрузки.
Тогда мы сформулировали три принципа новой архитектуры:♋️ Прогноз профита
Лёгкая ML-модель оценивает на старте, может ли пользователя заинтересовать какая-либо реклама. Если нет — мы избегаем сложных вычислений и сохраняем ресурсы.
♋️ Генерация кандидатов
Вместо алгоритмического подбора по ключевым словам мы перешли к методам поиска ближайших соседей. Нейросеть сопоставляет пользователей и объявления в едином векторном пространстве, что повышает и эффективность, и качество.
♋️ Трёхстадийное ранжирование
Сначала кандидатов отфильтровывает лёгкая модель, а потом тяжёлая. Так у нас появляется возможность использовать сложные алгоритмы без роста нагрузки.
Когда мы заявили, что средняя утилизация сервисов в 30–40% — это не предел, а повод для основательной оптимизации, нам не верили. Дескать, при большей нагрузке не избежать серьёзной деградации.Мы изменили подход:
🟢 Умная деградация
Мы перенастроили систему шедулинга, чтобы деградация происходила по длине очереди, а не по времени обработки уже взятых задач.
🟢 Избирательное отсечение
С помощью PID-контроллера мы в реальном времени регулируем порог отсечения запросов с низким профитом. Это даёт возможность «прореживать» очередь в пиках и жертвовать 1% качества, чтобы экономить 15% нагрузки.
🟢 Умная балансировка
Мы научились учитывать гетерогенность железа и направлять больше запросов на более производительные машины, чтобы снизить дисперсию.
В результате нам удалось поднять утилизацию и изъять 10–20% лишних ресурсов из сервисов.
От Protobuf к собственному форматуПерекладывание данных — тихий убийца производительности. Мы прошли путь от ручной сериализации через Protobuf к FlatBuffers. Однако операции чтения оставались заметно более дорогими по сравнению с чтением полей из простых структур.
Мы создали собственный формат YAFF, который объединил лучшие черты Protobuf (схему) и FlatBuffers (подход к доступу), и оптимизировали его для современных процессоров. В итоге получили двукратное улучшение CPU time, а размер сообщений сократился на треть.
Микросервисы, которые экономятПринято считать, что разделение монолита — это компромисс между скоростью разработки и эффективностью. Мы доказали обратное. Вынос логики «быстрых счётчиков» в отдельный микросервис позволил создать в нём специализированный кеш под наши паттерны доступа (например, с группировкой по экспериментам, а не по кампаниям).
Это улучшило локальность данных и преобразовало CPU-bound-задачу в memory-bound, что в совокупности позволяет экономить CPU.
Наш опыт демонстрирует, что экономия на масштабе — это не только оптимизация алгоритмов. Ещё это:
🟢 Умная деградация, которая позволяет повышать утилизацию
🟢 Борьба с накладными расходами на каждом уровне, особенно при сериализации
🟢 Стратегическое применение микросервисов для улучшения локальности данных
🟢 Постоянный мониторинг с использованием перфоратора, PGO-оптимизация и нагрузочное тестирование
Кстати, с этой темой Антон выступал на конференции «Я про бэкенд». Доклад можно посмотреть на платформах:➖
Ютуб➖
VK ВидеоПрезентацию выступления Антона прикрепляем ниже 🔽
Больше записей докладов с конференции собрали для вас в плейлистах:➖
Ютуб➖
VK ВидеоПодписывайтесь: 💬
@Yandex4Backend📹
@YandexforBackend