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

Можно ли применить index ко всем столбцам в БД?

1.0 Junior🔥 141 комментариев
#Другое

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

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

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

Индексирование всех столбцов в БД

Короткий ответ: Технически можно, но это ОЧЕНЬ плохая идея. Разберу почему и когда индексировать правильно.

Почему это плохая идея?

1. Значительные затраты памяти

Индекс — это отдельная структура данных (обычно B-tree) которая дублирует данные.

# Пример: таблица с 10 столбцами
class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)  # Index автоматический
    email = Column(String(255), index=True)  # +1 индекс
    first_name = Column(String(100), index=True)  # +1 индекс
    last_name = Column(String(100), index=True)  # +1 индекс
    # ... и так для всех 10 столбцов

# Результат:
# - Таблица занимает 100 GB
# - Индексы занимают ещё 100+ GB
# - Вместо хранения 100 GB у нас ~200 GB

Реальные числа:

  • VARCHAR(255) без индекса: ~256 байт
  • VARCHAR(255) с индексом: +50-100 GB на 100 млн строк
  • 10 индексов = +500-1000 GB памяти!

2. Замедление при записях (INSERT, UPDATE, DELETE)

Каждый раз при изменении данных БД должна обновить ВСЕ индексы.

# INSERT без индексов
INSERT INTO users (email, first_name, last_name, ...)
VALUES ('john@example.com', 'John', 'Doe', ...)
# Быстро: 1 операция в таблицу

# INSERT со 100 индексами
# 1 операция в таблицу + 100 обновлений индексов
# Может быть 10-100x медленнее!

Пример в PostgreSQL:

CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    email VARCHAR(255),
    first_name VARCHAR(100),
    last_name VARCHAR(100),
    phone VARCHAR(20),
    address TEXT,
    city VARCHAR(100),
    country VARCHAR(100),
    zip_code VARCHAR(10),
    birth_date DATE
);

-- БЕЗ индексов
INSERT INTO users (email, first_name, last_name, phone, address, city, country, zip_code, birth_date)
SELECT ... FROM large_data_source;  -- 100 млн строк за 5 минут

-- Со ВСЕМИ индексами
CREATE INDEX ON users(email);
CREATE INDEX ON users(first_name);
CREATE INDEX ON users(last_name);
CREATE INDEX ON users(phone);
CREATE INDEX ON users(address);
CREATE INDEX ON users(city);
CREATE INDEX ON users(country);
CREATE INDEX ON users(zip_code);
CREATE INDEX ON users(birth_date);

INSERT INTO users (...)  -- Та же операция теперь 30-50 минут!

3. Увеличение времени обслуживания БД

-- Когда нужна дефрагментация или VACUUM
VACUUM users;  -- Без индексов: 5 минут
VACUUM users;  -- Со 100 индексами: 50+ минут

4. Усложнение плана запроса (Query Planner)

Optimizer должен выбрать лучший индекс из 100 вариантов.

EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'john@example.com';

-- Без индексов
Seq Scan on users  (cost=0.00..10000.00 rows=1) (actual time=50.23..100.45 rows=1)
Panning time: 0.05ms

-- Со 100 индексами
Bitmap Index Scan on idx_users_email  (actual time=50.34..100.56 rows=1)
Panning time: 50ms  (дольше анализировать план!)

Когда индексировать?

1. Столбцы в WHERE условиях

-- Часто используется в WHERE
SELECT * FROM users WHERE email = ?;  -- INDEX нужен!
SELECT * FROM users WHERE status = ?;  -- INDEX нужен!

CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_status ON users(status);

-- Редко используется
SELECT * FROM users WHERE internal_notes = ?;  -- INDEX НЕ нужен

2. Столбцы в JOIN условиях

SELECT u.*, p.title
FROM users u
JOIN posts p ON u.id = p.user_id;  -- INDEX на posts.user_id нужен!

CREATE INDEX idx_posts_user_id ON posts(user_id);

3. Столбцы в ORDER BY

SELECT * FROM posts ORDER BY created_at DESC;  -- INDEX помогает

CREATE INDEX idx_posts_created_at ON posts(created_at DESC);

4. Столбцы в GROUP BY (иногда)

SELECT status, COUNT(*)
FROM users
GROUP BY status;  -- INDEX может помочь

Когда НЕ индексировать?

1. Столбцы с низкой селективностью

-- Столбец с 2 значениями (Male/Female)
SELECT * FROM users WHERE gender = 'M';
-- INDEX неэффективен (вернёт 50% таблицы)

-- Столбец с булевым значением
SELECT * FROM users WHERE is_active = true;
-- INDEX на is_active часто ВРЕДИТ производительности

