← Назад к вопросам
Приведи пример плохого использования индекса
2.0 Middle🔥 161 комментариев
#SQL и базы данных
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Примеры плохого использования индексов
Это один из самых часто встречаемых багов, которые я встречал в production. Индексы помогают, но только если их правильно использовать.
1. Индекс игнорируется из-за типов данных
-- Таблица
CREATE TABLE users (
id INTEGER PRIMARY KEY,
user_id VARCHAR(36), -- UUID как строка
name VARCHAR(255)
);
CREATE INDEX idx_user_id ON users(user_id);
-- ПЛОХО: индекс НЕ используется
SELECT * FROM users WHERE user_id = 123; -- Ищем число в VARCHAR
-- PostgreSQL конвертирует: CAST(user_id AS INTEGER) = 123
-- Это функция на столбце → индекс игнорируется!
-- ХОРОШО:
SELECT * FROM users WHERE user_id = '123'; -- Строка
Почему: Когда на столбец применена функция (implicit или explicit), БД не может использовать индекс. Нужно искать по изначальному типу.
2. Индекс на столбце с функцией
-- ПЛОХО: индекс обычный
CREATE TABLE orders (
id INTEGER,
created_at TIMESTAMP
);
CREATE INDEX idx_created_at ON orders(created_at);
-- Этот запрос НЕ использует индекс
SELECT COUNT(*) FROM orders
WHERE DATE(created_at) = '2024-03-20';
-- DATE() — функция на столбце → индекс не помогает
-- ХОРОШО: либо функциональный индекс
CREATE INDEX idx_created_date ON orders(DATE(created_at));
-- Либо range query
SELECT COUNT(*) FROM orders
WHERE created_at >= '2024-03-20'::TIMESTAMP
AND created_at < '2024-03-21'::TIMESTAMP;
-- Это использует индекс на created_at
3. Индекс на низко-селективных столбцах
-- ПЛОХО: индекс на столбце с мало уникальных значений
CREATE TABLE events (
id BIGINT PRIMARY KEY,
event_type VARCHAR(20), -- только 5 типов: 'click', 'view', 'purchase' и т.д.
timestamp TIMESTAMP
);
CREATE INDEX idx_event_type ON events(event_type);
-- Для 1 млрд строк, где 200M 'click' событий
SELECT * FROM events WHERE event_type = 'click';
-- Индекс найдёт 200M строк, что 20% таблицы
-- БД подумает: "быстрее полный scan" и не использует индекс
-- Решение: составной индекс
CREATE INDEX idx_type_time ON events(event_type, timestamp DESC);
SELECT * FROM events
WHERE event_type = 'click'
AND timestamp > NOW() - INTERVAL '1 day';
4. Индекс, но в неправильном порядке столбцов
-- ПЛОХО: индекс создан в неправильном порядке
CREATE TABLE sales (
id BIGINT,
user_id INTEGER,
product_id INTEGER,
amount DECIMAL(10,2)
);
CREATE INDEX idx_user_product ON sales(product_id, user_id);
-- Этот запрос НЕ использует индекс эффективно
SELECT * FROM sales WHERE user_id = 42;
-- Индекс начинается с product_id, а мы ищем по user_id
-- Нужно сканировать весь индекс
-- ХОРОШО: переделать индекс
DROP INDEX idx_user_product;
CREATE INDEX idx_user_product ON sales(user_id, product_id);
-- Теперь WHERE user_id = 42 использует индекс полностью
Правило: В составном индексе порядок столбцов важен — более селективные и часто используемые в WHERE идут первыми.
5. Индекс, который не помогает из-за LIMIT и OFFSET
-- ПЛОХО: индекс есть, но данные не отсортированы
CREATE TABLE users (
id SERIAL,
status VARCHAR(20),
created_at TIMESTAMP,
name VARCHAR(255)
);
CREATE INDEX idx_status ON users(status);
-- Запрос требует сортировку, индекс не помогает
SELECT * FROM users
WHERE status = 'active'
ORDER BY created_at DESC
LIMIT 10 OFFSET 1000;
-- Индекс найдёт все 'active', но затем нужно сортировать по created_at
-- Дорого на больших выборках
-- ХОРОШО: составной индекс с сортировкой
CREATE INDEX idx_status_created ON users(status, created_at DESC);
-- Теперь индекс уже отсортирован, не нужна доп. сортировка
6. Индекс, но слишком много индексов (bloat)
-- ПЛОХО: слишком много индексов
CREATE TABLE products (
id INTEGER PRIMARY KEY,
name VARCHAR(255),
category VARCHAR(100),
price DECIMAL(10,2)
);
CREATE INDEX idx_name ON products(name);
CREATE INDEX idx_category ON products(category);
CREATE INDEX idx_price ON products(price);
CREATE INDEX idx_name_category ON products(name, category);
CREATE INDEX idx_category_price ON products(category, price);
-- 5 индексов!
-- Проблемы:
-- - INSERT/UPDATE медленнее (нужно обновлять все индексы)
-- - Память больше
-- - Оптимизатор запутается
-- ХОРОШО: 1-2 ключевых индекса
CREATE INDEX idx_category_price ON products(category, price);
-- Покрывает queries по категории и цене
7. Индекс на NULL значениях
-- ПЛОХО: B-tree индекс не находит NULL
CREATE TABLE logs (
id BIGINT,
error_code INTEGER, -- NULL если нет ошибки
message TEXT
);
CREATE INDEX idx_error ON logs(error_code);
-- Этот запрос НЕ использует индекс
SELECT * FROM logs WHERE error_code IS NULL;
-- B-tree индексы не хранят NULL значения стандартно
-- ХОРОШО: использовать partial индекс
CREATE INDEX idx_error_null ON logs(error_code) WHERE error_code IS NULL;
-- Или в PostgreSQL 15+: CREATE INDEX ... WHERE error_code IS NOT DISTINCT FROM NULL
Практический пример: диагностика
-- PostgreSQL: как найти неиспользуемые индексы
SELECT
schemaname, tablename, indexname, idx_scan
FROM pg_stat_user_indexes
WHERE idx_scan = 0
ORDER BY pg_relation_size(indexrelid) DESC;
-- MySQL: explain вывод
EXPLAIN
SELECT * FROM users WHERE status = 'active';
-- Если key_len маленький или NULL — индекс не используется
Вывод
Индексы — это как лекарство: без них плохо, но передозировка тоже вредит. Ключевой навык — понимать plan explain и знать, почему конкретный индекс (не) используется в конкретном запросе.