PostgreSQL и MongoDB без DBA: 14 промтов, которые находят узкие места, шардируют и переносят данные

PostgreSQL и MongoDB без DBA: 14 промтов, которые находят узкие места, шардируют и переносят данные

Большинство команд не держит отдельного DBA — роль «человека, который разберётся с базой» достаётся бэкендеру, аналитику или тимлиду между задачами. По опыту, узкие места почти всегда одни и те же: индекс не тот, план запроса читают редко, а миграция делается в окно простоя, потому что «так проще». LLM полезна здесь не как оракул, а как быстрый второй пилот: она разбирает EXPLAIN ANALYZE, предлагает DDL, пишет агрегации и проверяет, не сделает ли миграция блокировку таблицы на миллион строк.

Ниже — 14 промтов, которые закрывают реальные задачи: диагностика, индексы, JOIN, схема, миграции, MongoDB-агрегации, репликация, шардирование, N+1 и бэкапы. Каждый промт — с примером входа, ожидаемым результатом и указанием, когда он уместен. Все команды и синтаксис соответствуют официальной документации PostgreSQL 16 и MongoDB 7.0.

Как пользоваться подборкой

Вставляйте промты в ассистента по одному, не смешивая: чем чище вход, тем меньше галлюцинаций. Для тяжёлых запросов добавляйте реальный EXPLAIN (ANALYZE, BUFFERS) — без него любая рекомендация по индексам превращается в гадание. Перед выполнением DDL всегда прогоняйте его на копии схемы.

Промты для PostgreSQL

1. Чтение EXPLAIN ANALYZE и поиск главного тормоза

Задача: запрос идёт 4 секунды, а непонятно почему. Промт заставляет модель разобрать план построчно и указать самую дорогую ноду.

Ты — эксперт по PostgreSQL 16. Ниже план запроса.
Найди 3 главные проблемы по убыванию влияния.
Для каждой укажи ноду, факт из плана (rows, actual time, loops)
и конкретное действие. Отдельно скажи, если оценка rows
отличается от actual в 10+ раз — значит устаревшая статистика.

<вставь вывод EXPLAIN (ANALYZE, BUFFERS)>

Ожидаемый результат: разбор вида «Seq Scan по orders: 1.2 млн строк, actual time 2100 ms — нужен индекс по (user_id, created_at)» плюс предупреждение о расхождении оценок. Когда использовать: первая диагностика любого медленного запроса. Официальная документация по планам — postgresql.org/docs/current/using-explain.html.

2. Подбор индекса под конкретный WHERE

Задача: есть запрос с фильтром по двум колонкам и сортировкой — нужен составной индекс в правильном порядке.

Запрос: SELECT * FROM events WHERE tenant_id = $1
AND created_at > now() - interval '7 days' ORDER BY created_at DESC LIMIT 50;
Таблица: 80 млн строк, tenant_id — 5000 уникальных значений.
Предложи составной индекс, объясни порядок колонок
и почему DESC уместен. Проверь, нужен ли INCLUDE.

Ожидаемый результат: CREATE INDEX CONCURRENTLY idx_events_tenant_created ON events (tenant_id, created_at DESC); с пояснением, что tenant_id идёт первым из-за селективности, а CONCURRENTLY не блокирует запись. Когда использовать: когда EXPLAIN показывает Sort поверх Index Scan.

3. Переписывание медленного JOIN

Задача: JOIN трёх таблиц с агрегацией выполняется по 8 секунд.

Перепиши запрос, не меняя результат. Варианты: заменить JOIN
на EXISTS, вынести агрегацию в CTE, использовать LATERAL.
Для каждого варианта объясни, как изменится план.
Верни финальный SQL и список изменений.

<вставь запрос>

Ожидаемый результат: переписанный SQL + объяснение, что EXISTS вместо JOIN убирает дедупликацию, а LATERAL позволяет использовать индекс во вложенном подзапросе.

4. Проверка DDL на блокировки

Задача: добавить колонку в горячую таблицу и не уронить прод.

Какие блокировки возьмёт ALTER TABLE orders ADD COLUMN note text
DEFAULT 'x' NOT NULL на таблице 200 ГБ?
Что делать в PG 11+ и как избежать перезаписи таблицы?
Дай безопасную последовательность шагов.

Ожидаемый результат: начиная с PG 11 добавление колонки с константным DEFAULT не перезаписывает таблицу — это подтверждается release notes PG 11. Для volatile-дефолтов и смены типа — пошаговый план с ACCESS EXCLUSIVE и lock_timeout.

5. Проектирование схемы из требований

Задача: превратить бизнес-описание в нормализованную схему.

Спроектируй схему для сервиса бронирования: пользователи,
помещения, слоты, оплаты, отмены. Дай CREATE TABLE
с типами, PK, FK, CHECK. Отдельно предложи, где денормализовать
для скорости чтения и почему.

Ожидаемый результат: DDL с timestamptz, numeric для денег, частичными индексами и обоснованием, почему статус лучше enum, чем text.

6. Миграция без даунтайма

Задача: переименовать колонку на таблице, которая всегда под нагрузкой.

Нужно переименовать колонку user_name в username
на таблице 50 млн строк без простоя.
Дай план expand-contract: новые и старые поля, триггер или
двойная запись, бэкфилл батчами, финальный DROP.

