Как оценить алгоритмическую сложность операций с индексом?

«Как оценить алгоритмическую сложность операций с индексом?» — вопрос из категории SQL и базы данных, который задают на 33% собеседований Data Инженер. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Оценка сводится к пониманию структуры индекса и стоимости операций доступа. В реальной работе я смотрю на план запроса (EXPLAIN ANALYZE) и оцениваю, соответствует ли выбранный доступ ожидаемой сложности.

Основные структуры и их сложность:

Тип индекса Структура данных Поиск по ключу (=) Диапазонный поиск (BETWEEN, >) Вставка / Удаление Типичное применение в БД
B-дерево (B+tree) Сбалансированное дерево O(log n) O(log n + k) (k - кол-во записей в диапазоне) O(log n) PostgreSQL, MySQL (InnoDB), SQL Server. Универсальный выбор.
Хеш-индекс Хеш-таблица O(1) в среднем Не поддерживается O(1) в среднем PostgreSQL, MySQL (MEMORY). Только для точечных запросов.
Bitmap-индекс Битовые массивы O(1) для AND/OR Неэффективно O(n) в худшем Колоночные СУБД (ClickHouse). Для низкокардинальных полей (пол, статус).

Практическая оценка на примере PostgreSQL:

  1. Смотрю план запроса. Ожидаю увидеть Index Scan или Index Only Scan для поиска по индексированному полю.
    EXPLAIN ANALYZE SELECT * FROM orders WHERE user_id = 12345; -- user_id проиндексирован
    -- В плане должно быть: Index Scan using idx_orders_user_id on orders ...
  2. Оцениваю стоимость. Операция Index Scan состоит из:
    • Проход по дереву индекса: O(log n).
    • Обращение к таблице (heap fetch) по TID: +1 операция чтения на каждую найденную строку. Если индекс covering (включает все нужные поля), происходит Index Only Scan и второе чтение не нужно.
  3. Для составного индекса (A, B) сложность поиска по A — O(log n), а поиск только по BO(n) (полный сканирование индекса), так как префикс ключа не задан.

Что влияет на реальную производительность, помимо Big O:

  • Размер страницы/блока БД: B-дерево оптимизировано под пакетное чтение с диска.
  • Фактор кластеризации (CLUSTER): Если данные в таблице физически упорядочены по ключу индекса, снижаются затраты на heap fetch.
  • Селективность: Индекс выгоден, если он отфильтровывает большую часть данных. Для неселективных полей (например, status = 'active', где 90% строк) полное сканирование таблицы может быть быстрее.