Проблема: отчётность, которая не успевала за бизнесом
Год назад я работал аналитиком в компании, которая владела маркетплейсом с оборотом в несколько миллионов долларов в месяц. Каждый понедельник руководство ждало отчёт по продажам за прошлую неделю. И каждый понедельник я чувствовал себя виноватым: дашборд грузился 40–50 секунд, а иногда и вовсе падал с таймаутом. Вишенкой на торте был запрос, который строил воронку продаж: он выполнялся 42 секунды, съедал 100% CPU и блокировал другие отчёты.
В какой-то момент терпение лопнуло. Я решил разобраться, почему так происходит, и использовать AI-ассистента не для написания кода с нуля, а для диагностики и оптимизации. За две недели я сократил время выполнения ключевых запросов с 40+ секунд до 200–500 миллисекунд. Ниже — 14 промтов, которые я использовал. Каждый из них проверен на реальных данных, с реальными индексами и EXPLAIN ANALYZE. Это не теория — это мой рабочий дневник.
1. «Объясни план выполнения, как будто я джун»
Зачем: EXPLAIN ANALYZE выдаёт десятки строк, в которых легко утонуть. Промт заставляет AI перевести план на человеческий язык и указать на самое узкое место.
Промт:
Ты — эксперт по PostgreSQL. Вот вывод EXPLAIN (ANALYZE, BUFFERS) для запроса:
[вставь план]
Объясни простыми словами:
1. Какие операции самые дорогие и почему?
2. Есть ли Seq Scan по большим таблицам?
3. Какие индексы могли бы помочь?
4. Что именно занимает больше всего времени?
Не предлагай переписать запрос, только диагностика.
Реальный пример: Я получил план, где был Seq Scan on orders (cost=0.00..123456.78 rows=1000000 width=120). AI объяснил: «PostgreSQL читает всю таблицу orders, потому что нет индекса по полю created_at, которое используется в WHERE. Это 1 млн строк, отсюда 40 секунд». После создания индекса время упало до 1.2 секунды.
2. «Найди неявные приведения типов»
Зачем: Неявные касты — частая причина, почему индекс не используется. Например, WHERE user_id = '123' (строка вместо числа) или WHERE created_at::date = '2026-09-01'.
Промт:
Проанализируй SQL-запрос на предмет неявных приведений типов, которые мешают использованию индексов. Покажи проблемные места и предложи, как переписать условие, чтобы индекс работал.
Запрос:
[вставь SQL]
Пример: В запросе было WHERE status = 'active' (status — enum), но PostgreSQL всё равно делал Seq Scan. AI подсказал: «Приведение к enum происходит неявно, но если в таблице 90% строк имеют status='active', планировщик может решить, что Seq Scan дешевле. Попробуй частичный индекс: CREATE INDEX idx_orders_active ON orders (created_at) WHERE status = 'active';». Помогло — запрос ускорился в 8 раз.
3. «Сгенерируй индексы под конкретный запрос»
Зачем: Иногда неочевидно, какой именно индекс нужен: составной, частичный или с включением колонок (INCLUDE).
Промт:
У меня есть запрос, который выполняется медленно. Проанализируй его и предложи 2-3 варианта индексов (составной, частичный, с INCLUDE). Для каждого варианта напиши DDL и объясни, в каких случаях он будет эффективен.
Запрос:
[вставь SQL]
Пример: Запрос фильтровал по user_id и сортировал по created_at DESC. AI предложил:
CREATE INDEX idx_orders_user_created ON orders (user_id, created_at DESC);
Время выполнения упало с 12 секунд до 80 мс.
4. «Перепиши коррелированный подзапрос в JOIN»
Зачем: Коррелированные подзапросы в SELECT или WHERE часто выполняются для каждой строки. JOIN обычно быстрее.
Промт:
Перепиши этот запрос, заменив коррелированные подзапросы на JOIN или оконные функции. Сохрани логику. Сравни производительность.
Запрос:
[вставь SQL]
Пример: Было:
SELECT o.id, (SELECT SUM(amount) FROM payments p WHERE p.order_id = o.id) AS total
FROM orders o WHERE o.created_at > '2026-09-01';
Стало:
SELECT o.id, COALESCE(SUM(p.amount), 0) AS total
FROM orders o
LEFT JOIN payments p ON p.order_id = o.id
WHERE o.created_at > '2026-09-01'
GROUP BY o.id;
Ускорение в 6 раз.
5. «Проверь, не делает ли ORM N+1 запросов»
Зачем: Если вы используете ORM (например, SQLAlchemy или Django ORM), AI может проанализировать код и найти проблему N+1.
Промт:
Вот фрагмент кода на Python (SQLAlchemy). Проверь, нет ли проблемы N+1 запросов. Если есть — покажи, как переписать с помощью joinedload или selectinload.
Код:
[вставь код]
Пример: AI нашёл, что в цикле для каждого заказа делается отдельный запрос к таблице users. После замены на joinedload(Order.user) количество запросов сократилось с 1000+ до 1, время ответа API — с 4 секунд до 200 мс.
6. «Настрой autovacuum для горячей таблицы»
Зачем: Если таблица часто обновляется, autovacuum может не успевать, и производительность падает.
Промт:
У меня есть таблица orders, в которую идёт интенсивная запись (10k строк в минуту) и обновление статусов. Предложи параметры autovacuum для этой таблицы, чтобы избежать bloat и не тормозить запросы. Объясни каждый параметр.
Пример: AI рекомендовал:
ALTER TABLE orders SET (autovacuum_vacuum_scale_factor = 0.01, autovacuum_vacuum_threshold = 1000);
После этого bloat снизился, запросы стабилизировались.
7. «Покажи, как использовать pg_stat_statements»
Зачем: Этот модуль — золотая жила для поиска медленных запросов.
Промт:
Объясни, как установить и использовать pg_stat_statements для поиска топ-10 самых ресурсоёмких запросов. Напиши SQL-запрос, который выведет запросы с самым высоким total_exec_time.
Пример: AI дал запрос:
SELECT query, calls, total_exec_time, mean_exec_time
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 10;
Я нашёл запрос, который выполнялся 5000 раз в день и суммарно съедал 2 часа CPU. После оптимизации сэкономил кучу ресурсов.
8. «Перепиши запрос с CTE на временную таблицу»
Зачем: В PostgreSQL CTE (WITH) до версии 12 был optimisation fence. В новых версиях это исправлено, но иногда материализация всё равно мешает.
Промт:
У меня есть запрос с несколькими CTE, который выполняется медленно. Предложи переписать его с использованием временных таблиц или подзапросов. Сравни планы выполнения.
Запрос:
[вставь SQL]
Пример: Запрос с 4 CTE выполнялся 18 секунд. После переписывания на временные таблицы с индексами — 1.5 секунды.
9. «Как разбить большой запрос на части»
Зачем: Если запрос слишком сложный, его можно разбить на несколько материализованных представлений или использовать incremental approach.
Промт:
Этот запрос агрегирует данные за месяц по 10 млн строк. Предложи стратегию разбиения: материализованное представление, партиционирование или pre-aggregation. Опиши плюсы и минусы.
Пример: AI предложил создать материализованное представление, обновляемое ночью. Утром отчёты грузились мгновенно.
10. «Найди блокировки и долгие транзакции»
Зачем: Иногда запрос тормозит не из-за плана, а из-за блокировок.
Промт:
Напиши SQL-запрос к pg_locks и pg_stat_activity, который покажет все текущие блокировки и транзакции, которые их держат. Добавь фильтр по длительности > 5 секунд.
Пример: AI дал запрос, который выявил транзакцию, висящую 10 минут и блокирующую обновление статусов заказов. После kill проблема ушла.
11. «Оптимизируй сортировку и лимиты»
Зачем: ORDER BY с LIMIT может быть медленным, если нет подходящего индекса.
Промт:
Запрос: SELECT * FROM orders ORDER BY created_at DESC LIMIT 100. Предложи индекс, который ускорит эту операцию. Учти, что нужны только последние 100 записей.
Пример: AI предложил:
CREATE INDEX idx_orders_created_desc ON orders (created_at DESC);
Время выборки упало с 3 секунд до 5 мс.
12. «Проверь статистику и обнови её»
Зачем: Планировщик использует статистику. Если она устарела, план может быть неоптимальным.
Промт:
Как часто нужно запускать ANALYZE для таблицы orders? Напиши команду для ручного обновления статистики и объясни, как проверить, когда последний раз обновлялась статистика.
Пример: AI подсказал:
ANALYZE orders;
SELECT last_analyze FROM pg_stat_user_tables WHERE relname = 'orders';
После обновления статистики планировщик выбрал Index Scan вместо Seq Scan.
13. «Сгенерируй тестовые данные для нагрузочного тестирования»
Зачем: Чтобы проверить оптимизацию, нужны реалистичные данные.
Промт:
Напиши скрипт на Python, который сгенерирует 1 млн строк в таблицу orders с полями: id, user_id, amount, status, created_at. Данные должны быть реалистичными: 80% заказов оплачены, 10% отменены, 10% в обработке. created_at — случайные даты за последний год.
Пример: AI сгенерировал скрипт с использованием Faker и psycopg2. Я загрузил данные и протестировал индексы.
14. «Напиши объяснение для руководства»
Зачем: Когда вы оптимизировали запрос, нужно объяснить бизнесу, что изменилось и почему это важно.
Промт:
Напиши краткое объяснение для руководства: как оптимизация SQL-запросов повлияла на скорость отчётов и какие выгоды это принесло. Используй метафоры, избегай технического жаргона.
Пример: AI написал: «Раньше отчёт готовился 40 секунд, теперь — 0.3 секунды. Это как пересесть с лошади на болид Формулы-1. Теперь мы можем строить отчёты в реальном времени и быстрее реагировать на изменения рынка».
Результат
За две недели я оптимизировал 14 запросов. Среднее время выполнения упало с 40 секунд до 300 миллисекунд. Дашборд перестал падать, а руководство начало получать отчёты вовремя. Самое главное — я научился системно подходить к оптимизации: сначала EXPLAIN ANALYZE, потом индексы, потом переписывание запросов. AI-ассистент стал не заменой, а ускорителем: он объяснял планы, предлагал варианты и помогал не упустить детали.
Если вы работаете с PostgreSQL и сталкиваетесь с медленными запросами, начните с промта №1. Дальше — по ситуации. Главное — не бойтесь экспериментировать и всегда проверяйте результат на реальных данных. Удачи!
Источники:
- Официальная документация PostgreSQL: EXPLAIN, Indexes, pg_stat_statements.
- Статья «Understanding EXPLAIN ANALYZE» на PostgreSQL Wiki: https://wiki.postgresql.org/wiki/Using_EXPLAIN
- Книга «PostgreSQL 14 Administration Cookbook» (Simon Riggs, Gianni Ciolli).
Комментарии