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

Что такое низкая селективность в базе данных?

2.0 Middle🔥 101 комментариев
#Базы данных и SQL

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

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

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

Селективность в базе данных (Selectivity)

Селективность (Selectivity) — это степень, в какой индекс или запрос сокращает объем данных, подлежащих обработке. Низкая селективность означает, что индекс или запрос возвращает много строк относительно всего объема данных в таблице.

Основной концепт

Селективность выражается как отношение количества возвращаемых строк к общему количеству строк в таблице:

Селективность = (количество возвращаемых строк) / (общее количество строк) * 100%

Примеры:

  • Высокая селективность: 1-5% (индекс фильтрует хорошо)
  • Низкая селективность: 30-100% (индекс малоэффективен)

Примеры низкой селективности

Пример 1: Индекс на булевом поле

// Таблица с 1 миллионом пользователей
CREATE TABLE users (
    id BIGINT PRIMARY KEY,
    name VARCHAR(100),
    is_active BOOLEAN,  // индекс на этом поле
    email VARCHAR(100)
);

CREATE INDEX idx_is_active ON users(is_active);

// Запрос с низкой селективностью
SELECT * FROM users WHERE is_active = true;  // возвращает ~500k строк (50%)

// Индекс малоэффективен:
// - Всего строк: 1,000,000
// - Возвращаемых строк: 500,000
// - Селективность: 50% (очень низкая)
// - БД может вообще не использовать индекс, а сделать полное сканирование

Пример 2: Индекс на статусе заказа

CREATE TABLE orders (
    id BIGINT PRIMARY KEY,
    customer_id BIGINT,
    status VARCHAR(20),  -- 'completed', 'pending', 'cancelled'
    amount DECIMAL(10,2)
);

CREATE INDEX idx_status ON orders(status);

-- 1 млн заказов
-- Распределение: 60% 'completed', 30% 'pending', 10% 'cancelled'

SELECT * FROM orders WHERE status = 'completed';
-- Возвращает 600,000 строк из 1,000,000
-- Селективность: 60% (низкая)
-- Индекс может быть неэффективен

Пример 3: Индекс на городе в большой таблице

CREATE TABLE customers (
    id BIGINT PRIMARY KEY,
    name VARCHAR(100),
    city VARCHAR(50),  -- мало уникальных значений
    country VARCHAR(50)
);

CREATE INDEX idx_city ON customers(city);

-- 10 млн клиентов
-- В стране всего 50 городов
-- Каждый город: ~200k клиентов

SELECT * FROM customers WHERE city = 'Moscow';
-- Возвращает 200,000 строк из 10,000,000
-- Селективность: 2% (кажется неплохой)
-- Но все равно много записей!

Высокая селективность (для сравнения)

CREATE TABLE users (
    id BIGINT PRIMARY KEY,
    email VARCHAR(100) UNIQUE,  -- индекс (уникальный)
    name VARCHAR(100)
);

-- Всего: 1,000,000 пользователей

SELECT * FROM users WHERE email = 'john@example.com';
-- Возвращает 1 строку из 1,000,000
-- Селективность: 0.0001% (отличная!)
-- Индекс очень эффективен

Как низкая селективность влияет на производительность

При низкой селективности:

  1. Индекс может не использоваться вообще

    • Оптимизатор решит, что полное сканирование быстрее
    • EXPLAIN показывает Sequential Scan, а не Index Scan
  2. Больше I/O операций

    • Нужно прочитать много блоков данных с диска
    • Больше обращений в память
  3. Больше обработки в памяти

    • Фильтрование большого количества строк
    • Сортировка, группировка медленнее
// Пример на Java
@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
    // Низкая селективность - медленно
    @Query("SELECT o FROM Order o WHERE o.status = 'COMPLETED'")
    List<Order> findCompletedOrders();  // может вернуть 60% заказов
    
    // Высокая селективность - быстро
    @Query("SELECT o FROM Order o WHERE o.id = :orderId")
    Optional<Order> findById(@Param("orderId") Long orderId);  // вернет 1 строку
}

Как оценить селективность

PostgreSQL:

-- Смотреть EXPLAIN
EXPLAIN (ANALYZE) SELECT * FROM orders WHERE status = 'completed';

-- Результат:
-- Seq Scan on orders (cost=0.00..15000.00 rows=600000) <- низкая селективность
-- Filter: (status = 'completed')

-- Vs.
EXPLAIN (ANALYZE) SELECT * FROM users WHERE id = 1;
-- Index Scan using users_pkey (cost=0.29..8.30 rows=1) <- высокая селективность

MySQL:

EXPLAIN SELECT * FROM orders WHERE status = 'completed'\G

-- Результат показывает количество examined rows vs returned rows

Решения для низкой селективности

1. Составной индекс (Composite Index)

-- Плохо: индекс на одном поле
CREATE INDEX idx_status ON orders(status);
SELECT * FROM orders WHERE status = 'completed' AND amount > 1000;

-- Хорошо: индекс на двух полях
CREATE INDEX idx_status_amount ON orders(status, amount);
-- Теперь фильтр более селективен

2. Условный индекс (Partial Index)

-- Индекс только для активных заказов (более селективный)
CREATE INDEX idx_completed_orders ON orders(id)
WHERE status = 'completed';

-- Индекс будет меньше и быстрее

3. Денормализация (Denormalization)

// Вместо частого запроса:
SELECT COUNT(*) FROM orders WHERE status = 'completed';

// Хранить счетчик в отдельной таблице
@Entity
public class OrderStats {
    @Id
    private String status;
    
    private Long count;  // обновляется триггером
}

// Запрос теперь очень быстрый (высокая селективность)

4. Фильтрование на уровне приложения (иногда)

// Плохо: низкая селективность
List<Order> orders = orderRepository.findByStatus("completed");

// Хорошо: более специфичный запрос
List<Order> orders = orderRepository.findByStatusAndAmountGreaterThan(
    "completed", BigDecimal.valueOf(1000)
);

5. Партиционирование таблицы

-- Разбить большую таблицу по статусу
CREATE TABLE orders_completed PARTITION OF orders
FOR VALUES IN ('completed');

CREATE TABLE orders_pending PARTITION OF orders
FOR VALUES IN ('pending');

-- Теперь запрос ищет только в нужной партиции

Практический пример анализа

@Service
public class OrderAnalyticsService {
    @Autowired
    private OrderRepository orderRepository;
    
    // Низкая селективность - может быть медленно
    public List<Order> getCompletedOrders() {
        // ОБЪЯСНЕНИЕ: статус есть у 60% заказов
        // Селективность: 60% - очень низкая
        // Возможно, БД вообще не использует индекс
        return orderRepository.findByStatus("COMPLETED");
    }
    
    // Высокая селективность - быстро
    public Order getOrderById(Long id) {
        // Селективность: 0.0001%
        // БД точно использует PRIMARY KEY индекс
        return orderRepository.findById(id).orElse(null);
    }
    
    // Средняя селективность - хорошо
    public List<Order> getRecentCompletedOrders(LocalDateTime since) {
        // Статус = COMPLETED (60%)
        // + Дата > since (может быть 10%)
        // Итоговая селективность: ~6%
        return orderRepository.findByStatusAndCreatedAtAfter(
            "COMPLETED", since
        );
    }
}

Итого

Низкая селективность означает, что индекс или запрос возвращает много строк (30-100%) из общего объема данных. Это снижает эффективность индексов, и БД может вообще их не использовать. Решения: составные индексы, условные индексы, более специфичные запросы, партиционирование. Хорошая селективность — это 1-5% возвращаемых строк.

Что такое низкая селективность в базе данных? | PrepBro