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

Может ли программист влиять на планировщик в БД?

2.3 Middle🔥 141 комментариев
#Базы данных (SQL)

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

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

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

Может ли программист влиять на планировщик в БД?

Прямой ответ: ДА

Программист имеет довольно ограниченное, но реальное влияние на query planner (планировщик запросов) в базе данных. Это влияние косвенное - через написание правильных запросов и использование подсказок.

Что такое Query Planner?

Query Planner - это компонент СУБД, который решает ОПТИМальный способ выполнения SQL запроса. Он анализирует:

  • Наличие индексов
  • Статистику данных (количество строк, распределение значений)
  • Стоимость различных операций (доступ к диску, сравнение)
  • Различные варианты плана выполнения
-- Одна и та же цель может быть выполнена несколькими способами:
SELECT * FROM users WHERE age > 25 AND city = 'Moscow';

-- Вариант 1: Сначала индекс по age, потом фильтр по city
-- Вариант 2: Сначала индекс по city, потом фильтр по age
-- Вариант 3: Full table scan если индексов нет
-- Query Planner выбирает самый быстрый вариант

Как программист может влиять на Planner?

1. Создание и использование индексов

from sqlalchemy import Column, Integer, String, Index, create_engine
from sqlalchemy.orm import declarative_base

Base = declarative_base()

class User(Base):
    __tablename__ = 'users'
    
    id = Column(Integer, primary_key=True)
    email = Column(String, unique=True)  # Индекс
    age = Column(Integer)
    city = Column(String)
    
    # Составной индекс помогает Planner
    __table_args__ = (
        Index('idx_city_age', 'city', 'age'),
    )

# С индексом Planner выберет быстрый план
# query = session.query(User).filter(User.city == 'Moscow').filter(User.age > 25)

# Без индекса пришлось бы сканировать всю таблицу

2. Использование EXPLAIN для анализа плана

-- PostgreSQL: показать план выполнения
EXPLAIN ANALYZE
SELECT * FROM users WHERE city = 'Moscow' AND age > 25;

-- Вывод:
-- Seq Scan on users (cost=0.00..1500.00 rows=100)
--   Filter: ((city = 'Moscow') AND (age > 25))

-- "Seq Scan" = полное сканирование (ПЛОХО)
-- После создания индекса измениться на:
-- Index Scan using idx_city_age on users
--   Index Cond: (city = 'Moscow' AND age > 25)

3. Переписание запроса для подсказки Planner

# ПЛОХОЙ запрос - Planner может выбрать неоптимальный план
from sqlalchemy import func

query = session.query(User).filter(
    (User.age > 25) | 
    (User.age < 18)
)

# ХОРОШИЙ запрос - явно указываешь структуру
from sqlalchemy import and_, or_

query = session.query(User).filter(
    or_(
        and_(User.city == 'Moscow', User.age > 25),
        and_(User.city == 'SPB', User.age < 18)
    )
)

4. Использование хинтов (hints)

Зависит от СУБД:

-- PostgreSQL: нет прямых hints
-- Но можно влиять через параметры:
SET random_page_cost = 1;  -- Влияет на выбор индекса

-- MySQL: используются hints
SELECT /*+ INDEX(users idx_city_age) */ 
       * FROM users WHERE city = 'Moscow';

-- Oracle: используются hints
SELECT /*+ INDEX(users idx_city_age) */ 
       * FROM users WHERE city = 'Moscow';

5. Анализ и обновление статистики

-- PostgreSQL: Planner использует статистику
ANALYZE users;  -- Пересчитать статистику

-- Если Planner неправильно оценивает количество строк,
-- статистика может быть устаревшей

-- MySQL
ANALYZE TABLE users;

-- После анализа Planner выберет более правильный план

6. Оптимизация структуры запроса

# ПЛОХО: Planner теряется в сложности
result = session.query(User).filter(
    User.id.in_(
        session.query(Order.user_id).filter(Order.amount > 1000)
    )
).all()

# ХОРОШО: Явное JOIN даёт Planner больше информации
from sqlalchemy import join

result = session.query(User).join(
    Order, User.id == Order.user_id
).filter(
    Order.amount > 1000
).distinct().all()

# Planner лучше оптимизирует JOIN с известной структурой

7. Управление параметрами Planner (PostgreSQL)

from sqlalchemy import text, create_engine

engine = create_engine('postgresql://user:pass@localhost/db')

with engine.connect() as conn:
    # Влиять на выбор индексов
    conn.execute(text('SET random_page_cost = 1'))
    # random_page_cost влияет на выбор между индексом и seq scan
    
    # Отключить определенные типы операций если нужно
    conn.execute(text('SET enable_seqscan = off'))
    # Но это ОЧЕНЬ рисковано!

Ограничения влияния

Программист НЕ может напрямую:

  • Заставить использовать определенный индекс (без hints)
  • Изменить порядок операций если он экономичнее
  • Переписать логику Planner
  • Заставить параллелизм если его нет
-- Даже если напишешь так, Planner всё равно выберет лучший план
SELECT * FROM users WHERE age > 25;
-- Planner может использовать индекс или seq scan,
-- независимо от твоего предпочтения

Практический пример: как влиять

# Ситуация: Planner выбирает seq scan вместо индекса

# 1. Проверяем план
from sqlalchemy import text
result = session.execute(
    text('EXPLAIN ANALYZE SELECT * FROM users WHERE email = :email'),
    {'email': 'user@example.com'}
)
print(result.fetchall())

# 2. Если используется Seq Scan, хотя есть индекс:
#    - Пересчитываем статистику
session.execute(text('ANALYZE users'))

# 3. Или создаём более специфичный индекс
text('CREATE INDEX idx_email_status ON users(email, status)')

# 4. Или переписываем запрос для ясности:
query = session.query(User).filter(
    User.email == 'user@example.com',
    User.status == 'active'
)

# 5. После этого Planner выберет правильный план

Вывод

Программист может влиять на Query Planner косвенно через:

  • Создание индексов - даёт Planner больше вариантов
  • Переписание запросов - ясная структура помогает Planner
  • Анализ EXPLAIN - понимание выбора Planner
  • Обновление статистики - помогает Planner принять правильное решение
  • Использование hints (зависит от БД) - прямое указание

Но Planner остаётся автономным - он выбирает то, что считает оптимальным, основываясь на стоимостной модели.

Может ли программист влиять на планировщик в БД? | PrepBro