2. Большие TEXT/BLOB столбцы

-- Индекс на большой TEXT
CREATE INDEX idx_comments_text ON comments(comment_text);
-- Индекс будет огромным и медленным!

-- Правильное решение: FULL TEXT SEARCH
CREATE INDEX idx_comments_text_fts ON comments USING GIN(to_tsvector('english', comment_text));
SELECT * FROM comments WHERE to_tsvector('english', comment_text) @@ plainto_tsquery('english', 'keyword');

3. Часто меняющиеся столбцы

-- Столбец, который меняется в каждом UPDATE
ALTER TABLE users ADD COLUMN last_login_at TIMESTAMP;

-- UPDATE каждой секунды: 1000 строк/сек = 86 млн раз/день
UPDATE users SET last_login_at = NOW() WHERE id = ?;

-- INDEX усложнит эти обновления, выигрыш в SELECT незначительный

4. Маленькие таблицы

-- На таблице из 100 строк
CREATE INDEX idx_small_table_col ON small_table(column);
-- Full table scan быстрее индекса! Индекс только занимает память

Реальный пример: правильная стратегия

# models.py
from sqlalchemy import Column, Integer, String, DateTime, Boolean, Index
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class User(Base):
    __tablename__ = 'users'
    
    id = Column(Integer, primary_key=True)  # Индекс по умолчанию
    
    # Часто ищем по email
    email = Column(String(255), unique=True, index=True)
    
    # Часто ищем по статусу (но может быть составной индекс)
    status = Column(String(50), index=False)  # Низкая селективность
    
    # Часто ищем по user_id в JOIN
    profile_id = Column(Integer, index=True)
    
    # Редко используется, большой TEXT
    bio = Column(String(1000), index=False)  # No index!
    
    # Часто сортируем
    created_at = Column(DateTime, index=True)
    
    # Часто обновляем, не ищем
    last_login_at = Column(DateTime, index=False)
    
    __table_args__ = (
        # Составной индекс для WHERE + ORDER BY
        Index('idx_status_created', 'status', 'created_at'),
    )

Стратегия индексирования

# 1. Найди N самых медленных запросов (с помощью query logs)
# 2. Профилируй запросы (EXPLAIN ANALYZE)
# 3. Добавь индексы ТОЛЬКО для оптимизации этих запросов
# 4. Проверь: не замедлил ли INSERT/UPDATE/DELETE
# 5. Мониторь размер БД
# 6. Удали неиспользуемые индексы

# Пример: найти неиспользуемые индексы в PostgreSQL
SELECT
    schemaname,
    tablename,
    indexname,
    idx_scan
FROM pg_stat_user_indexes
WHERE idx_scan = 0
ORDER BY pg_relation_size(indexrelid) DESC;
# Эти индексы можно удалить!

Золотые правила

ПравилоЧто делать
Индексируй для поискаINDEX на столбцы в WHERE, JOIN, ORDER BY
Избегай множественных индексов3-5 индексов на таблицу — обычно достаточно
Профилируй перед индексациейEXPLAIN ANALYZE для проверки
Составные индексы дешевлеINDEX (col1, col2) лучше двух отдельных
Мониторь размерИндексы занимают место!
Удаляй неиспользуемыеpg_stat_user_indexes показывает неиспользуемые
Не индексируй всё подрядТестируй на реальных нагрузках

SQL для мониторинга индексов

-- PostgreSQL: размер каждого индекса
SELECT
    indexrelname,
    pg_size_pretty(pg_relation_size(indexrelid)) AS size,
    idx_scan,
    idx_tup_read,
    idx_tup_fetch
FROM pg_stat_user_indexes
ORDER BY pg_relation_size(indexrelid) DESC;

-- MySQL: все индексы
SELECT
    TABLE_NAME,
    INDEX_NAME,
    SEQ_IN_INDEX,
    COLUMN_NAME
FROM INFORMATION_SCHEMA.STATISTICS
WHERE TABLE_SCHEMA = 'your_db'
ORDER BY TABLE_NAME, INDEX_NAME;

Итог

  1. Не индексируй все столбцы — это убьёт INSERT/UPDATE/DELETE
  2. Индексируй только горячие пути — WHERE, JOIN, ORDER BY
  3. Профилируй ПЕРЕД индексацией — EXPLAIN ANALYZE
  4. Мониторь размер — индексы занимают место
  5. Удаляй неиспользуемые — периодически чистись
  6. Используй составные индексы — более эффективны

Практика: Хорошо спроектированная БД имеет 3-10 индексов на таблицу, не 100!

Можно ли применить index ко всем столбцам в БД? | PrepBro