04База данных
Оптимизация SQL-запроса
От EXPLAIN до индексов, переписки запроса и материализованных views.
Оптимизируй запрос:
{{query}}
Подход: измерь → пойми → исправь → проверь.
1. Замерь baseline
EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT)
{{query}};
Запиши:
- Total time
- Самые дорогие операции
- Какие строки сканируются vs нужны
2. Прочитай план
Снизу вверх. Ищи:
| Что | Что значит | Часто фиксится |
|---|---|---|
| Seq Scan на большой таблице | Нет индекса или статистика устарела | Создать индекс / ANALYZE |
| Nested Loop с большим outer rows | Неэффективный JOIN | Hash Join, переписать |
| Index Scan + Filter много строк | Часть условия не покрывается индексом | Composite index |
| Sort на много строк | ORDER BY без подходящего индекса | Композитный индекс с DESC |
| Hash на много памяти | Нужен work_mem | Tune work_mem |
| External merge | Не помещается в work_mem | Увеличить work_mem или переписать |
3. Распространённые фиксы
A. Отсутствующий индекс
-- Запрос:
SELECT * FROM orders WHERE customer_id = 123 ORDER BY created_at DESC;
-- Индекс:
CREATE INDEX idx_orders_customer_created ON orders (customer_id, created_at DESC);
B. SELECT *
-- плохо
SELECT * FROM users WHERE id = 1;
-- хорошо — только нужные поля
SELECT id, name, email FROM users WHERE id = 1;
Особенно важно если есть TEXT/JSONB колонки которые не нужны.
C. N+1
-- плохо: цикл с подзапросом
SELECT * FROM orders;
-- для каждого ↓
SELECT * FROM users WHERE id = ?;
-- хорошо: JOIN
SELECT o.*, u.name FROM orders o JOIN users u ON u.id = o.user_id;
-- или ORM: include / preload
D. JOIN order
PostgreSQL обычно выбирает сам, но если запрос сложный — иногда нужно подсказать:
-- Маленькую таблицу левее в JOIN
FROM small_table s
JOIN huge_table h ON s.id = h.small_id
E. LIMIT без ORDER BY
-- может вернуть случайные строки
SELECT * FROM orders LIMIT 10;
-- лучше явный порядок
SELECT * FROM orders ORDER BY created_at DESC LIMIT 10;
F. WHERE на функции
-- плохо — функция блокирует индекс
WHERE LOWER(email) = 'foo@bar.com'
-- хорошо — функциональный индекс или нормализованная колонка
CREATE INDEX idx_users_email_lower ON users (LOWER(email));
G. OR через UNION
-- иногда быстрее
SELECT * FROM t WHERE a = 1
UNION ALL
SELECT * FROM t WHERE b = 2;
-- чем
SELECT * FROM t WHERE a = 1 OR b = 2;
H. EXISTS вместо JOIN для existence-check
-- быстрее
WHERE EXISTS (SELECT 1 FROM child WHERE child.parent_id = parent.id)
-- медленнее (JOIN + DISTINCT)
SELECT DISTINCT parent.* FROM parent JOIN child ON ...
4. Когда переписать
- Если запрос постоянно медленный и индексы не помогают — ищи структурную проблему
- Может нужна денормализация (например, счётчик)
- Может материализованное view (для тяжёлой агрегации)
- Может CTE для читаемости
5. Когда индексы не панацея
- Если запрос возвращает > 10% строк таблицы — Seq Scan может быть быстрее
- Если table очень маленькая — Seq Scan быстрее
- Если есть много INSERT/UPDATE — индексы замедляют их
6. Финальная проверка
После изменений:
- Повторный EXPLAIN ANALYZE
- Сравни total time
- Проверь на разных размерах данных (не только маленьких)
- Убедись что план стабилен (PostgreSQL может выбрать другой план при росте данных)
Анти-паттерны
- ❌ "Просто добавь индекс" не глядя на план
- ❌ Создание индекса в продакшене во время пика трафика (используй
CREATE INDEX CONCURRENTLY) - ❌ ORDER BY RAND() — пересортирует всю таблицу
- ❌ Сравнение времени без EXPLAIN — может быть кеш
В конце
- Текущий план
- Найденные проблемы
- Предложенные изменения (с обоснованием)
- Ожидаемый импакт
Похожие промты
site / auditFeatured
Аудит производительности (Core Web Vitals)
Глубокая проверка LCP, INP, CLS с привязкой к коду и приоритизированным планом исправлений.
performancecore web vitalslighthouse
Открыть
Продвинутый30-60 мин
site / auditFeatured
Мастер-аудит сайта: 6 измерений за один проход
Orchestrator-аудит по 6 направлениям: UX, accessibility, performance, SEO, brand consistency, security. Quick scan + deep dive + приоритизированный план + композитная оценка + roadmap.
auditorchestratorux
Открыть
Продвинутый2-4 часа
site / audit
Performance budget по типам страниц
Бюджеты JS/CSS/images для разных типов страниц, целевые Web Vitals, enforcement в CI с конкретными порогами.
performancebudgetci
Открыть
Продвинутый1-2 часа