
Статья подготовлена вместе с экспертами
Дарья Шибкова, менеджер продукта

Приложение запускалось на одном сервере и держало 1000 пользователей без единой жалобы. На 50 000 активных пользователей страницы стали открываться с задержкой, отчёты крутились по десять секунд. На 200 000 активных пользователей база данных превратилась в узкое место всей системы: код оптимизирован, кэш прогрет, а запросы всё равно упираются в один и тот же сервер СУБД.
Масштабирование базы данных это закономерный этап роста продукта. Почти каждый сервис, который набирает аудиторию, рано или поздно приходит к моменту, когда база данных не справляется с нагрузкой: пул соединений переполнен, диск не успевает обрабатывать запись, реплики начинают отставать. Это не всегда означает, что архитектура была спроектирована неправильно — это значит, что нагрузка выросла быстрее, чем закладывали.
Сначала рассмотрим признаки того, что база данных работает на пределе. Затем — стратегии масштабирования: от самых простых (например, оптимизации запросов) до более сложных (например, шардирования). Эта статья для тех, кто чувствует проблему на уровне метрик и жалоб, но не всегда знает, каким термином она называется: для разработчиков, архитекторов и CTO на стадии активного роста.

Дарья Шибкова, менеджер продукта

Дмитрий Ковальчук, менеджер продукта
База данных редко перестает справляться с нагрузкой мгновенно. Сначала растёт задержка на пиках, потом деградация становится постоянной. Ловить момент нужно по метрикам, а не по жалобам пользователей: к моменту, когда жалобы дошли до поддержки, база уже перегружена. Ниже перечислены пороги, которые стоит держать под алертами постоянно.
Используйте таблицу как ориентир-светофор: зелёная зона — норма, жёлтая — пора планировать масштабирование, красная — база данных уже стала узким местом.
| Метрика | 🟢 Зелёный | 🟡 Жёлтый | 🔴 Красный | Что означает |
| CPU (sustained) | < 50% | 50–70% | > 70% | Высокая загрузка CPU увеличивает время выполнения запросов и время ожидания в очереди. |
| Buffer cache hit ratio | > 99% | 95–99% | < 95% | Данные читаются с диска, а не из RAM. Возможные причины: недостаточный объём RAM, неэффективные индексы или неоптимальные запросы |
| Disk I/O await (SSD) | < 10 мс | 10–20 мс | > 20 мс | Диск не успевает за нагрузкой ввода-вывода |
| Connections | < 60% max | 60–80% max | > 80% max | Приближается исчерпание доступных соединений (connection storm), свободных подключений почти не осталось |
| Replication lag | < 100 мс | 0,1–1 с | > 1–5 с | Реплика отстаёт, поэтому чтение с неё может возвращать устаревшие данные |
| Slow queries | нет > 1 с | единичные > 1 с | регулярные > 1 с | Запросы выполняются дольше ожидаемого |
Порог устойчивой загрузки CPU (sustained) выше 70% — инженерный ориентир, а не жесткая граница: конкретное значение зависит от профиля нагрузки и запаса под пики. Аналогично, пороги по disk I/O await и количеству соединений в таблице служат практическими ориентирами, а не абсолютными константами.
Buffer cache hit ratio отражает процент страниц, извлекаемых из оперативной памяти (buffer cache) без обращения к диску. Для систем OLTP значение ниже 95% обычно сигнализирует о дефиците горячих данных в RAM или о неэффективном использовании индексов (последовательное сканирование). Для оценки метрики необходимо выполнить запрос к системной статистике:
SELECT sum(heap_blks_hit) / sum(heap_blks_hit + heap_blks_read) FROM pg_statio_user_tables;
Connections выше 80% от max_connections — предвестник connection storm, когда всплеск клиентов разом занимает все свободные слоты, и новые подключения начинают падать с ошибкой. Replication lag в норме держится ниже 100 мс, но под нагрузкой даёт всплески вплоть до минут.
Инженер видит проблему в метриках, а бизнес — в деградации пользовательского опыта. Timeout в час пик, когда каталог не открывается под нагрузкой. Пользователи замечают, что страницы каталога, поиска и отчётов начинают открываться значительно медленнее, сложные SELECT с JOIN начинают стоить секунды. Медленный поиск и фильтрация: запросы перебирают миллионы строк без индекса. Отчёты формируются несколько минут и начинают конкурировать с транзакционной нагрузкой за ресурсы. Очереди на запись, когда блокировки строк выстраивают транзакции в цепочку. Ключевым системным индикатором перегрузки является регулярная необходимость в «тушении пожаров»: если DBA или дежурный инженер вынужден еженедельно вручную прерывать зависшие запросы, перезапускать пулы и чистить блокировки, это свидетельствует о том, что база данных работает на пределе своих возможностей. В такой ситуации система находится в состоянии хронической нестабильности, хотя полный отказ еще не наступил.
При первых признаках деградации производительности обычно увеличивают объём памяти и число CPU. Это разумная тактическая мера: вертикальное масштабирование (scale-up) позволяет быстро снизить нагрузку на сервер, устранить узкое место и выиграть время для дальнейшего анализа. Однако такой подход эффективен лишь до определённого предела. Но как стратегия оно не работает: когда объём горячих данных превышает RAM, тюнинг shared_buffers уже не помогает: оптимальный диапазон составляет около 25% от объема RAM с практическим максимумом в 8–16 ГБ. Данные, превышающие этот лимит, неизбежно будут считываться с диска, что делает дальнейший рост параметра бессмысленным для производительности. Дальше упор идёт в потолок железа и в стоимость: каждый следующий шаг по мощности сервера дорожает нелинейно. Момент, когда докидывание RAM перестаёт помогать, сигналит, что база данных не справляется с нагрузкой архитектурно, и пора переходить к стратегиям масштабирования.
Правильный порядок экономит месяцы работы и деньги. Дешёвые меры (индексы, пул соединений) дают эффект за часы, дорогие (шардирование) переписывают архитектуру приложения. Идти нужно снизу вверх и останавливаться, как только нагрузка перестала быть проблемой.
Самый дешёвый и самый недооценённый уровень. Правильный индекс ускоряет запрос в 10–100 раз, и часто одного этого достаточно, чтобы снять нагрузку без единого нового сервера. Типовые проблемы: отсутствие индексов на колонках в WHERE и JOIN, проблема N+1 (когда приложение вместо одного запроса делает по одному на каждую строку списка), SELECT * вместо перечисления нужных колонок, отсутствие партицирования на больших таблицах. Низкий buffer cache hit ratio часто указывает именно сюда: последовательное сканирование без индекса гоняет с диска страницы, которых там быть не должно.
Инструменты диагностики: EXPLAIN ANALYZE показывает реальный план выполнения запроса, slow query log собирает всё, что дольше порога, расширение pg_stat_statements агрегирует статистику по всем запросам и находит самые дорогие. Начинать масштабирование, не пройдя этот уровень, почти всегда преждевременно.
Наращивание ресурсов одного сервера: больше CPU, больше RAM, быстрые NVMe-диски. Главный плюс в простоте: приложение не меняется, схема данных не трогается, риск минимален. Это первый этап масштабирования: сначала выжимают максимум из вертикального масштабирования, затем переходят к горизонтальному. Несмотря на простоту внедрения, вертикальное масштабирование ограничено физическими пределами. Когда объем данных выходит за рамки оптимального размера RAM, оптимизация памяти теряет смысл, а стоимость оборудования растет быстрее, чем увеличивается производительность системы.
В большинстве веб-приложений 80–95% запросов приходится на чтение: карточки товаров, ленты, поиск, профили. Read replica (реплика для чтения): копия мастера, которая принимает на себя SELECT-запросы, пока мастер занимается записью. Реплики оправданы, когда доля чтения достигает примерно 80% трафика: тогда вынос чтения разгружает мастер в разы, а добавление новых реплик почти линейно наращивает пропускную способность на чтение.
Плата за это: асинхронность. Данные доезжают до реплики с задержкой (replication lag), и сразу после записи клиент может прочитать с реплики устаревшую версию. Типичный сценарий: пользователь оставил комментарий, страница перезагрузилась с реплики, а комментария ещё нет. Для таких случаев применяют паттерн read-your-writes: чтение только что записанных данных направляют на мастер, остальное на реплики. Мониторинг lag при этом обязателен: реплика, отставшая на минуты, тихо отдаёт неверные ответы.
PostgreSQL создаёт под каждое клиентское соединение отдельный процесс операционной системы, и каждый такой процесс стоит около 5–10 МБ RAM даже в простое; тысяча соединений на сервере с 8 ГБ памяти съедает её впустую. Connection pooling (пул соединений) решает это посредником: PgBouncer держит тысячи клиентских подключений, но к самой БД открывает лишь десятки, переиспользуя их между клиентами. Для продакшена с более чем сотней одновременных соединений пул обязателен, а не опционален.
В режиме transaction pooling PgBouncer переиспользует бэкенд-соединения между транзакциями, а не держит их закреплёнными за клиентом на всю сессию: это и даёт основной прирост эффективной ёмкости при том же числе реальных подключений к БД. Размер бэкенд-пула подбирают по классическому ориентиру (ядра × 2) + число дисков. Пул отвязывает жизненный цикл клиента от дорогого бэкенд-процесса, и один этот слой часто снимает проблему, которую иначе списали бы на нехватку памяти.
Горизонтальное разбиение данных по нескольким серверам: каждый шард: самостоятельная СУБД, которая хранит своё подмножество данных, а маршрутизация идёт по ключу шардирования (например, по user_id). Теоретически масштабируется неограниченно, включая запись. Практически: самая дорогая мера с серьёзной платой за сложность: JOIN между шардами работает только по колонке распределения (co-located данные), а произвольный JOIN между шардами недоступен или дорог; транзакция, затрагивающая несколько шардов, требует двухфазного коммита (2PC); приложение приходится переписывать под топологию шардов.
Для PostgreSQL шардирование чаще всего строят на расширении Citus: оно раскладывает таблицы по узлам через distribution column и держит связанные строки на одном узле (co-location), чтобы JOIN между ними работал локально; JOIN между шардами возможен только по общей колонке распределения, а справочные reference-таблицы синхронизируются через 2PC. Шардирование: последняя мера, к которой переходят, только исчерпав все предыдущие уровни.
| Этап | Стратегия | Сложность | Когда применять |
| 1 | Оптимизация запросов и индексов | Низкая | Всегда первым, до любого железа |
| 2 | Вертикальное масштабирование | Низкая | Быстрый прирост, пока есть потолок |
| 3 | Connection pooling | Низкая | Более 100 одновременных соединений |
| 4 | Read replicas | Средняя | Read-heavy нагрузка (≥ 80% чтения) |
| 5 | Кэширование Redis | Средняя | Повторяющиеся дорогие запросы |
| 6 | Партицирование | Средняя | Таблицы от 100 млн строк |
| 7 | Шардирование | Высокая | Предыдущие уровни исчерпаны |
Стратегии универсальны, но потолки и инструменты у СУБД разные. То, что в одной базе решается штатной фичей, в другой требует внешнего расширения.
Универсальная реляционная СУБД для OLTP-нагрузки (обработки транзакций в реальном времени). Хорошо масштабируется вертикально и через read replicas. Горизонтальное шардирование в ядре отсутствует и решается через Citus или FDW; актуальный релиз на момент публикации: PostgreSQL 18. Главное ограничение: пропускная способность на запись упирается в один мастер. Read replicas разгружают только чтение: при write-heavy нагрузке они не помогают, и это тот случай, когда приходится смотреть в сторону других семейств или шардирования.
Здесь запись можно масштабировать на несколько узлов, чего нет в single-master модели PostgreSQL. Group Replication: мультимастер-репликация с согласованием на основе кворума большинства (majority-consensus): узлы договариваются о порядке транзакций голосованием. Galera Cluster: синхронная мультимастер-репликация, где транзакция подтверждается сразу на всех узлах. Оба подхода дают несколько точек записи и потому подходят для геораспределённой записи, где нельзя гонять все транзакции через один мастер. Плата: накладные расходы на согласование между узлами и чувствительность к сетевым задержкам.
Часто правильный ход: не масштабировать основную реляционную БД до бесконечности, а снять с неё непрофильную нагрузку. Redis берёт на себя кэш и сессии, разгружая PostgreSQL от повторяющихся SELECT. ClickHouse забирает аналитику: тяжёлые агрегирующие запросы убивают OLTP-базу, а колоночная СУБД справляется с ними на другом порядке объёмов. Elasticsearch или OpenSearch закрывают полнотекстовый поиск по каталогу, который в реляционной базе всегда дорог. Подробнее о связке управляемых баз под высоконагруженный сценарий: в материале «E-commerce и DBaaS: масштабируемые решения для ритейла».
Управляемые базы данных VK Cloud снимают операционную часть масштабирования, когда держать DBA в штате дорого или некогда: read replicas, connection pooling и автоматический failover на резервную реплику работают на уровне сервиса. Масштабирование чтения и переключение при сбое мастера настраиваются через панель, а не скриптами на своих серверах. Попробовать DBaaS →
Облако не отменяет законы масштабирования, но убирает ручной труд на каждом уровне. Операции, которые в self-hosted занимают часы настройки, в управляемом сервисе сводятся к нескольким кликам. Важно понимать границу: где облако реально помогает, а где остаётся ответственность архитектора.
Введение облачной СУБД меняет природу масштабирования: вместо трудоемкого проекта оно становится операцией, которая работает через панель управления. Read replica добавляется в несколько кликов вместо ручной настройки репликации. Вертикальное масштабирование делается через resize с минимальным даунтаймом, а не миграцией на новый сервер. Connection pooling и failover работают на стороне провайдера.
VK Cloud DBaaS поддерживает управляемые инстансы MySQL, PostgreSQL, MongoDB, Redis, ClickHouse, Tarantool и OpenSearch. Для разных СУБД доступны конфигурации Single, Master-Replica и Cluster; в зависимости от выбранной топологии сервис берет на себя значительную часть задач администрирования, включая развертывание, управление репликацией, резервное копирование и восстановление.
Для PostgreSQL кластерная конфигурация поддерживает синхронную и асинхронную репликацию, балансировщик нагрузки и автоматическое переключение роли master при недоступности основного узла. Высокая доступность PostgreSQL-кластера обеспечивается службой Patroni: после failover одна из реплик становится новым master, а сервис создает замену выбывшей реплике.
Мультизональная конфигурация повышает устойчивость к отказам инфраструктуры: узлы кластера распределяются между зонами доступности, поэтому выход из строя одного ЦОД не обязательно приводит к недоступности базы. При сохранении кворума кластер автоматически выбирает новый master и продолжает принимать транзакции; такая схема особенно полезна для высоконагруженных систем и сервисов с высокими требованиями к сохранности данных.
Connection pooling устраняет необходимость самостоятельно разворачивать и сопровождать отдельный PgBouncer или аналогичный компонент. Пул соединений ограничивает число прямых подключений к СУБД, снижает накладные расходы на создание сессий, помогает переживать кратковременные всплески запросов и защищает базу от исчерпания доступных подключений. Управление через интерфейс сервиса упрощает настройку и делает поведение подключений более предсказуемым для прикладных команд.
Автомасштабирование ресурсов по заданным порогам позволяет заранее определить реакцию на рост нагрузки — например, на увеличение потребления CPU, памяти или свободного места на диске. Это снижает риск инцидентов из-за нехватки ресурсов, уменьшает объем ручных операций со стороны DBA и DevOps и позволяет не держать постоянно избыточную конфигурацию на всякий случай.
Подробнее о возможностях DBaaS VK Cloud →
Честный взгляд: облако остаётся инструментом, а не подменяет проектирование. Проблема производительности в коде будет актуальной и в managed-базе: провайдер не переписывает запросы приложения. Неправильно спроектированная схема данных не станет быстрее от миграции: денормализация, отсутствие нужных индексов, гигантские таблицы без партицирования переезжают в облако вместе с приложением. DBaaS снимает операционную нагрузку (бэкапы, failover, обновления, мониторинг + вертикальное и горизонтальное масштабирование, автомасштабирование дисков ), но не делает работу архитектора.
| Параметр | Self-hosted | VK Cloud DBaaS |
| Добавить read replica | Ручная настройка, 1–8 ч | Несколько кликов |
| Вертикальное масштабирование | Миграция + даунтайм | Resize, минимальный даунтайм |
| Горизонтальное масштабирование | Проектирование и настройка шардинга, балансировки и межузлового взаимодействия вручную | Масштабирование через read replicas, для шардинга требуется отдельная архитектура |
| Автомасштабирование диска | Кастомный скрипт | Встроено |
| Connection pooler | Отдельная установка PgBouncer | Встроено |
| Failover | Вручную через Patroni | Автоматический |
| Мониторинг метрик | Prometheus + Grafana вручную | Встроенные дашборды |
| Стоимость DevOps | Высокая, DBA в штате | Снижена |
Масштабирование дешевле готовить заранее, чем чинить под нагрузкой. Ниже — минимум, который стоит закрыть, пока метрики в зелёной зоне.
✅ Мониторинг выстроен. CPU, RAM, IOPS, connections, replication lag и slow queries шлют алерты до того, как войдут в красную зону, а не после.
✅ Индексы проверены. Нет отсутствующих индексов на горячих WHERE/JOIN и нет неиспользуемых, которые зря тормозят запись.
✅ Connection pooling настроен. PgBouncer стоит перед базой, если одновременных соединений больше сотни.
✅ Read replica добавлена. Чтение уходит на реплику, мастер занят записью; паттерн read-your-writes продуман для критичных сценариев.
✅ Кэширование внедрено. Redis прикрывает повторяющиеся тяжёлые SELECT.
✅ Тест нагрузки пройден. Точка деградации известна заранее — вы знаете, на каком RPS база начнёт захлёбываться.
✅ Резервное копирование настроено и восстановление проверено. Бэкап без проверенного восстановления не считается бэкапом.
Проблемы масштабирования нарастают постепенно, и это не плохая новость: между первым жёлтым сигналом на дашборде и реальным отказом обычно есть недели, а то и месяцы. Успеет среагировать тот, у кого выстроен мониторинг, кто знает стратегии в правильном порядке и подготовил инфраструктуру заранее.
На практике оптимизация запросов, read replicas и connection pooling закрывают около 80% случаев, когда база данных перестаёт справляться, и делают это дёшево. Шардирование нужно редко и остаётся крайней мерой для по-настоящему больших объёмов. Переход на управляемый VK Cloud DBaaS снимает операционную нагрузку и ускоряет те же самые шаги масштабирования базы данных, но не отменяет работу над схемой и запросами. Порядок действий важнее героизма: сначала измерить, потом применять стратегии снизу вверх.
Начните с диагностики запросов, а не с покупки железа. Включите slow query log, прогоните тяжёлые запросы через EXPLAIN ANALYZE: правильный индекс ускоряет запрос в 10–100 раз и часто снимает проблему целиком. Только после этого смотрите на метрики CPU, RAM, IOPS и connections, и лишь затем переходите к масштабированию инфраструктуры.
Шардирование: последняя мера. Сначала пройдите оптимизацию запросов, вертикальное масштабирование, read replicas, connection pooling и кэширование. К шардированию переходят при объёмах данных от нескольких терабайт или нагрузке от сотен тысяч запросов в секунду, когда все предыдущие уровни исчерпаны, из-за высокой сложности (ограничения на JOIN между шардами, двухфазные транзакции, переписывание приложения).
Вертикальное масштабирование: наращивание ресурсов одного сервера (CPU, RAM, диски); оно простое, но ограничено потолком железа. Горизонтальное масштабирование: добавление новых серверов в виде реплик или шардов; оно сложнее в реализации, но теоретически неограниченно по ёмкости. На практике сначала выжимают выжимают максимум из вертикального масштабирования, затем переходят к горизонтальному
Read replica: копия базы-мастера, которая обслуживает только запросы на чтение. Поскольку в большинстве приложений 80–95% запросов приходится на SELECT, вынос чтения на реплики разгружает мастер, оставляя ему запись. Каждая добавленная реплика почти линейно умножает пропускную способность на чтение; плата — асинхронная задержка (replication lag), которую нужно учитывать для сценариев, где важна свежесть данных.
Наши специалисты свяжутся с вами в ближайшее время и ответят на все вопросы.

Будем держать в курсе новостей и облачных трендов




