Действуй как DBA с опытом тюнинга OLTP под нагрузкой. Оптимизируй запрос:
{{query}}
СУБД: {{engine}}. Цель: {{target_time}}.
Шаги
1. Сними честный план
EXPLAIN (ANALYZE, BUFFERS, VERBOSE, FORMAT TEXT)— обязательно ANALYZE, иначе видишь только оценки- Прогрей кеш: запусти 2-3 раза, бери последний прогон
- Покажи также
pg_stat_statementsдля этого запроса:mean_exec_time,calls,shared_blks_hit/read
2. Читай план снизу вверх по дереву
Для каждого узла отметь:
- actual rows vs estimated — расхождение >10× = устаревшая статистика или плохая селективность
- actual time vs total cost — где время реально тратится
- loops — количество итераций (Nested Loop с loops=100k = беда)
- Buffers: shared hit/read — read = с диска, hit = из кеша
3. Классифицируй проблему
| Симптом в плане | Диагноз | Фикс |
|---|---|---|
| Seq Scan на большой таблице с фильтром | Нет индекса или WHERE не sargable | CREATE INDEX или переписать предикат |
| Index Scan но Rows Removed by Filter > 0 | Индекс покрывает не все условия | Композитный индекс или INCLUDE |
| Nested Loop с большим loops | Оптимизатор недооценил селективность | ANALYZE, расширенная статистика, или хинт join |
| Hash Join с Batches > 1 | Не хватает work_mem | Поднять work_mem для сессии |
| Sort: external merge Disk | Сортировка не влезает в память | work_mem, или индекс с правильным порядком |
| BitmapHeapScan с lossy heap blocks | Bitmap не помещается в work_mem | work_mem или более селективный индекс |
4. Проверь sargability предикатов
Эти переписывания обычно дают порядки:
WHERE date_trunc('day', created_at) = '2026-01-01'→WHERE created_at >= '2026-01-01' AND created_at < '2026-01-02'WHERE lower(email) = ?→ функциональный индекс(lower(email))или хранить нормализованнымWHERE id::text = ?→ убрать каст, привести параметр к типу колонкиWHERE col + 1 = ?→WHERE col = ? - 1
5. Проверь join order
Если оптимизатор выбирает плохой порядок:
- Запусти
ANALYZEна участвующих таблицах - Создай
CREATE STATISTICS ... ON (col_a, col_b) FROM tableдля коррелированных колонок - В крайнем случае — материализуй промежуточный результат через CTE с
MATERIALIZED
6. Покажи финальный план
До/после, с цифрами: planning time, execution time, buffers. Целевое улучшение должно быть видно в actual time, а не только в cost.
Формат вывода
## Диагноз
- Корневая проблема: ...
- Второстепенные: ...
## Изменения
1. CREATE INDEX ... — обоснование
2. Переписано WHERE — обоснование
## План ДО
[EXPLAIN ANALYZE до]
## План ПОСЛЕ
[EXPLAIN ANALYZE после]
## Метрики
| | До | После |
| Execution time | 4200ms | 38ms |
| Shared reads | 18420 | 12 |
Анти-паттерны
- ❌ Смотреть на
costвместоactual time— cost это оценка, врёт регулярно - ❌ Создавать индекс на каждую колонку из WHERE — индексы стоят на запись
- ❌ Покрывающий индекс с 8 колонками — переписывает планировщик, кеш страдает
- ❌
OPTIMIZE FORхинты без понимания — фиксируешь сегодняшнюю статистику - ❌ Игнорировать
Rows Removed by Filter— это работа впустую
Объясни мне X — как ребёнку 10 лет
Универсальный промт «расскажи без жаргона». Подставь любой непонятный термин или концепт — получишь объяснение, которое можно понять с первого раза.
Аудит производительности (Core Web Vitals)
Глубокая проверка LCP, INP, CLS с привязкой к коду и приоритизированным планом исправлений.
Мастер-аудит сайта: 6 измерений за один проход
Orchestrator-аудит по 6 направлениям: UX, accessibility, performance, SEO, brand consistency, security. Quick scan + deep dive + приоритизированный план + композитная оценка + roadmap.