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

Что такое Full Scan?

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

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

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

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

Что такое Full Scan

Full Scan (полное сканирование таблицы, TABLE SCAN) — это операция в базе данных, при которой СУБД последовательно читает все строки таблицы с начала до конца, без использования индексов. Это самый неэффективный способ поиска данных, особенно для больших таблиц.

Когда происходит Full Scan

// SQL пример 1: Поиск без индекса
SELECT * FROM users WHERE age > 30;
// БД читает ВСЕ строки и проверяет условие age > 30 для каждой

// SQL пример 2: Функция на индексированном поле
SELECT * FROM users WHERE UPPER(name) = 'JOHN';
// Индекс на name не может быть использован из-за функции UPPER
// БД читает все строки

// SQL пример 3: Отрицание
SELECT * FROM users WHERE status != 'inactive';
// Индекс на status менее полезен

Full Scan vs Index Scan — сравнение

┌─────────────────────────────────────┐
│ ТАБЛИЦА USERS (1 млн строк)        │
│                                    │
│ Index на ID: есть ✓                │
│ Index на name: есть ✓              │
│ Index на age: НЕТ                 │
└─────────────────────────────────────┘

Запрос 1: SELECT * FROM users WHERE id = 5
  -> ЭФФЕКТИВНО: Index Scan (прямой доступ по индексу за O(log n))
  
Запрос 2: SELECT * FROM users WHERE name = 'John'
  -> ЭФФЕКТИВНО: Index Scan на name (за O(log n))
  
Запрос 3: SELECT * FROM users WHERE age > 30
  -> НЕЭФФЕКТИВНО: Full Scan (читает 1 млн строк за O(n))

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

// Пример с цифрами

// Full Scan — читает 1,000,000 строк
// Время: ~10-15 секунд (зависит от размера строки, скорости диска)
SELECT COUNT(*) FROM orders WHERE status = 'pending';

// Index Scan — проходит по индексу (может быть 20,000 строк)
// Время: ~50-100 ms
SELECT COUNT(*) FROM orders WHERE order_id = 12345;
// (если order_id — primary key)

Почему СУБД выбирает Full Scan

Оптимизатор запросов (Query Planner) решает использовать Full Scan когда:

1. Нет подходящего индекса

// Таблица users: индексы только на id и email
SELECT * FROM users WHERE age > 25; // age НЕ индексирована
// Full Scan

// Решение: создать индекс
CREATE INDEX idx_users_age ON users(age);
SELECT * FROM users WHERE age > 25; // Теперь будет Index Scan

2. Индекс не применим из-за функции

// Индекс на name НЕ может быть использован
SELECT * FROM users WHERE UPPER(name) = 'JOHN';
// Full Scan

// Решение 1: Хранить данные в нужном формате
SELECT * FROM users WHERE name = 'JOHN'; // Точное совпадение, индекс работает

// Решение 2: Функциональный индекс (если БД поддерживает)
CREATE INDEX idx_users_name_upper ON users(UPPER(name));

3. Слишком большой процент строк совпадает

// В таблице users 1 млн строк
// 999,000 из них имеют status = 'active' (99.9%)

SELECT * FROM users WHERE status = 'active';

// Оптимизатор решает:
// - Index Scan: прочитать 999,000 записей через индекс
// - Full Scan: прочитать все 1,000,000 строк
// -> ВЫБИРАЕТ Full Scan (примерно одинаково, но проще)

4. Запрос не может быть оптимизирован

SELECT * FROM users;
// Нужны все строки -> Full Scan (это нормально)

SELECT * FROM users WHERE id != 5;
// Исключение одной строки из 1млн -> Full Scan (выгоднее)

Как обнаружить Full Scan

PostgreSQL — EXPLAIN

EXPLAIN SELECT * FROM orders WHERE user_id = 123 AND created_at > '2024-01-01';

