Спрос на магазины функций машинного обучения с низкой задержкой выше, чем когда-либо, но на самом деле внедрение одного в масштабе остается проблемой.Это стало ясно, когда инженеры ShareChat Иван Бурмистров и Андрей Манаков приняли P99 CONF 23 этап, чтобы поделиться тем, как они построили магазин функций ML с низкой задержкой на основе ScyllaDB. Это не чистое тематическое исследование, в котором принятие нового продукта спасает день.Это история «уроков», взгляд на ценность неустанной оптимизации производительности - с некоторыми важными инженерными улочками. Оригинальная реализация системы не соответствовала требованиям к масштабируемости компании.Окончательная цель состояла в том, чтобы поддерживать 1 миллиард функций в секунду, но система потерпела неудачу при нагрузке всего в 1 миллион.С помощью некоторых умных решений проблем команда отключила его.Давайте посмотрим, как их инженерам удалось перейти от первоначальной неудачи к достижению своей высокой цели производительности без масштабирования основной базы данных. Присоединяйтесь к своим коллегам на P99 24 CONF, бесплатной высокотехнической виртуальной конференции на тему «производительность всех вещей». Майкл Стонебракер, создатель Postgres и профессор MIT Брайан Кантрилл, соучредитель и CTO Oxide Computer Avi Kivity, создатель KVM, соучредитель и CTO ScyllaDB Лиз Райс, главный исполнительный директор eBPF Isovalent Энди Павло, профессор CMU Эшли Уильямс, основатель / генеральный директор Axo, бывший ядерный коллектив Rust, основатель Rust Foundation Карл Лерш, Токийский создатель, соавтор Rust и инженер в AWS Зарегистрируйтесь сейчас - это бесплатно Зарегистрируйтесь сейчас - это бесплатно Зарегистрируйтесь сейчас - это бесплатно Помимо еще одного замечательного разговора Ивана из ShareChat, ожидайте более 60 инженерных разговоров об оптимизации производительности в Disney / Hulu, Shopify, Lyft, Uber, Netflix, American Express, Datadog, Grafana, LinkedIn, Google, Oracle, Redis, AWS, ScyllaDB и многое другое. ShareChat: ведущая социальная медиа-платформа Индии Чтобы понять масштабы вызова, важно немного узнать о ShareChat, ведущей платформе социальных сетей в Индии.На приложении ShareChat пользователи открывают и потребляют контент на более чем 15 разных языках, включая видео, изображения, песни и многое другое. Между двумя приложениями они обслуживают быстро растущую базу пользователей, которая уже имеет более 325 миллионов активных пользователей в месяц. Машинное обучение на ShareChat Эта история фокусируется на системе, стоящей за магазинами функций ML для приложения короткого формата Moj. Он предлагает полностью персонализированные потоки примерно 20 миллионам ежедневно активных пользователей, 100 миллионам ежемесячно активных пользователей. Потоки обслуживают 8000 запросов в секунду, и в среднем по каждому запросу ранжируются 2000 кандидатов на контент (например, чтобы найти 10 лучших предложений). Иван Бурмистров, главный инженер по программному обеспечению в ShareChat, объяснил: «Мы вычисляем функции для разных «субъектов». пост — это одна сущность, пользователь — другая и так далее. С точки зрения вычисления, они довольно похожи. Однако, важное различие заключается в количестве функций, которые мы должны получить для каждого типа сущности. Когда пользователь запрашивает питание, мы получаем функции пользователя для этого одного пользователя. Однако, чтобы ранжировать все сообщения, нам нужно брать функции для каждого кандидата (поста), который ранжируется, поэтому общая нагрузка на систему, генерируемая функциями сообщения, намного больше, чем генерируемая функциями пользователя. Что пошло не так Первоначально основное внимание было уделено созданию магазина пользовательских функций в режиме реального времени, потому что в этот момент пользовательские функции были самыми важными.Команда начала создавать магазин функций с этой целью в виду.Но затем приоритеты изменились, а функции для публикации также стали фокусом.Этот сдвиг произошел, потому что команда начала создавать совершенно новую систему ранжирования с двумя основными различиями по сравнению с ее предшественником: Почти в режиме реального времени функции сообщения были более важными Количество статей для ранжирования увеличилось с сотен до тысяч Иван объяснил: «Когда мы пошли тестировать эту новую систему, она потерпела печальный сбой.При примерно 1 млн изображений в секунду система стала нереактивной, задержки прошли через крышу и так далее». В конечном счете, проблема возникла из того, как системная архитектура использовала предварительно агрегированные куски данных, называемые плитками. Например, они могут агрегировать количество понравившихся для сообщения в определенную минуту или другой промежуток времени. Вот высокоуровневый взгляд на системную архитектуру. Есть несколько тем в режиме реального времени с сырыми данными (подобаются, клики и т. Д.). Работа Flink агрегирует их в плитки и пишет их в ScyllaDB. Затем есть сервис функций, который запрашивает плитки из ScyllaDB, агрегирует их и возвращает результаты в сервис потока. Первоначальная схема базы данных и конфигурация плитки привели к проблемам с масштабируемостью. первоначально каждая организация имела свой собственный раздел, при этом строки временной отметки и название функции были упорядочены кластерными столбцами. ]. Плитки были рассчитаны на сегменты одной минуты, 30 минут и одного дня. Запрос на час, один день, семь дней или 30 дней потребовал подбирать около 70 плиток на функцию в среднем. Узнайте больше в этом NoSQL моделирование данных мастер-класс Если вы сделаете математику, становится понятно, почему она потерпела неудачу. Системе нужно было обрабатывать около 22 миллиардов строк в секунду. Первоначальная оптимизация В этот момент команда отправилась на миссию оптимизации. Первоначальная схема базы данных была обновлена, чтобы хранить все строки функций вместе, сериализованные как буферы протокола для определенной временной отметки. Поскольку архитектура уже использовала Apache Flink, переход к новой схеме плотинга был довольно легким, благодаря передовым возможностям Flink в строительстве трубопроводов данных. С этой оптимизацией умножитель «Функции» был удален из вышеуказанного уравнения, а количество необходимых строк для получения было уменьшено на 100X: с примерно 2 миллиардов до 200 миллионов строк/сек. Команда также оптимизировала конфигурацию плитки, добавив дополнительные плитки на пять минут, три часа и пять дней до одной минуты, 30 минут и один день плитки. Чтобы справиться с большим количеством строк/секунд на стороне базы данных, они изменили стратегию сжатия ScyllaDB с постепенной до нивелированной. Этот вариант лучше соответствовал их шаблонам запросов, сохраняя соответствующие строки вместе и уменьшая чтение I/O. Результат: мощность ScyllaDB эффективно удвоилась. Узнайте больше о стратегии компоновки Самым простым способом удовлетворить оставшуюся нагрузку было бы масштабировать ScyllaDB в 4 раза. Тем не менее, более/большие кластеры увеличивали бы затраты, и это просто не входило в их бюджет. Улучшенная локализация кэша Одним из возможных способов уменьшить нагрузку на ScyllaDB было улучшение локальной частоты ударов кэша, поэтому команда решила исследовать, как это может быть достигнуто. Очевидным выбором было использование последовательного метода хеширования, хорошо известной методики направления запроса к определенной реплике от клиента на основе некоторой информации о запросе. Поскольку команда использовала NGINX Ingress в настройке Kubernetes, использование возможностей NGINX для последовательного хеширования казалось естественным выбором. По документации NGINX Ingress, установка последовательного хеширования была бы столь же простой, как добавление трех строк кода. Эта простая конфигурация не работала. Клиентский подкомплект привел к огромному перезагрузке ключей – до 100% в худшем случае.Поскольку ключи узлов можно изменить в хеш-ринге, было невозможно использовать реальные сценарии с автоскалированием. Было сложно предоставить хэш-значение для запроса, потому что Ingress не поддерживает самое очевидное решение: заголовок gRPC. Латентность сильно ухудшилась, и было неясно, что вызвало латентность хвоста. Чтобы поддержать подгруппу подгрупп, команда изменила их подход. Они создали двухступенчатую хеш-функцию: сначала хешируя субъект, а затем добавляя случайный префикс. Это распределило субъект по желаемому количеству подгрупп. Теоретически этот подход может вызвать столкновение, когда субъект несколько раз перемещается на тот же субъект. Однако риск низкий, учитывая большое количество реплик. Ingress не поддерживает использование заголовка gRPC в качестве переменной, но команда нашла выход: использование перезаписи пути и предоставление необходимого хеш-ключа в самом пути. К сожалению, выявление причины ухудшения задержки потребовало бы значительного времени, а также улучшения наблюдаемости. Чтобы удовлетворить этот срок, команда разделила сервис Feature на 27 различных сервисов и вручную разделила все субъекты между ними на клиента. Это был не самый элегантный подход, но он был простым и практичным – и он добился отличных результатов. Скорость ударов в кэше улучшилась до 95% и нагрузка ScyllaDB была уменьшена до 18,4 миллионов строк в секунду. Тем не менее, этот подход к разделению развертываний «старой школы» по-прежнему не был идеальным дизайном. Поддержание 27 развертываний было скучным и неэффективным. Плюс к этому, скорость удара кэша не была стабильной, а масштабирование ограничивалось тем, что в каждом развертывании нужно было поддерживать высокое минимальное количество подкастов. Следующий этап оптимизации: последовательное хаширование, сервис функций Готовы к еще одному кругу оптимизации, команда пересмотрела последовательный подход к хашированию с использованием страницы, называемой Envoy Proxy, развернутой с функцией службы. Envoy Proxy обеспечивал лучшую наблюдаемость, которая помогла выявить проблему с хвостом задержки. Проблема: различные шаблоны запросов к службе Feature вызвали огромную нагрузку на слой и кеш gRPC. Затем команда оптимизировала сервис Feature. Формированная библиотека кэширования (FastCache от VictoriaMetrics) и внедренные серийные письма и улучшенное выдворение, чтобы уменьшить контрит мутекса в 100 раз. Формованный gprc-go и реализованный буферный бассейн через различные соединения, чтобы избежать споров во время высокого параллелизма. Используются параметры объединения объектов и настройки коллектора мусора (GC) для снижения скорости распределения и циклов GC. С Envoy Proxy, обрабатывающим 15% трафика в своем доказательстве концепции, результаты были многообещающими: показатель ударов кэша в 98%, что уменьшило нагрузку на ScyllaDB до 7,4 млн строк в секунду. Уроки выучены Вот как выглядело это путешествие с точки зрения временной линии: В заключение Андрей подвел итоги лучших уроков, которые команда извлекла из этого проекта (до сих пор): Несмотря на то, что команда ShareChat резко изменила дизайн своей системы, ScyllaDB, Apache Flink и VictoriaMetrics продолжали хорошо работать. Каждая оптимизация сложнее предыдущей и имеет меньшее влияние. Простые и практичные решения (например, разделение магазина функций на 27 развертываний) действительно работают. Решение, которое обеспечивает лучшую производительность, не всегда удобное для пользователя. Например, их пересмотренная схема базы данных дает хорошую производительность, но ее трудно поддерживать и понимать. Каждая система уникальна.Иногда вам может понадобиться перевернуть библиотеку по умолчанию и настроить ее для вашей конкретной системы, чтобы получить наилучшую производительность. Смотрите полный разговор P99 CONF! Смотрите полный разговор P99 CONF! Смотрите полный разговор P99 CONF! О компании Cynthia Dunlop Синтия является старшим директором по стратегии контента в ScyllaDB. Она пишет о разработке программного обеспечения и инженерии качества более 20 лет.