← Назад к вопросам

Что такое Explain?

2.0 Middle🔥 161 комментариев
#SOLID и паттерны проектирования#Spring Framework

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое EXPLAIN (в контексте SQL)

Определение

EXPLAIN — это команда в SQL, которая показывает план выполнения запроса (query execution plan) базой данных. Она не выполняет сам запрос, а только показывает, как база данных планирует его выполнить.

Зачем это нужно

Основная цель EXPLAIN — оптимизация производительности SQL запросов:

  • Найти узкие места — какие операции медленные
  • Понять, используются ли индексы — правильно ли работает индексация
  • Выявить полные сканирования таблиц — когда база читает все строки
  • Оптимизировать запрос — переписать для лучшей производительности
  • Предсказать нагрузку — как запрос повлияет на систему

Синтаксис

EXPLAIN SELECT * FROM users WHERE id = 123;
EXPLAIN ANALYZE SELECT * FROM users WHERE age > 30;
EXPLAIN (FORMAT JSON) SELECT * FROM orders;

Простой пример

-- Без индекса
EXPLAIN SELECT * FROM users WHERE name = John;

-- Результат:
-- Seq Scan on users (cost=0.00..35.00 rows=1)
--   Filter: (name = John)

Этот вывод означает:

  • Seq Scan — последовательное сканирование (читает все строки)
  • cost=0.00..35.00 — приблизительная стоимость (время выполнения)
  • rows=1 — ожидается 1 строка

EXPLAIN ANALYZE — полная информация

EXPLAIN ANALYZE SELECT * FROM users WHERE name = John;

-- Результат:
-- Seq Scan on users (cost=0.00..35.00 rows=1 width=200)
--   Filter: (name = John)
--   Planning Time: 0.152 ms
--   Execution Time: 2.351 ms
--   Actual rows: 1

Здесь видно:

  • Actual rows — сколько строк реально было
  • Execution Time — реальное время выполнения

Типичные операции в плане

1. Seq Scan (Sequential Scan) — ПЛОХО

Полное сканирование таблицы — читает ВСЕ строки.

EXPLAIN SELECT * FROM orders WHERE status = pending;
-- Seq Scan on orders (cost=0.00..1000.00 rows=5000)
--   Filter: (status = pending)

Проблема: если 5000 строк, прочитаны все. Медленно на больших таблицах.

2. Index Scan — ХОРОШО

Использование индекса для быстрого поиска.

CREATE INDEX idx_status ON orders(status);

EXPLAIN SELECT * FROM orders WHERE status = pending;
-- Index Scan using idx_status on orders
--   (cost=0.29..10.00 rows=5000)
--   Index Cond: (status = pending)

Преимущество: база быстро находит нужные строки через индекс.

3. Join Operations

EXPLAIN SELECT u.name, o.amount FROM users u
  INNER JOIN orders o ON u.id = o.user_id
  WHERE u.id = 123;

-- Nested Loop (cost=0.56..20.00 rows=10)
--   -> Index Scan using idx_users_id on users (cost=0.29..8.00 rows=1)
--   -> Index Scan using idx_orders_user_id on orders (cost=0.29..10.00 rows=10)

Типы JOIN операций

  • Nested Loop — для маленьких таблиц или индексированных соединений
  • Hash Join — для больших таблиц, когда нет индекса
  • Merge Join — для отсортированных данных

Java код для анализа EXPLAIN

import java.sql.*;

public class ExplainAnalyzer {
    
    public static void analyzeQuery(String query) throws SQLException {
        try (Connection conn = DriverManager.getConnection(
                "jdbc:postgresql://localhost/mydb",
                "user",
                "password")) {
            
            // Используем EXPLAIN ANALYZE
            String explainQuery = "EXPLAIN ANALYZE " + query;
            
            try (Statement stmt = conn.createStatement();
                 ResultSet rs = stmt.executeQuery(explainQuery)) {
                
                System.out.println("=== Query Plan ===");
                while (rs.next()) {
                    System.out.println(rs.getString(1));
                }
            }
        }
    }
    
