Сервис развивается: тестируем формат, собираем идеи, улучшаем сервис. Есть идеи? Написать
Дайджесты новостей
Эволюция защиты СУБД от перегрузок в Uber: от статических квот к адаптивному управлению Cinnamon

Эволюция защиты СУБД от перегрузок в Uber: от статических квот к адаптивному управлению Cinnamon

Инфраструктура Uber обслуживает более 170 миллионов активных пользователей ежемесячно. Поездки, расчет маршрутов, курьерская доставка и обработка финансовых транзакций генерируют десятки миллионов запросов в секунду к внутренним хранилищам. Основной объем данных — десятки петабайт — сосредоточен в двух специализированных распределенных базах данных: Docstore (транзакционная СУБД общего назначения) и Schemaless (масштабируемое append-only хранилище с неизменяемой историей версий).

Под капотом обе системы построены поверх кластеров реляционной СУБД MySQL с быстрыми накопителями NVMe SSD. Данные разбиты на партиции, каждая из которых объединена в группу репликации под управлением алгоритма консенсуса Raft: один пишущий узел-лидер и два ведомых узла (followers). Архитектурно система разделена на два слоя: stateless query engine, отвечающий за синтаксический анализ, планирование и маршрутизацию запросов, и stateful storage engine, непосредственно исполняющий транзакции, управляющий пулами соединений к MySQL и гарантирующий консенсус репликации.

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

Провал статических квот на уровне шлюзов

На раннем этапе развития защиту пытались реализовать стандартным индустриальным методом — ограничением частоты запросов (rate limiting) с использованием алгоритма маркерной корзины (token bucket) на внешнем шлюзе. В stateless query engine была развернута система учета условных единиц емкости (capacity units) с хранением счетчиков в распределенном кластере Redis.

Эта схема быстро продемонстрировала фундаментальные изъяны. Во-первых, сетевые накладные расходы: каждый входящий вызов требовал дополнительного обращения к Redis, что увеличивало задержку и создавало критическую единую точку отказа. Во-вторых, централизованный шлюз не мог обладать актуальной картиной состояния тысяч независимых узлов хранения.

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

Внедрение очередей CoDel и переход от FIFO к адаптивному LIFO

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

Чтобы разорвать этот замкнутый круг, инженеры обратились к алгоритму CoDel (Controlled Delay), изначально разработанному для борьбы с переполнением буферов в сетевых маршрутизаторах. Запросы разделили на независимые очереди по типу операций:

  • Очередь Read — легковесные точечные чтения по индексам;
  • Очередь Write — вставки, обновления и удаление строк;
  • Очередь Slow — тяжелые аналитические сканирования диапазонов и внутренние фоновые процессы репликации.

Ключевым новшеством CoDel стало переключение дисциплины обслуживания. В нормальном режиме очередь работает по привычному принципу FIFO (первым пришел — первым ушел). Однако как только задержка ожидания в очереди превышает заданный порог, очередь переключается на адаптивный LIFO (последним пришел — первым ушел). Свежие запросы из головы очереди немедленно берутся в обработку и успевают выполниться в рамках клиентского таймаута, тогда как старые, безнадежно опоздавшие запросы отбрасываются с быстрой ошибкой (fail-fast).

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

Контроллер Cinnamon: обратная связь по PID и шесть уровней приоритета

Для решения проблемы избирательного сброса трафика инженеры Uber спроектировали Cinnamon — контроллер адаптивного управления нагрузкой, построенный на математическом аппарате пропорционально-интегрально-дифференцирующего (ПИД) регулирования.

Схема адаптивного регулирования нагрузки Cinnamon: ПИД-контроллер и шесть уровней приоритета

Вместо попыток угадать жесткие лимиты QPS Cinnamon опирается на закон Литтла: конкурентность (concurrency) равна произведению пропускной способности системы на среднюю задержку. Конкурентность и время ожидания в очереди отражают реальную утилизацию ресурсов узла. Контроллер непрерывно отслеживает задержку на 90-м перцентиле (p90 latency) и процент внутренних ошибок, динамически корректируя допустимый таймаут нахождения в очереди и лимит одновременно исполняемых запросов (inflight limit).

