Управляемые базы данных: RDS, Aurora и DynamoDB
Управляемая реляционная (RDS/Aurora) против NoSQL (DynamoDB). RDS снимает патчинг, бэкапы и failover, но оставляет тебе схему и вертикальный потолок; DynamoDB меняет джойны на single-digit-ms на любом масштабе — если проектировать ключи, чтобы обходить hot partition.
За неделю до запуска команда переключает базу RDS на Multi-AZ, читает на слайде «высокая доступность» и говорит залу: «теперь чтение прикрыто». Нет. Утренний всплеск трафика, read-heavy страница товара ползёт, единственный primary упёрт в 100% CPU, а совершенно здоровый standby сидит в другой AZ и не делает ничего, кроме ожидания смерти primary. Multi-AZ не обслужил ни одного чтения — standby это синхронный дублёр для failover, а не второй читатель. Решением в тот день была read replica; урок в том, что «HA» и «масштабирование чтения» — разные фичи, которые просто обе включают вторую копию базы, и путаница между ними стоит тебе аварии в самый загруженный час.
RDS и Aurora: управляемая реляционная, с тем, чем владеешь ты
Если ты хоть раз дебажил ночной пейджер из-за патча базы или вручную настраивал standby-репликацию в выходные, то поймёшь привлекательность управляемой реляционной — но «управляемый» покрывает конкретный список вещей, и именно в зазоре между тем, что берёт AWS, и тем, чем владеешь ты, прячутся production-инциденты.
Amazon RDS запускает знакомый тебе реляционный движок — PostgreSQL, MySQL, MariaDB, Oracle или SQL Server — и снимает операционную рутину. AWS владеет патчингом ОС и движка, автоматическими бэкапами и point-in-time recovery, а также механикой failover. Aurora — облачно-нативный вариант: MySQL- и PostgreSQL-совместимый движок, где AWS заменил слой хранения распределённым, log-structured парком, реплицируемым шесть раз через три AZ, что даёт более быстрый failover и read replicas, делящие один том хранения. В любом случае ты получаешь SQL: вторичные индексы, многострочные транзакции и джойны по нормализованным таблицам.
Чего AWS не снимает с тебя — это часть, которую джуны считают автоматической. Ты по-прежнему владеешь схемой, индексами и тюнингом запросов — отсутствующий индекс или N+1-паттерн запросов это твой баг, а не AWS, и управляемость не спасёт таблицу, делающую sequential scan. И реляционная на RDS масштабируется вертикально: ты делаешь инстанс больше. У этого есть потолок — самый большой класс инстанса это жёсткая стена, и записи фундаментально идут через один primary.
Две фичи постоянно смешивают, а смешивать нельзя. Multi-AZ выделяет синхронную standby-реплику в другой Availability Zone; каждый commit реплицируется на неё до подтверждения. Если primary падает, RDS автоматически переключает DNS-запись на standby, обычно в диапазоне 60–120 секунд. Но AWS прямо говорит: развёртывание Multi-AZ с одним standby «не является решением для масштабирования сценариев только-для-чтения. Нельзя использовать standby-реплику для обслуживания трафика чтения». Это высокая доступность, а не масштабирование чтения. Чтобы масштабировать чтение, добавляют read replicas: они реплицируются асинхронно (поэтому могут отставать), ты направляешь трафик чтения на их собственные эндпоинты, и — что важно — read replica может быть повышена (promote) до самостоятельного primary, частый путь для миграций и региональной DR.
# Multi-AZ = HA: синхронный standby, который НЕ обслуживает чтение
aws rds create-db-instance \
--db-instance-identifier orders-primary \
--engine postgres \
--db-instance-class db.r6g.xlarge \
--multi-az # автоматический failover, ~60-120с, НЕ масштабатор чтения
# Read replica = масштабирование чтения: асинхронно, свой эндпоинт, promotable
aws rds create-db-instance-read-replica \
--db-instance-identifier orders-replica-1 \
--source-db-instance-identifier orders-primaryDynamoDB: serverless NoSQL, живущая и умирающая по ключу партиции
DynamoDB — полностью управляемое, serverless key-value и документное хранилище. Нет инстансов, которые надо подбирать по размеру — ты отдаёшь AWS таблицу и паттерны доступа. У каждого элемента есть partition key (ключ партиции); DynamoDB прогоняет этот ключ через внутреннюю хеш-функцию, и выход выбирает физическую партицию, в которой живёт элемент. Опциональный sort key упорядочивает элементы с одним ключом партиции, давая range-запросы внутри одной коллекции. Сделанное верно, это даёт латентность в single-digit-миллисекунды на практически любом масштабе, потому что запрос резолвится в одну партицию хешированием, а не сканом.
Ты выбираешь режим ёмкости. On-demand тарифицирует за запрос и автомасштабируется мгновенно — идеально для непредсказуемого или всплескового трафика. Provisioned заставляет объявить read capacity units (RCU) и write capacity units (WCU), опционально с auto scaling. Один WCU — это запись 1 КБ/с; один RCU — strongly-consistent чтение 4 КБ в секунду (или два eventually-consistent чтения). Для более богатого доступа добавляют GSI (global secondary index) — другой ключ партиции, реплицированный и eventually consistent — или LSI (local secondary index), альтернативный sort key на том же ключе партиции.
Вот ловушка, которая троттлит команды, думающие, что у них есть запас: hot partition (горячая партиция). AWS проектирует каждую партицию выдавать максимум 3000 read units/сек и 1000 write units/сек. Если одно значение ключа партиции бьётся непропорционально — звёздный пользователь, enum status = "PENDING", сегодняшняя дата как ключ — весь этот трафик ложится на одну партицию, упирается в этот per-partition потолок, и тебя троттлит, хотя суммарная provisioned-ёмкость таблицы едва задействована. Вся игра в том, чтобы проектировать ключи с высокой кардинальностью, чтобы нагрузка расходилась равномерно; для неизбежно горячих ключей делают write-sharding, добавляя суффикс к ключу. И у DynamoDB нет джойнов и нет ad-hoc запросов — ты моделируешь под известные паттерны доступа заранее, часто через single-table design, и выбираешь eventual vs strong consistency на каждое чтение.
// Один элемент, смоделированный для доступа по пользователю, затем по недавнему заказу (sort key)
{
"PK": { "S": "USER#42" }, // ключ партиции: высокая кардинальность, расходит нагрузку
"SK": { "S": "ORDER#2026-06-04" }, // sort key: range-запросы внутри пользователя
"total": { "N": "129.00" },
"status": { "S": "PENDING" } // НЕ делай это ключом партиции: hot partition
}| Измерение | RDS / Aurora (реляционная) | DynamoDB (NoSQL) |
|---|---|---|
| Модель запросов | SQL: джойны, ad-hoc запросы, транзакции | По ключу; модель под паттерны доступа, без джойнов |
| Масштабирование | Вертикальное (больше инстанс) + read replicas; есть потолок | Горизонтальное по партициям; практически неограниченное |
| Высокая доступность | Multi-AZ синхронный standby, failover ~60–120с | Встроенная; реплицируется по 3 AZ автоматически |
| Латентность на масштабе | Зависит от индексов/тюнинга; деградирует под нагрузкой | Single-digit ms, если ключи расходятся равномерно |
| Классический режим отказа | Упор в вертикальный потолок; допущение, что Multi-AZ масштабирует чтение | Троттлинг hot partition; нужен джойн, который не сделать |
▸Почему это работает
Почему hot partition троттлит тебя, когда дашборд показывает свободную ёмкость? Per-partition лимиты DynamoDB (3000 RCU/сек, 1000 WCU/сек) физические, а не логические. Суммарная ёмкость таблицы делится между партициями, поэтому 100 000 provisioned WCU, размазанные по многим партициям, бесполезны единственному ключу, который маппится в одну партицию — этот ключ всё равно упирается в 1000 WCU/сек, а остальное уходит в троттлинг. Adaptive capacity может одолжить горячей партиции часть неиспользованной пропускной способности соседей, что смягчает, но не стирает лимит. Единственный надёжный фикс — дизайн ключа: расходи записи так, чтобы ни одна физическая партиция не была бутылочным горлышком.
Решение: богатые запросы против простого огромного масштаба
Правило, которое несут сеньоры: реляционная с богатыми запросами, транзакциями, джойнами и умеренным масштабом → RDS/Aurora; огромный масштаб, простой доступ по ключу, предсказуемая латентность и serverless-операции → DynamoDB. Тарификация обоих иллюстративна и зависит от региона — сверяй текущие ставки на страницах RDS pricing и DynamoDB pricing — но форма различается: RDS тарифицирует за инстанс-час плюс хранение независимо от загрузки; DynamoDB on-demand тарифицирует за запрос и реально падает к нулю в простое.
Классическая катастрофа lift-and-shift — взять нормализованную реляционную схему, скопировать её таблица-в-таблицу на DynamoDB «ради масштаба», а потом обнаружить, что приложению нужен джойн, который DynamoDB сделать не может — вынуждая N round-trip или скан, убивающий весь смысл. Моделируй паттерны доступа сначала; если им нужны ad-hoc джойны, тебе была нужна реляционная база.
Хранилище состояния сессий многопользовательской игры: сотни тысяч записей/сек на пике, доступ только по ключу sessionId, нужна предсказуемая низкая латентность, и команда хочет ноль серверов БД в эксплуатации. Выбери базу.
Ты включаешь RDS Multi-AZ и ждёшь, что read-heavy трафик ускорится. Не ускоряется. Почему?
Твоя таблица DynamoDB provisioned с кучей суммарной ёмкости, но один паттерн доступа по ключу status постоянно троттлится. Что происходит?
- 01Сопоставь RDS Multi-AZ с read replicas и скажи, чем RDS управляет за тебя, а чем нет.
- 02Объясни модель партиций DynamoDB и ловушку hot partition, и когда выбирать DynamoDB вместо RDS.
Управляемые базы данных в AWS делятся на две семьи. RDS и Aurora — управляемая реляционная: AWS владеет патчингом, бэкапами и failover, и ты получаешь SQL с джойнами, транзакциями и ad-hoc запросами — но ты по-прежнему владеешь схемой, индексами и тюнингом запросов и масштабируешься вертикально в жёсткий потолок. Две фичи RDS постоянно путают: Multi-AZ — синхронный standby в другой AZ, делающий failover автоматически примерно за 60–120 секунд и не обслуживающий чтение (высокая доступность, а не масштабирование чтения), тогда как read replicas асинхронны, имеют собственные эндпоинты для поглощения нагрузки чтения и могут быть повышены до primary. DynamoDB — serverless NoSQL: ключ партиции хешируется для выбора физической партиции, опциональный sort key упорядочивает элементы внутри неё, и ты получаешь латентность в single-digit-миллисекунды на любом масштабе — при условии, что ключи расходятся равномерно. Каждая партиция упирается примерно в 3000 read units и 1000 write units в секунду, поэтому низкокардинальный ключ порождает hot partition, который троттлит тебя при простаивающей суммарной ёмкости; лекарство — высококардинальные ключи или write-sharding. У DynamoDB нет джойнов, поэтому ты моделируешь под паттерны доступа заранее. Решение — это весь урок: богатые запросы, транзакции и джойны на умеренном масштабе хотят RDS/Aurora; огромный масштаб, простой доступ по ключу, предсказуемая латентность и ноль серверов в эксплуатации хотят DynamoDB. Цены иллюстративны и зависят от региона — всегда сверяй на страницах тарификации RDS и DynamoDB. Теперь, когда на ревью встретишь выбор базы, задай два вопроса: требует ли паттерн доступа джойнов или ad-hoc запросов — и если выбор пал на DynamoDB, какова кардинальность ключа партиции, потому что именно этот атрибут решит, получишь ли ты single-digit-ms или шторм троттлинга.
Практика
Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.
Что-то непонятно?
Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.
Примени это
Примени этот урок в реальном проекте.