Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Ответ на вопрос о критериях создания индекса
На что смотришь при создании индекса?
При создании индекса нужно анализировать несколько ключевых факторов. Это критическая оптимизация БД, требующая понимания как бизнес-логики, так и технических характеристик.
1. Частота использования столбца в WHERE
Правило: индексируй столбцы, которые часто используются в фильтрации.
-- Часто выполняемые запросы
SELECT * FROM users WHERE email = ?; -- Индекс на email
SELECT * FROM orders WHERE status = ?; -- Индекс на status
SELECT * FROM logs WHERE timestamp > ?; -- Индекс на timestamp
-- Плохо: индекс на редко используемый столбец
SELECT * FROM users WHERE nickname = ?; -- Если nickname редко ищем
2. Кардинальность столбца (Cardinality)
Определение: количество уникальных значений в столбце.
-- ВЫСОКАЯ кардинальность (много уникальных) - ХОРОШО для индекса
SELECT COUNT(DISTINCT id) as cardinality FROM users; -- Например: 1 000 000
SELECT COUNT(DISTINCT email) as cardinality FROM users; -- Например: 1 000 000
-- НИЗКАЯ кардинальность (мало уникальных) - ПЛОХО для индекса
SELECT COUNT(DISTINCT gender) as cardinality FROM users; -- Например: 2 (M/F)
SELECT COUNT(DISTINCT is_active) as cardinality FROM users; -- Например: 2 (true/false)
-- Проверка эффективности индекса
SELECT
column_name,
COUNT(DISTINCT column_name) as cardinality,
COUNT(*) as total_rows,
ROUND(100.0 * COUNT(DISTINCT column_name) / COUNT(*), 2) as selectivity_percent
FROM users
GROUP BY column_name
ORDER BY cardinality DESC;
Пороги:
- > 95% уникальных → отличная кандидатура
- 50-95% уникальных → хорошая кандидатура
- < 10% уникальных → плохая кандидатура (может быть индекс, но не как основной)
3. Размер таблицы (table size)
Правило: индексируй в больших таблицах (> 10 000 строк).
-- Проверка размера таблицы
SELECT
table_name,
COUNT(*) as row_count,
ROUND(pg_total_relation_size(table_name::regclass) / 1024 / 1024, 2) as size_mb
FROM information_schema.tables
WHERE table_schema = 'public'
GROUP BY table_name
ORDER BY COUNT(*) DESC;
Рекомендации:
- < 1 000 строк → индекс бесполезен
- 1 000 - 10 000 → может быть полезен
-
10 000 → индекс почти всегда стоит
-
1 000 000 → индекс КРИТИЧЕН
4. Количество JOIN'ов
Правило: индексируй foreign key столбцы.
-- ВСЕГДА индексируй FK
CREATE INDEX idx_orders_user_id ON orders(user_id);
CREATE INDEX idx_order_items_order_id ON order_items(order_id);
CREATE INDEX idx_comments_post_id ON comments(post_id);
-- Плохой запрос без индекса (медленный JOIN)
SELECT u.*, o.*
FROM users u
JOIN orders o ON u.id = o.user_id -- Нужен индекс на orders.user_id
WHERE u.id = 123;
-- Хороший запрос с индексом (быстрый)
-- (индекс на foreign key)
5. ORDER BY и GROUP BY
Правило: индексируй столбцы в ORDER BY и GROUP BY.
-- Частый сортировочный запрос
SELECT * FROM posts
ORDER BY created_at DESC -- Нужен индекс на created_at
LIMIT 10;
-- Индекс для этого
CREATE INDEX idx_posts_created_at DESC ON posts(created_at DESC);
-- Группировка
SELECT category_id, COUNT(*)
FROM products
GROUP BY category_id; -- Нужен индекс на category_id
CREATE INDEX idx_products_category_id ON products(category_id);
6. Selectively (составные индексы)
Правило: если два столбца часто используются вместе в WHERE.
-- Частый запрос
SELECT * FROM orders
WHERE user_id = ? AND status = ?; -- Оба в WHERE
-- Обычные индексы (неоптимально)
CREATE INDEX idx_orders_user_id ON orders(user_id);
CREATE INDEX idx_orders_status ON orders(status);
-- ЛУЧШЕ: составной индекс
CREATE INDEX idx_orders_user_status ON orders(user_id, status);
-- Порядок имеет значение!
-- Лучше: (user_id, status) если user_id более selective
-- Худше: (status, user_id) если status имеет низкую кардинальность
7. Стоимость обслуживания индекса
Каждый индекс имеет ЦЕНУ:
-- Индекс ускоряет SELECT, но замедляет INSERT/UPDATE/DELETE
-- Медленный INSERT (много индексов)
INSERT INTO users (id, email, name, phone, address, created_at)
VALUES (1, 'user@example.com', 'John', '555-1234', '123 Main St', now());
-- БД обновляет индексы на каждом столбце
-- Плохо: индекс на редко используемый столбец
CREATE INDEX idx_users_phone ON users(phone); -- Если phone редко ищем
-- Хорошо: индекс на часто ищемый столбец
CREATE INDEX idx_users_email ON users(email); -- email часто ищем
8. EXPLAIN и Query Plan
Всегда проверяй EXPLAIN перед созданием индекса:
-- PostgreSQL
EXPLAIN (ANALYZE, BUFFERS)
SELECT * FROM users WHERE email = 'user@example.com';
-- MySQL
EXPLAIN FORMAT=JSON
SELECT * FROM users WHERE email = 'user@example.com';
-- Искать:
-- - Seq Scan (плохо, нужен индекс)
-- - Index Scan (хорошо)
-- - Full Index Scan (если можно улучшить)
-- - Execution time
9. Nullability
Правило: индексы в БД обычно исключают NULL.
-- Проверка NULL значений
SELECT COUNT(*) as null_count
FROM users
WHERE email IS NULL; -- Если много NULL, индекс менее эффективен
-- Если NULL часто - может быть better индекс
CREATE INDEX idx_users_email_not_null ON users(email)
WHERE email IS NOT NULL; -- Partial index
10. UPDATE frequency (частота обновлений)
Правило: на редко обновляемые столбцы индексируй смелее.
// Java пример: анализ обновлений
public class IndexAnalyzer {
public boolean shouldIndex(String columnName, long tableSize,
long updateFrequencyPerSecond,
double selectivityPercent) {
// Условия для создания индекса
boolean isLargeTable = tableSize > 10_000;
boolean isSelective = selectivityPercent > 50;
boolean isNotFrequentlyUpdated = updateFrequencyPerSecond < 100;
return isLargeTable && isSelective && isNotFrequentlyUpdated;
}
}
Чеклист при создании индекса
✅ ДА, создавай индекс если:
- Столбец часто используется в WHERE
- Высокая кардинальность (> 50% уникальных)
- Таблица большая (> 10 000 строк)
- Foreign key
- Используется в ORDER BY или GROUP BY
- Редко обновляется
- Таблица растёт (будет иметь миллионы строк)
❌ НЕ создавай индекс если:
- Низкая кардинальность (< 10% уникальных)
- Столбец редко используется в WHERE
- Таблица маленькая (< 1 000 строк)
- Столбец часто обновляется
- SELECT медленнее, чем INSERT/UPDATE с индексом
- Уже есть лучший составной индекс
Практический пример
-- Таблица users
CREATE TABLE users (
id BIGINT PRIMARY KEY,
email VARCHAR(255) UNIQUE,
username VARCHAR(100),
status VARCHAR(20),
created_at TIMESTAMP,
updated_at TIMESTAMP
);
-- Индексы:
CREATE INDEX idx_users_email ON users(email); -- ✅ Высокая кардинальность, часто ищем
CREATE INDEX idx_users_username ON users(username); -- ✅ Высокая кардинальность
CREATE INDEX idx_users_created_at ON users(created_at DESC); -- ✅ ORDER BY
CREATE INDEX idx_users_status_created ON users(status, created_at); -- ✅ Составной
-- НЕ индексируем status отдельно - низкая кардинальность
Резюме
При создании индекса смотри на:
- Частота использования в WHERE
- Кардинальность (> 50% уникальных)
- Размер таблицы (> 10 000 строк)
- Foreign keys - индексируй всегда
- ORDER BY, GROUP BY - индексируй столбцы
- Составные индексы - если 2+ столбца вместе
- EXPLAIN - проверяй перед созданием
- Стоимость INSERT/UPDATE/DELETE
- Selectivity - эффективность фильтрации
- Частота обновлений - редко обновляемые лучше
Golden rule: Индекс хорош, если ускоряет SELECT больше, чем замедляет INSERT/UPDATE/DELETE.