Ожидаемый результат: последовательность фаз с окнами бэкфилла по 10 тыс. строк и проверкой отставания реплики. Подход expand-contract описан в документации по ALTER TABLE.

7. Защита от N+1 на уровне запроса

Задача: ORM делает сотни мелких запросов вместо одного.

Вот код на бэкенде: цикл по заказам, внутри — запрос к items.
Покажи N+1, перепиши одним запросом с JOIN или
array_agg. Сравни количество round-trip.

Ожидаемый результат: один запрос с array_agg или jsonb_agg, экономия сотен сетевых вызовов.

Промты для MongoDB

8. Анализ explain() и подбор индекса

Задача: агрегация в MongoDB сканирует всю коллекцию.

Коллекция orders, 30 млн документов.
Запрос: db.orders.find({status:'paid', region:'EU'}).sort({createdAt:-1}).limit(20)
Вот вывод explain('executionStats').
Скажи, COLLSCAN или IXSCAN, сколько документов просмотрено,
и предложи индекс. Учти ESR-правило (Equality, Sort, Range).

Ожидаемый результат: db.orders.createIndex({status:1, region:1, createdAt:-1}) с пояснением ESR. Документация по ESR — mongodb.com/docs/manual/tutorial/equality-sort-range-rule.

9. Агрегационный пайплайн вместо кода приложения

Задача: посчитать топ регионов по выручке за месяц.

Напиши aggregation pipeline: сгруппируй заказы за последние 30 дней
по региону, посчитай сумму и средний чек, отфильтруй регионы
с суммой ниже 10000, отсортируй по убыванию.
Поставь $match первым стейджем и объясни почему.

Ожидаемый результат: пайплайн с $match → $group → $match → $sort, где первый $match использует индекс. Официальный справочник — mongodb.com/docs/manual/core/aggregation-pipeline.

10. Проектирование схемы документа

Задача: выбрать между embedded и referencing для новых сущностей.

Сервис: пользователи, посты, комментарии, лайки.
Что встроить, что вынести в отдельные коллекции?
Учти лимит документа 16 МБ и паттерн bucket для временных рядов.
Дай примеры документов.

Ожидаемый результат: комментарии — embedded до N штук с выносом хвоста, лайки — отдельная коллекция с уникальным составным индексом.

11. Репликация: чтение с secondary

Задача: снять нагрузку чтения с primary.

Настройка readPreference=secondaryPreferred в приложении.
Какие риски: устаревшие данные при чтении после записи? Дай
стратегию: где оставить primary, где secondary, как проверить
отставание репликации (rs.status()).

Ожидаемый результат: разбор read-your-writes, рекомендация оставить критичные чтения на primary, команда проверки rs.printSecondaryReplicationInfo().

12. Шардирование: выбор ключа

Задача: коллекция растёт, вертикально масштабировать больше нельзя.

Коллекция 500 млн документов. Предложи ключ шардирования
(hashed vs ranged) для tenant_id и createdAt.
Объясни, как ключ влияет на балансировку и целевые запросы.
Дай команды sh.shardCollection и включения шардирования БД.

Ожидаемый результат: для равномерности — hashed-ключ, для range-запросов — составной. Команды: sh.enableSharding('app'), sh.shardCollection('app.orders', {tenantId:'hashed'}). Документация — mongodb.com/docs/manual/sharding.

Промты для обеих баз

13. Миграция PostgreSQL → MongoDB

Задача: перенести часть данных в документную модель.

Перенеси таблицы orders, order_items, payments в коллекции.
Предложи схему с embedded items и ссылкой на payments.
Напиши скрипт миграции на Python с батчами по 1000
и проверкой количества. Учти типы: numeric → Decimal128, timestamptz → Date.

Ожидаемый результат: скрипт с insert_many, маппингом типов и сверкой count_documents после переноса.

14. Стратегия бэкапов и восстановления

Задача: проверить, что бэкап реально восстанавливается.

PostgreSQL: pg_dump vs pg_basebackup vs WAL-архивирование
для RPO 5 минут. MongoDB: mongodump vs oplog vs snapshot.
Дай команды, частоту и обязательный шаг проверки восстановления
на отдельном стенде.

Ожидаемый результат: для RPO 5 минут — pg_basebackup + непрерывное архивирование WAL через archive_command; для MongoDB — oplog-бэкапы. Ключевой шаг — регулярный restore-тест: непроверенный бэкап не считается бэкапом.

Сценарий Промт Что даёт
Диагностика 1, 8 Находит главный тормоз в плане
Индексы 2, 8 Готовый DDL с обоснованием
Миграции 4, 6, 13 План без простоя
Масштабирование 11, 12 Ключ шардирования и репликация
Надёжность 14 Восстановление, а не только дамп

Что в итоге

Эти 14 промтов не заменяют DBA, но закрывают 80% ежедневных задач: понять план, поставить индекс, безопасно мигрировать, спроектировать документ, выбрать ключ шардирования. Главное правило — всегда давайте модели реальные факты: план запроса, размер таблицы, вывод explain(). Без них ответ будет общим, с ними — применимым.

Начните с промта №1 на самом медленном запросе вашего сервиса: чаще всего один правильный индекс снимает нагрузку заметнее, чем любая оптимизация кода.

← All posts

Comments