Весь входящий трафик в Cinnamon разбит на шесть строгих уровней приоритета:

  • Уровень t0 — критические инфраструктурные операции минимального объема;
  • Уровень t1 — важнейший пользовательский трафик в реальном времени (вызов машины, навигация, транзакции);
  • Уровни t2 и t3 — регулярные интерактивные операции приложений;
  • Уровень t4 — фоновые синхронизации и сервисные задачи;
  • Уровень t5 — аналитические выборки, пакетная обработка и генерация отчетов.

При фиксации признаков перегрузки Cinnamon начинает поэтапно сбрасывать запросы с наименее приоритетных уровней. В первую очередь полностью отсекаются вызовы уровней t5 и t4. Благодаря этому пользовательский трафик уровней t0 и t1 сохраняет полную доступность и низкую задержку. С появлением приоритетов отпала необходимость в отдельной очереди Slow: долгие задачи просто маркируются низким приоритетом и обрабатываются в общих пулах без риска заблокировать интерактивный поток.

Учет консенсуса Raft и защита от «шумного соседа»

В распределенной системе локального состояния отдельного сервера недостаточно для оценки общей устойчивости. В схеме репликации Raft узел-лидер может иметь запас по процессору и памяти, однако его ведомые реплики (followers) могут не справляться с потоком записи, накапливая отставание применения журнала (commit-index lag). Если лидер продолжит принимать данные, кластер окажется под угрозой потери консенсуса.

В новой версии архитектуры унифицированного движка управления перегрузками (BYOS — Bring Your Own Signals) Cinnamon научился агрегировать распределенные сигналы. Когда отставание репликации на ведомых узлах превышает допустимый лимит, лидер принудительно активирует регулирование и сбрасывает входящие запросы на запись, давая репликам возможность догнать состояние журнала.

Параллельно для предотвращения сценария «шумного соседа» (noisy neighbour), когда один некорректно работающий микросервис заполняет всю очередь базы данных миллионами однотипных запросов, был интегрирован механизм Scorecard. Он отслеживает индивидуальную конкурентность каждого подключенного клиента независимо от общего состояния СУБД. Превышение персонального лимита изолирует клиента-нарушителя до того, как его поведение повлияет на работу других сервисов платформы.

ПодходМеханизм управленияДостоинстваОграничения и риски
Статические квоты (Redis)Token bucket на уровне stateless-шлюзаПростота начальной конфигурацииДополнительный сетевой hop, QPS не отражает реальную стоимость запроса
Очереди CoDelЗадержка в очереди, адаптивный LIFOЛиквидация bufferbloat, быстрый сброс безнадежных вызововОтсутствие приоритизации: сброс критических бизнес-запросов
CinnamonПИД-регулятор, приоритеты t0–t5Динамическая адаптация к конкурентности, спасение трафика t0/t1Необходимость точной классификации трафика и калибровки сигналов
Движок BYOSВнешние сигналы, включая Raft replication lagЗащита распределенного консенсуса и ведомых репликКомплексность сопровождения распределенной обратной связи

Эксплуатационные результаты и выводы

Переход на адаптивное управление нагрузкой привел к качественному скачку стабильности баз данных Uber. По сравнению с классической схемой статических лимитов пропускная способность кластеров в условиях пиковой перегрузки увеличилась на 80%. Задержка на 99-м перцентиле сократилась на 70%. В моменты экстремальных всплесков трафика потребление кучи (heap) снизилось на 60%, а пиковое количество горутин в среде Go упало на 93% — в одном из стресс-тестов число активных легковесных потоков сократилось со 150 000 до 10 000, полностью предотвратив исчерпание памяти.

Инженеры подчеркивают, что Cinnamon представляет собой внутреннюю платформу, архитектура которой глубоко завязана на специфику Uber. Прямой перенос такого решения в другие проекты требует осторожности: неправильная классификация приоритетов может оставить важный трафик без защиты, а возврат клиентам одинаковых таймаутов или кодов 429 способен спровоцировать синхронный шторм повторных попыток (thundering herd). Однако главный концептуальный вывод универсален: надежная защита распределенных СУБД должна строиться на адаптивных контурах обратной связи внутри слоя хранения, где реальное состояние ресурсов известно с абсолютной точностью.