    public static void main(String[] args) throws SQLException {
        String query = "SELECT * FROM users WHERE id = 123";
        analyzeQuery(query);
    }
}

Интерпретация метрик

Cost

Format: cost=X..Y

  • X — стоимость получения первой строки
  • Y — стоимость получения всех строк
-- cost=0.00..35.00 означает:
-- 0.00 единиц стоимости для первой строки
-- 35.00 единиц стоимости для всех строк

Rows

Плановое количество строк (может отличаться от реального).

-- Если план говорит rows=100, а реально 10000 → проблема!
-- Это может привести к неоптимальному выбору алгоритма

Пример: индексирование улучшает производительность

public class QueryOptimization {
    
    // ДО оптимизации
    public void slowQuery() throws SQLException {
        String sql = "SELECT * FROM orders WHERE created_date > 2024-01-01";
        // EXPLAIN показывает:
        // Seq Scan on orders (cost=0.00..5000.00 rows=50000)
        // Planning Time: 0.1 ms
        // Execution Time: 2500 ms  ← ОЧЕНЬ МЕДЛЕННО!
    }
    
    // ПОСЛЕ оптимизации - добавим индекс
    public void optimizedQuery() throws SQLException {
        // Создали индекс
        // CREATE INDEX idx_orders_created ON orders(created_date);
        
        String sql = "SELECT * FROM orders WHERE created_date > 2024-01-01";
        // EXPLAIN показывает:
        // Index Scan using idx_orders_created on orders
        // Planning Time: 0.2 ms
        // Execution Time: 50 ms  ← ВЫ в 50 раз быстрее!
    }
}

Красные флаги при анализе EXPLAIN

-- 🔴 Seq Scan вместо Index Scan
EXPLAIN SELECT * FROM big_table WHERE id = 123;
-- Seq Scan on big_table ← ПЛОХО! Должен быть индекс

-- 🔴 Hash Join вместо Nested Loop
-- Hash Join может быть медленнее

-- 🔴 Rows не совпадают с actual rows
EXPLAIN ANALYZE SELECT * FROM orders WHERE status = pending;
-- rows=100, Actual rows: 50000 ← ОЧЕНЬ неправильно!
-- Статистика устарела, нужно ANALYZE TABLE

-- 🔴 Слишком высокая стоимость
-- cost > 100000 для простого запроса ← проблема

Решение проблем

-- Проблема: Seq Scan вместо индекса
CREATE INDEX idx_email ON users(email);

-- Проблема: неправильная статистика
ANALYZE users;  -- PostgreSQL
ANALYZE TABLE users;  -- MySQL

-- Проблема: сложный JOIN
-- Переписать запрос или добавить индексы на foreign keys

-- Проблема: слишком много фильтров
SELECT * FROM users WHERE age > 30 AND status = active AND country = US;
-- Может потребоваться composite индекс
CREATE INDEX idx_users_composite ON users(age, status, country);

Инструменты для анализа

  • pgAdmin (PostgreSQL) — графический интерфейс для EXPLAIN
  • MySQL Workbench (MySQL) — встроенный EXPLAIN анализатор
  • DBeaver — универсальный инструмент для всех БД
  • explain.depesz.com — онлайн визуализация EXPLAIN результатов

Лучшие практики

  1. Всегда проверяй EXPLAIN для медленных запросов
  2. Используй EXPLAIN ANALYZE для реальных метрик
  3. Создавай индексы на WHERE условия и JOIN поля
  4. Следи за Seq Scan на больших таблицах
  5. Обновляй статистику регулярно

Итоговый вывод

EXPLAIN — это критический инструмент для оптимизации SQL. Он показывает, как база данных собирается выполнить запрос, позволяя разработчику найти узкие места и добавить нужные индексы. В production среде медленный SQL может стать причиной падения всего приложения, поэтому понимание EXPLAIN — это обязательный навык SQL разработчика.

Что такое Explain? | PrepBro