Масштабирование инфраструктуры криптобиржи: от 1 тыс. до 100 тыс. пользователей в день
Содержание
- Почему биржи ломаются под нагрузкой
- Три этапа масштабирования
- Находите узкие места раньше, чем они найдут вас
- Масштабирование базы данных: первая стена на вашем пути
- Масштабирование матчинг-движка: священный одиночный поток
- Масштабирование WebSocket: реальное время при большом масштабе
- Проектирование API-шлюза: ваш вход
- Инфраструктура блокчейн-нод
- CDN и оптимизация статических ресурсов
- Мониторинг и оповещения: наблюдаемость биржи
- Автомасштабирование с Kubernetes
- Аварийное восстановление и мультирегиональность
- Нагрузочное тестирование: имитация реальной торговой нагрузки
- Оптимизация затрат при масштабировании
- Реальные метрики и бенчмарки
- Собираем всё вместе
Почему биржи ломаются под нагрузкой
Каждая криптобиржа прекрасно работает в среде разработки. Каждая биржа легко справляется с первой сотней пользователей. Проблемы начинаются, когда приходят реальные трейдеры — а они всегда появляются в самый неподходящий момент. Листинг токена становится вирусным. Биткоин двигается на 15% за час. Начинается каскад ликвидаций. И внезапно ваш матчинг-движок тонет в ордерах, соединения с базой данных исчерпаны, WebSocket-серверы рвут соединения, а очередь поддержки заполняется тикетами «ГДЕ МОИ ДЕНЬГИ».
Я видел биржи, которые идеально справлялись с демо-торговлей, но рушились за считаные минуты реального рыночного события. Первопричина почти всегда одна и та же: инфраструктура спроектирована под среднюю нагрузку, а не под пиковую. А в крипте пиковая нагрузка — это не 2x от средней, а 10x–50x. Волатильность, которая делает крипту захватывающей для трейдеров, делает её пугающей для DevOps-инженеров.
Если вы строите на программном обеспечении для криптобирж вроде Codono, у вас уже есть проверенный в продакшене фундамент. С self-hosted криптобиржей вы контролируете каждый уровень стека — от матчинг-движка до базы данных. Но масштабирование этого фундамента под реальный рост требует осознанных инфраструктурных решений на каждом уровне. Это руководство подробно описывает, какие именно решения нужны, с реальными цифрами и конфигурациями, которые имеют значение.
Три этапа масштабирования
Масштабирование инфраструктуры биржи происходит не непрерывно. Оно идёт отдельными фазами, каждая со своими узкими местами и решениями. Понимание того, на каком этапе вы находитесь, уберегает вас от чрезмерной инженерии слишком рано или от недостаточных инвестиций слишком поздно.
Этап 1: Стартап (1 тыс. DAU)
На этом этапе всё работает на одном сервере или небольшом кластере. Ваша база данных, матчинг-движок, API-сервер и WebSocket-сервер могут делить одну машину. Это нормально. Не позволяйте никому убедить вас в обратном.
Типичная инфраструктура: 1–3 сервера, один инстанс базы данных, монолитное или простое микросервисное развёртывание. Общие расходы на облако: 500–2 000 долларов в месяц.
Что ломается первым: производительность запросов к базе данных по истории ордеров и таблицам сделок. Когда кто-то впервые экспортирует свою историю сделок по 50 торговым парам, ваша база данных зависает — и это чувствуют все.
Что делать: добавьте индексы в базу данных, внедрите пулинг соединений (PgBouncer или ProxySQL), настройте базовое кэширование в Redis для горячих данных вроде цен тикеров и балансов пользователей. Пока ничего не шардируйте. Пока не добавляйте реплики для чтения. Просто сделайте свой единственный инстанс максимально эффективным.
Этап 2: Рост (10 тыс. DAU)
Теперь у вас реальная конкурентная торговля. Несколько торговых пар активны одновременно. WebSocket-соединения начинают потреблять ощутимую память. Ваш API начинает упираться в лимиты, о необходимости которых вы не подозревали.
Типичная инфраструктура: 5–15 серверов, база данных с репликами для чтения, выделенный сервер матчинг-движка, отдельный WebSocket-кластер. Расходы на облако: 5 000–15 000 долларов в месяц.
Что ломается первым: лимиты WebSocket-соединений, пропускная способность базы данных на запись, глубина очереди матчинг-движка во время всплесков волатильности.
Что делать: вынесите матчинг-движок на выделенное оборудование. Добавьте реплики базы данных для запросов на чтение. Внедрите полноценный pub/sub-слой для WebSocket-рассылки. Разверните API-шлюз с ограничением частоты запросов. Именно здесь ваш выбор технологического стека начинает по-настоящему иметь значение.
Этап 3: Масштаб (100 тыс. DAU)
Теперь вы эксплуатируете настоящую биржевую инфраструктуру. Каждому компоненту нужны горизонтальное масштабирование, отказоустойчивость и географическое распределение. Вам нужна выделенная DevOps-команда, а не разработчик, который иногда делает деплои.
Типичная инфраструктура: 50+ серверов в нескольких регионах, шардированные базы данных, кластеры Kubernetes, выделенные блокчейн-ноды, мульти-CDN. Расходы на облако: 30 000–80 000 долларов в месяц.
Что ломается первым: всё, что работало на этапе 2, но держалось на изоленте. Шардирование базы данных становится обязательным. Пробелы в мониторинге превращаются в простои. Процесс деплоя становится узким местом.
Что делать: всё, что описано в этом руководстве.
Находите узкие места раньше, чем они найдут вас
Прежде чем масштабировать что-либо, нужно знать, что на самом деле работает медленно. Гадать — дорого. Вот шесть самых распространённых узких мест в инфраструктуре биржи, ранжированные по частоте, с которой они оказываются реальной причиной проблем с производительностью:
-
Пропускная способность базы данных на запись — каждый ордер, сделка, обновление баланса и депозит порождают записи. При 1 000 ордеров в секунду вы генерируете 3 000–5 000 записей в базу данных в секунду с учётом сделок и обновлений балансов.
-
Глубина очереди матчинг-движка — если ордера поступают в очередь быстрее, чем движок их обрабатывает, задержка растёт экспоненциально. Здоровая глубина очереди — менее 100 ордеров. Если во время всплеска вы видите 10 000+, у вас проблемы.
-
Количество WebSocket-соединений — каждый подключённый пользователь держит открытое TCP-соединение. При 10 тыс. одновременных соединений вы используете ~80 МБ памяти ядра только под сокетные буферы. При 100 тыс. вам нужны выделенные WebSocket-серверы.
-
Отставание синхронизации блокчейн-нод — если ваша нода обнаружения депозитов отстаёт от вершины цепи, пользователи сообщают о «пропавших депозитах», а ваша команда поддержки паникует. Нода, отстающая на 10 блоков в Ethereum, означает, что депозиты появляются на 2+ минуты позже.
-
Насыщение лимитов API — торговые боты будут долбить ваш API тысячами запросов в секунду с одного аккаунта. Без должного ограничения частоты несколько агрессивных ботов могут ухудшить опыт для всех пользователей.
-
Нехватка памяти Redis — если вы кэшируете агрессивно (а вы должны), потребление памяти Redis растёт вместе с базой пользователей. Шторм вытеснения ключей может за секунды перерасти в перегрузку базы данных.
Профилируйте до оптимизации. Инструментируйте всё. Настройте метрики Prometheus с первого дня, а не с сотого.
Масштабирование базы данных: первая стена на вашем пути
База данных — узкое место в 80% проблем масштабирования бирж. Вот последовательность решений в том порядке, в котором их следует внедрять.
Пулинг соединений
Ваше приложение, вероятно, открывает новое соединение с базой данных на каждый запрос. При 1 000 запросов в секунду это 1 000 соединений. Большинство баз данных начинают буксовать уже при 200–500 активных соединениях. PgBouncer (PostgreSQL) или ProxySQL (MySQL) встают между вашим приложением и базой данных, поддерживая пул переиспользуемых соединений.
Установите размер пула в 2–4 раза больше числа ядер CPU на сервере базы данных. Машина с 16 ядрами должна иметь пул из 32–64 соединений. Больше — не лучше, а хуже, из-за конкуренции за блокировки.
Реплики для чтения
Когда один сервер базы данных достигает предела, первый шаг масштабирования — добавление реплик для чтения. Направляйте все запросы на чтение — снимки ордербука, историю сделок, представления портфеля, административные дашборды — на реплики. Записи оставляйте на первичном сервере.
Критическая деталь: лаг репликации. При асинхронной репликации реплика может отставать от первичного сервера на 10–100 мс. Для истории сделок это приемлемо. Для проверки баланса перед размещением ордера — нет. Балансы всегда читайте с первичного сервера.
Слои кэширования Redis
Не каждое чтение должно идти в базу данных. Агрессивно кэшируйте следующее:
- Данные тикеров (цена, объём за 24 ч, изменение за 24 ч): TTL 1 секунда
- Снимки ордербука: TTL 100 мс, перестраиваются из состояния матчинг-движка в памяти
- Балансы пользователей: сквозной кэш записи, инвалидируется при каждой сделке и выводе
- Конфигурация рынков (торговые пары, уровни комиссий, лимиты): TTL 60 секунд
- Данные сессий: TTL 30 минут со скользящим истечением
Правильно настроенный кэш Redis может устранить 90% чтений из базы данных. На этапе 2 одно это может отодвинуть необходимость реплик для чтения на месяцы.
Стратегии шардирования
Когда один первичный сервер не справляется с объёмом записи — обычно выше 10 000 записей в секунду в устойчивом режиме — нужно шардирование. Для бирж работают две стратегии шардирования:
Шардирование по торговой паре. Все ордера, сделки и данные ордербука для BTC/USDT идут в шард 1, ETH/USDT — в шард 2 и так далее. Это согласуется с естественным партиционированием матчинг-движка. Минус: запросы балансов пользователей должны обращаться к нескольким шардам.
Шардирование по ID пользователя. Все данные пользователя 12345 идут в шард A. Запросы балансов быстрые, но межпользовательские запросы (например, построение глобального ордербука) требуют scatter-gather. Это лучше работает для бирж со множеством торговых пар, но умеренным объёмом на пару.
Большинство бирж на этапе 3 используют гибрид: таблицы ордеров и сделок шардируются по торговой паре, таблицы пользователей и балансов — по ID пользователя, а справочные данные (конфигурации рынков, расписания комиссий) остаются на одном нешардированном инстансе.
Временные ряды для ордербуков
Исторические данные ордербуков — снимки глубины, которые питают графики и аналитику — не должны жить в вашей транзакционной базе данных. Используйте базу данных временных рядов: TimescaleDB, InfluxDB или ClickHouse. Они оптимизированы под большие объёмы append-only записей и запросы по временным диапазонам.
Активная торговая пара может генерировать более 10 000 обновлений ордербука в секунду. При 50 байтах на обновление это 43 ГБ в день на одну пару. Базы временных рядов справляются с этим за счёт колоночного хранения и автоматического уплотнения данных. Ваша реляционная база данных на этом захлебнётся.
Масштабирование матчинг-движка: священный одиночный поток
Матчинг-движок — компонент, который большинство пытается масштабировать неправильно. Вот почему — и что делать вместо этого.
Почему однопоточность важна
Матчинг-движок для одной торговой пары должен быть однопоточным. Это не ограничение — это требование дизайна. Сопоставление по приоритету цена-время (FIFO) требует детерминированного порядка операций. Многопоточное сопоставление вносит состояния гонки, которые могут вызывать некорректные исполнения, фантомную ликвидность и рассогласование балансов.
Хорошая новость: однопоточный движок на современном оборудовании может обрабатывать 50 000–100 000 ордеров в секунду. Если вы не управляете биржей из мирового топ-10, одного потока на пару достаточно.
Ордербуки в памяти
Ордербук должен жить в памяти, а не в базе данных. Ордербук на базе данных добавляет 1–10 мс задержки на операцию. Ордербук в памяти работает за микросекунды. База данных — для персистентности и восстановления, а не для активного сопоставления.
Архитектура: входящие ордера попадают в очередь в памяти, матчинг-движок обрабатывает их последовательно, результаты асинхронно записываются в базу данных через журнал событий. Если движок падает, он воспроизводит журнал событий для восстановления состояния. Это event sourcing — стандартный подход для финансовых матчинг-движков.
Горизонтальное масштабирование по торговым парам
Нельзя горизонтально масштабировать матчинг-движок одной торговой пары (без потери корректности). Но можно запускать независимые матчинг-движки для разных торговых пар на разных ядрах или серверах.
На этапе 2 запускайте все движки на одном многоядерном сервере, по одному движку на ядро. На этапе 3 распределяйте движки по выделенным серверам, сгруппированным по объёму. Размещайте BTC/USDT и ETH/USDT на высокопроизводительных машинах с быстрым NVMe-хранилищем для журналов событий. Пары с малым объёмом размещайте на общих инстансах.
Программное обеспечение для криптобирж Codono берёт это партиционирование на себя, позволяя назначать торговые пары конкретным инстансам движка без изменения кода приложения.
Масштабирование WebSocket: реальное время при большом масштабе
WebSocket-соединения — stateful, долгоживущие и прожорливые по памяти. Их масштабирование требует принципиально иного подхода, чем масштабирование stateless HTTP API.
Пулинг соединений
Каждый WebSocket-сервер должен обрабатывать 10 000–50 000 одновременных соединений в зависимости от объёма сообщений. За этим пределом управление сокетами на уровне ядра становится узким местом. Используйте пулы соединений с проверками здоровья, чтобы распределять новые соединения между доступными серверами.
Настройте балансировщик нагрузки на поддержку WebSocket upgrade. nginx подойдёт, но требует явной конфигурации proxy_pass с заголовками upgrade соединения. HAProxy поддерживает WebSocket нативно и часто является лучшим выбором для этого уровня.
Архитектура Pub/Sub
Ключевая проблема: когда исполняется сделка по BTC/USDT, нужно уведомить каждого пользователя, подписанного на эту пару. Если 5 000 пользователей смотрят BTC/USDT через 10 WebSocket-серверов, матчинг-движок не может отправить 5 000 отдельных сообщений.
Решение — pub/sub-слой между матчинг-движком и WebSocket-серверами. Матчинг-движок публикует одно событие сделки в канал. Каждый WebSocket-сервер подписывается на каналы, интересные его подключённым клиентам, и рассылает сообщения локально.
Redis Pub/Sub работает на этапе 2. Он простой, быстрый, и Redis у вас уже есть. Ограничение: Redis Pub/Sub — это fire-and-forget. Если WebSocket-сервер пропустил сообщение (например, во время перезапуска), это сообщение потеряно.
NATS или NATS JetStream — лучший выбор на этапе 3. NATS обрабатывает миллионы сообщений в секунду, поддерживает воспроизведение сообщений для восстановления и имеет встроенную кластеризацию. Он создан именно для такого сценария использования.
Sticky-сессии
Когда WebSocket-соединение пользователя обрывается и он переподключается, в идеале он должен попасть на тот же сервер. Это позволяет избежать проблемы «вспышки устаревших данных», когда пользователь кратковременно видит устаревший ордербук, пока новый сервер догоняет актуальное состояние.
Настройте балансировщик нагрузки на sticky-сессии по исходному IP с разумным таймаутом (5–10 минут). Если вы используете Kubernetes, используйте headless-сервис с session affinity.
Сжатие сообщений
При масштабе пропускная способность WebSocket становится существенной. Активная биржа может генерировать 50 МБ/с сырого WebSocket-трафика по всем парам. Используйте сжатие per-message deflate (RFC 7692), чтобы сократить трафик на 60–80%. Большинство современных WebSocket-клиентов поддерживают его нативно.
Проектирование API-шлюза: ваш вход
Ваш API-шлюз обрабатывает каждый HTTP-запрос от трейдеров, ботов и фронтенд-приложений. Он должен быть быстрым, безопасным и горизонтально масштабируемым.
Ограничение частоты запросов
Внедряйте ограничение частоты на нескольких уровнях:
- Глобальный: 100 000 запросов в секунду для всех пользователей (защищает бэкенд от DDoS)
- По IP: 1 000 запросов в минуту (останавливает наивные злоупотребления)
- По API-ключу: 600 запросов в минуту для обычных пользователей, 6 000 для VIP/институциональных (соответствует системе уровней)
- По эндпоинту: эндпоинты размещения ордеров получают более строгие лимиты, чем эндпоинты только для чтения
Используйте алгоритм скользящего окна на базе Redis. Алгоритмы token bucket проще, но менее точны. Храните счётчики с TTL, соответствующими вашим окнам лимитов.
Подробности о построении надёжных API-интеграций: Codono предоставляет встроенное ограничение частоты с настраиваемыми уровнями для каждого API-ключа.
Кэширование ответов
Кэшируйте ответы API на уровне шлюза для эндпоинтов, которые не меняются от пользователя к пользователю:
GET /api/v1/ticker— кэш на 1 секундуGET /api/v1/depth— кэш на 100 мсGET /api/v1/trades— кэш на 500 мсGET /api/v1/markets— кэш на 60 секунд
Используйте заголовки Cache-Control, чтобы CDN тоже могли кэшировать эти ответы на периферии. Одно это может снизить нагрузку на бэкенд на 70% для эндпоинтов с преобладанием чтений.
Балансировка нагрузки
На этапе 2 достаточно одного инстанса nginx или HAProxy с round-robin. На этапе 3 вам нужно:
- Балансировка нагрузки уровня 7 с проверками здоровья
- Географическая маршрутизация (направлять азиатских пользователей на азиатские серверы)
- Автоматические выключатели (circuit breakers), исключающие нездоровые бэкенды
- Слив соединений для деплоев без простоя
AWS ALB, Google Cloud Load Balancer или выделенный кластер Envoy — все подходят. Важна активная проверка здоровья, а не пассивная. Не ждите 5 неудачных запросов, чтобы исключить бэкенд — опрашивайте его каждые 5 секунд и исключайте в момент, когда он перестаёт отвечать.
Географическое распределение
Развёртывайте API-шлюзы в каждом регионе, где у вас есть значительный пользовательский трафик. Трейдер в Токио, обращающийся к API-серверу в Вирджинии, добавляет 150–200 мс задержки к каждому запросу. Для размещения ордеров это неприемлемо.
Как минимум развернитесь в трёх регионах: Северная Америка, Европа и Азия. Направляйте пользователей к ближайшему шлюзу через географическую маршрутизацию на основе DNS (Route53, Cloudflare). Шлюз может затем маршрутизировать к вашему центральному матчинг-движку — эта задержка приемлема, потому что она происходит в вашей внутренней сети.
Инфраструктура блокчейн-нод
Вашей бирже нужно взаимодействовать с каждым поддерживаемым блокчейном: отслеживать депозиты, транслировать выводы и проверять подтверждения. Инфраструктурой нод часто пренебрегают, пока она не вызывает простой.
Выделенные и общие ноды
Общие ноды (Infura, Alchemy, QuickNode) подходят для этапа 1. Они надёжны, обслуживаются кем-то другим и стоят 50–500 долларов в месяц за сеть.
Выделенные ноды становятся необходимыми на этапе 2 для сетей с большим объёмом. Причины: лимиты у провайдеров общих нод жёсткие (обычно 100–1 000 запросов в секунду), нельзя настроить конфигурацию ноды под себя, и обработка ваших депозитов зависит от аптайма третьей стороны.
Запускайте выделенные ноды для 3–5 главных сетей по объёму. Для всего остального держите провайдеров общих нод как запасной вариант.
Балансировка нагрузки нод
Запускайте минимум две ноды на блокчейн. Ставьте их за балансировщиком нагрузки с проверками здоровья, которые проверяют статус синхронизации, а не просто доступность по HTTP. Нода, которая отвечает на проверки здоровья, но отстаёт от вершины цепи на 50 блоков, хуже выключенной ноды — она молча пропускает депозиты.
Логика проверки здоровья: запросить номер последнего блока ноды, сравнить его с эталоном (другая нода или API блокчейн-обозревателя) и пометить ноду нездоровой, если она отстаёт более чем на N блоков (где N зависит от времени блока сети).
Несколько провайдеров как запасной вариант
Даже с выделенными нодами настройте откат на общих провайдеров. Если обе ваши ноды Ethereum падают во время обновления Geth, Infura или Alchemy сохранят работу обработки депозитов. Реализуйте это как список приоритетов с автоматическим переключением:
- Первичная выделенная нода
- Вторичная выделенная нода
- Общий провайдер A (Alchemy)
- Общий провайдер B (Infura)
Функции безопасности Codono включают встроенный мониторинг здоровья нод и автоматическое переключение между блокчейн-провайдерами.
CDN и оптимизация статических ресурсов
Ваш торговый интерфейс, графики и статические ресурсы никогда не должны обращаться к исходным серверам в продакшене. Используйте CDN агрессивно.
Разместите за CDN с длинными TTL кэша следующее: JavaScript-бандлы (хэш содержимого в имени файла, кэш на 1 год), CSS-файлы, библиотеку графиков TradingView, шрифты, изображения и фавиконки. Используйте сброс кэша через хэширование имён файлов, а не параметры запроса (некоторые CDN вырезают параметры запроса).
Для торгового интерфейса в частности развёртывайте в нескольких регионах CDN, чтобы первоначальная загрузка страницы была быстрой независимо от местоположения пользователя. API-вызовы будут чуть медленнее для пользователей, удалённых от вашего бэкенда, но воспринимаемая производительность интерфейса должна быть мгновенной.
Устанавливайте immutable для хэшированных ресурсов. Это предотвращает лишние запросы ревалидации:
Cache-Control: public, max-age=31536000, immutable
Мониторинг и оповещения: наблюдаемость биржи
Общий инфраструктурный мониторинг необходим, но недостаточен. Нужны специфичные для биржи метрики, которые говорят, хороший ли опыт получают трейдеры.
Метрики, которые имеют значение
Задержка сопоставления ордеров (p50, p95, p99). Это время от отправки ордера до результата сопоставления. Цели: p50 < 1 мс, p95 < 5 мс, p99 < 10 мс. Если p99 превышает 50 мс, трейдеры заметят и начнут жаловаться. Если превышает 200 мс, арбитражные боты уйдут.
Коэффициент исполнения. Процент рыночных ордеров, исполняемых полностью. У здорового ордербука этот показатель выше 95%. Ниже 80% — вашему движку ликвидности требуется внимание.
Время обработки выводов. От запроса пользователя до трансляции в блокчейн. Цель: менее 5 минут для автоматических выводов, менее 2 часов для ручной проверки. Отслеживайте p95 — несколько медленных выводов генерируют непропорционально много тикетов в поддержку.
Задержка WebSocket-сообщений. Время от исполнения сделки до доставки сообщения последнему подключённому клиенту. Цель: менее 100 мс для 99% сообщений. Измеряйте это сквозным образом, а не только на уровне pub/sub.
Дельта синхронизации блокчейн-нод. Разница между последним блоком вашей ноды и вершиной цепи. Оповещайте, если она превышает 3 блока для быстрых сетей (Solana, BSC) или 2 блока для медленных (Bitcoin, Ethereum).
Настройка Prometheus + Grafana
Prometheus — стандарт мониторинга бирж. Экспортируйте пользовательские метрики из каждого компонента:
# Метрики матчинг-движка
exchange_order_latency_seconds{pair="BTC_USDT",type="limit"}
exchange_orders_processed_total{pair="BTC_USDT"}
exchange_order_book_depth{pair="BTC_USDT",side="bid"}
# Метрики WebSocket
exchange_ws_connections_active{server="ws-01"}
exchange_ws_messages_sent_total{pair="BTC_USDT"}
# Метрики блокчейна
exchange_node_sync_delta{chain="ethereum"}
exchange_deposit_processing_seconds{chain="ethereum"}
exchange_withdrawal_broadcast_seconds{chain="bitcoin"}
Создайте дашборды Grafana для трёх аудиторий: команда эксплуатации (здоровье инфраструктуры), торговая команда (метрики качества рынков) и руководство (бизнес-метрики: дневной объём, новые регистрации, выручка).
Маршрутизация оповещений
Не каждое оповещение должно будить кого-то в 3 часа ночи. Распределите оповещения по уровням:
- P1 (будить немедленно): матчинг-движок не работает, первичная база данных недоступна, аномалия баланса горячего кошелька, обработка выводов остановлена
- P2 (будить в рабочие часы): лаг репликации > 5 секунд, дельта синхронизации нод > 10 блоков, доля ошибок API > 1%
- P3 (тикет, без пробуждения): использование диска > 80%, сертификат истекает через 14 дней, коэффициент попаданий в кэш ниже 90%
Автомасштабирование с Kubernetes
Kubernetes — естественная платформа для масштабирования инфраструктуры биржи на этапе 2 и далее. Но не каждый компонент должен автомасштабироваться одинаково.
Horizontal Pod Autoscaler (HPA)
Настройте HPA для stateless-компонентов:
- Поды API-шлюза: масштабирование по CPU (цель 60%) и частоте запросов
- Поды WebSocket-серверов: масштабирование по числу соединений (цель 10 000 на под)
- Поды блокчейн-наблюдателей: масштабирование по глубине очереди (ожидающие транзакции)
- Воркеры истории сделок и аналитики: масштабирование по длине очереди задач
Не автомасштабируйте матчинг-движок через HPA. Каждый инстанс матчинг-движка обрабатывает конкретные торговые пары, а добавление нового инстанса требует перебалансировки назначений пар. Это ручная операция, которую нужно планировать, а не автоматизировать.
Прогнозирование трафика
Криптоторговля следует предсказуемым паттернам, наложенным на непредсказуемые всплески. Предсказуемая часть: объём достигает пика в часы американского рынка (14:00–21:00 UTC), по понедельникам и в дни крупных экономических новостей. Масштабируйтесь заранее под эти окна.
Непредсказуемая часть: биткоин двигается на 10%, и объём взрывается в 20 раз. Для этого настройте агрессивные параметры HPA с короткими окнами масштабирования вверх (30 секунд) и более длинными окнами масштабирования вниз (10 минут). Избыточное масштабирование на 10 минут стоит доллары. Недостаточное масштабирование на 10 минут теряет пользователей навсегда.
StatefulSets для stateful-компонентов
Используйте StatefulSets (а не Deployments) для: матчинг-движка, инстансов баз данных, кластеров Redis и блокчейн-нод. StatefulSets обеспечивают стабильные сетевые идентификаторы и персистентное хранилище — и то и другое необходимо stateful-компонентам для корректной работы.
Аварийное восстановление и мультирегиональность
Если ваша биржа работает в одной зоне доступности и эта зона падает, ваша биржа падает. На этапе 2 развёртывайтесь по зонам доступности внутри одного региона. На этапе 3 — по регионам.
Отказоустойчивость базы данных
Настройте автоматическое переключение для первичной базы данных. PostgreSQL с Patroni или MySQL с Group Replication могут повысить реплику до первичной за 10–30 секунд. Тестируйте это переключение ежемесячно. Непроверенное переключение хуже его отсутствия — оно даёт ложную уверенность.
Критическая деталь: после переключения все соединения приложения должны переподключиться к новой первичной базе. Используйте прокси соединений (PgBouncer, ProxySQL) с плавающим IP или DNS-записью, указывающей на инстанс, который в данный момент является первичным.
Горячий резерв матчинг-движка
Поддерживайте горячий резервный матчинг-движок, который воспроизводит журнал событий в реальном времени, но не обрабатывает новые ордера. Если первичный отказывает, повышайте резервный. Время восстановления: менее 30 секунд, с нулевой потерей ордеров (потому что журнал событий — источник истины).
Здесь окупается event sourcing. Резервному не нужно синхронизировать состояние с первичным — он независимо выводит состояние из того же журнала событий. Если оба инстанса обрабатывают одни и те же события, они приходят к одному и тому же состоянию. Детерминизм гарантирован однопоточным дизайном.
Мультирегиональное развёртывание
На этапе 3 запускайте активные API-шлюзы и WebSocket-серверы во всех регионах. Матчинг-движок запускайте в одном первичном регионе. Торговая задержка из удалённых регионов будет включать межрегиональный сетевой скачок (50–150 мс), но это приемлемо, потому что альтернатива — запуск матчинг-движков в нескольких регионах — порождает кошмары с консистентностью.
Согласно руководству по запуску, однорегиональный запуск вполне нормален. Мультирегиональность — оптимизация этапа 3, а не требование для запуска.
Нагрузочное тестирование: имитация реальной торговой нагрузки
Нельзя масштабировать то, что не протестировано. И большинство нагрузочных тестов для бирж нереалистичны, потому что не моделируют реальное торговое поведение.
Реалистичные профили торговой нагрузки
Реальный торговый трафик распределён неравномерно. Моделируйте эти типы пользователей:
- Маркет-мейкеры (5% пользователей, 60% ордеров): непрерывное размещение и отмена лимитных ордеров, 10–50 ордеров в секунду на маркет-мейкера, в основном паттерны отмены с заменой.
- Розничные трейдеры (80% пользователей, 20% ордеров): спорадические рыночные и лимитные ордера, интенсивное использование WebSocket-подписок, частые проверки балансов.
- Боты (15% пользователей, 20% ордеров): всплески API-трафика, агрессивное тестирование лимитов, высокая текучесть WebSocket-подписок.
Методология нагрузочного тестирования
- Базовая линия: прогоните текущий продакшен-паттерн трафика в течение 1 часа. Запишите все метрики.
- Нарастание: увеличивайте трафик в 2 раза каждые 10 минут, пока что-то не сломается. Запишите, где и как это сломалось.
- Всплеск: от базовой линии мгновенно перейдите к 10-кратному трафику. Измерьте время восстановления после окончания всплеска.
- Выдержка: прогоните 2-кратный трафик от базовой линии в течение 24 часов. Ищите утечки памяти, исчерпание пулов соединений и проблемы с дисковым пространством.
Используйте инструменты вроде k6, Gatling или собственные скрипты, моделирующие паттерны потока ордеров выше. Универсальные инструменты нагрузочного тестирования HTTP не понимают динамику ордербука — нужны скрипты, размещающие реалистичные последовательности лимитных ордеров, рыночных ордеров и отмен.
Оптимизация затрат при масштабировании
Затраты на облачную инфраструктуру растут быстрее выручки, если вы не занимаетесь оптимизацией осознанно.
Зарезервированные инстансы
Ваши серверы баз данных, серверы матчинг-движка и первичные инстансы Redis работают круглосуточно. Покупайте зарезервированные инстансы (обязательства на 1 или 3 года) для этих нагрузок. Экономия: 30–60% по сравнению с тарифами по требованию.
Спотовые инстансы для некритичных нагрузок
Используйте спотовые/прерываемые инстансы для: сред нагрузочного тестирования, синхронизации блокчейн-нод (только первоначальной), воркеров аналитики данных и стейджинг-сред. Эти нагрузки терпимы к прерываниям. Экономия: 60–90% по сравнению с тарифами по требованию.
Никогда не запускайте матчинг-движок, первичную базу данных или обработку выводов на спотовых инстансах. Экономия не стоит операционного риска.
Правильный подбор размеров
Большинство операторов бирж избыточно выделяют CPU и недостаточно — память. Пересматривайте размеры инстансов ежеквартально. Если загрузка CPU сервера никогда не превышает 30%, уменьшите его. Если использование памяти сервера регулярно достигает 85%, увеличьте его, пока он не начал свопить.
Управление жизненным циклом данных
Данные сделок старше 90 дней запрашиваются редко. Перемещайте их в холодное хранилище (S3, GCS) и запрашивайте через Athena или BigQuery по требованию. В горячей базе данных храните только последние 90 дней. Это может сократить затраты на хранение базы данных на 70% и улучшить производительность запросов к свежим данным.
Реальные метрики и бенчмарки
Вот целевые показатели производительности для каждого этапа масштабирования, основанные на реальной эксплуатации бирж:
| Метрика | Этап 1 (1 тыс. DAU) | Этап 2 (10 тыс. DAU) | Этап 3 (100 тыс. DAU) |
|---|---|---|---|
| Ордеров в секунду | 1 000 | 10 000–50 000 | 100 000+ |
| Задержка ордеров (p99) | < 50 мс | < 10 мс | < 5 мс |
| WebSocket-соединения | 500 | 5 000–20 000 | 100 000+ |
| Пропускная способность WS-сообщений | 10 тыс. сообщ/с | 100 тыс. сообщ/с | 1 млн+ сообщ/с |
| API-запросов в секунду | 5 000 | 50 000 | 500 000+ |
| Записей в базу данных в секунду | 500 | 5 000 | 50 000+ |
| Время обнаружения депозита | < 60 с | < 30 с | < 15 с |
| Время трансляции вывода | < 5 мин | < 2 мин | < 30 с |
| SLA аптайма | 99,5% | 99,9% | 99,95% |
Это не амбициозные цифры — это минимальные пороги, при которых пользовательский опыт остаётся приемлемым. Ниже этих целей трейдеры уходят. Выше них — вы конкурентоспособны.
Собираем всё вместе
Масштабирование криптобиржи — это не применение всех техник с первого дня. Это понимание того, какие проблемы решать на каждом этапе, и построение архитектуры, позволяющей добавлять мощности без полной переработки.
Начинайте просто. Развёртывание на одном сервере с правильным кэшированием и пулингом соединений выдерживает больше нагрузки, чем ожидает большинство. Когда этого недостаточно, добавьте реплики для чтения и pub/sub-слой. Когда и этого недостаточно, шардируйте базу данных и распределите WebSocket-серверы. Когда и этого недостаточно, переходите на мультирегиональность.
Константа на всех этапах: мониторинг. Нельзя масштабировать то, что нельзя измерить. Инструментируйте всё с первого дня. Метрики, которые вы собираете при 1 тыс. DAU, точно покажут, куда инвестировать при 10 тыс. DAU.
Если вы строите на программном обеспечении для криптобирж Codono, матчинг-движок, управление ордербуком и WebSocket-инфраструктура уже проверены в продакшене. Ваша задача — масштабировать окружающую инфраструктуру: базу данных, кэширование, балансировку нагрузки и блокчейн-ноды — под ваш рост. Это гораздо более решаемая задача, чем построение всего с нуля.
Готовы построить биржевую инфраструктуру, которая масштабируется? Изучите архитектуру Codono и посмотрите, как фундамент справляется с продакшен-нагрузкой торговли из коробки.
Codono Team
Codono builds enterprise-grade crypto exchange software deployed by 250+ operators across 40+ countries. Our team writes from production experience running spot, derivatives, custody, and compliance at scale.
View all posts by Codono Team →