// Результат:
// Seq Scan on orders (Sequential Scan = Full Scan)
// Filter: (user_id = 123) AND (created_at > '2024-01-01')
// Rows: 50000 out of 1000000 — вот это медленно!

EXPLAIN SELECT * FROM orders WHERE order_id = 12345;
// Index Scan using orders_pkey
// Rows: 1 — это быстро

MySQL — EXPLAIN

EXPLAIN SELECT * FROM users WHERE age > 30;

// type: ALL — это Full Scan
// rows: 1000000 — все строки
// Extra: Using where — фильтр применяется к каждой строке

EXPLAIN SELECT * FROM users WHERE id = 5;
// type: const — прямой доступ
// rows: 1 — одна строка

Практические сценарии и решения

Сценарий 1: Поиск по дате

// Плохо — Full Scan
SELECT * FROM orders WHERE DATE(created_at) = '2024-03-23';
// created_at — индексирована, но функция DATE разрушает индекс

// Хорошо — Index Range Scan
SELECT * FROM orders 
WHERE created_at >= '2024-03-23' AND created_at < '2024-03-24';
// Индекс используется, только нужные строки

Сценарий 2: Поиск по подстроке

// Плохо — Full Scan
SELECT * FROM products WHERE name LIKE '%phone%';
// Индекс на name не помогает для поиска внутри строки

// Решения:
// 1. Full-text search индекс
SELECT * FROM products WHERE MATCH(name) AGAINST('phone' IN BOOLEAN MODE);

// 2. Специальный индекс (например, GIN индекс в PostgreSQL)
CREATE INDEX idx_products_name_gin ON products USING GIN(to_tsvector('english', name));

Сценарий 3: Фильтрация по нескольким полям

// Плохо — может быть Full Scan
SELECT * FROM orders WHERE status = 'completed' AND total > 1000;
// Если индекс только на status

// Хорошо — составной индекс
CREATE INDEX idx_orders_status_total ON orders(status, total);
SELECT * FROM orders WHERE status = 'completed' AND total > 1000;
// Индекс покрывает оба условия

Java разработчик и Full Scan

// В Java коде (например, с Spring Data JPA)

@Entity
@Table(name = "users", indexes = {
    @Index(name = "idx_email", columnList = "email"),
    @Index(name = "idx_age", columnList = "age")
})
public class User {
    @Id
    private Long id;
    
    @Column(indexed = true)
    private String email;
    
    private int age; // Индексирован выше через @Index
}

// Repository
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    // ХОРОШО — использует индекс
    List<User> findByEmail(String email);
    
    // ПЛОХО — может быть Full Scan
    List<User> findByAge(int age);
    // Почему? Потому что оптимизатор может решить, что Full Scan выгоднее
    
    // ХОРОШО — явное указание
    @Query("SELECT u FROM User u WHERE u.age > :age")
    @QueryHints(@QueryHint(name = "org.hibernate.cacheable", value = "true"))
    List<User> findByAgeGreaterThan(@Param("age") int age);
}

Мониторинг в производстве

// Логирование медленных запросов
// MySQL: slow_query_log, slow_query_log_file

// В логе видим Full Scan:
Query_time: 8.234567  Lock_time: 0.000123  Rows_sent: 50000  Rows_examined: 1000000
// Rows_examined >> Rows_sent = Full Scan!

// PostgreSQL: log_min_duration_statement
query_time: 8234ms
planning_time: 0.234ms
execution_time: 8000ms

Заключение

Full Scan — это признак неэффективного запроса или отсутствия нужных индексов. Как Java разработчик, вы должны:

  1. Выбирать индексы — индексировать поля, по которым фильтруете
  2. Анализировать запросы — использовать EXPLAIN для проверки
  3. Писать правильный SQL — избегать функций на индексированных полях
  4. Тестировать производительность — на реальных данных и нагрузке
  5. Мониторить — отслеживать медленные запросы в production

Оптимизация запросов — это постоянная работа для высоконагруженных систем.

Что такое Full Scan? | PrepBro