awesome-everything EN
↑ Обратно к восхождению

Базы данных

Leading-column rule: почему порядок столбцов в composite-индексе важен

Суть Composite B-tree индекс на (a, b, c) ускоряет запросы, фильтрующие по a, a+b или a+b+c — но не запросы только по b или c. Это самое нарушаемое правило в продакшн-индексировании.
Высота — путь к senior
НольJuniorMiddleSenior
Ты на middle-высоте — в небе
◷ 14 min

Дашборд фильтрует задачи по workspace_id и status. Команда добавляет индекс (status, workspace_id). В staging работает быстро. В продакшне с 50M строк запрос со временем начинает делать seq scan, потому что фильтр WHERE workspace_id = $1 без status не использует leading-столбец. Один неправильный порядок столбцов — seq scan на 50M строк.

Как устроен B-tree внутри

B-tree — сбалансированное дерево, где каждый узел — disk page (8KB) с отсортированным списком ключей. Для composite-индекса (a, b, c) ключ — это кортеж (a, b, c), отсортированный лексикографически: сначала по a, при равных a — по b, при равных (a, b) — по c.

Для поиска по индексу планировщик должен начать с leading-столбца. Только зная значение a, он может перейти к правой ветке дерева. Без значения a вся структура бесполезна — как искать в телефонной книге, зная только второе имя, но не фамилию.

Фильтр запросаИндекс (a, b, c) работает?
WHERE a = $1Да — только leading-столбец
WHERE a = $1 AND b = $2Да — leading prefix a, b
WHERE a = $1 AND b = $2 AND c = $3Да — полное использование
WHERE a = $1 AND c = $3 (нет b)Частично — сужает по a, затем фильтрует c без индекса
WHERE b = $2Нет — b не leading-столбец
WHERE c = $3Нет — c не leading-столбец

Проектирование composite-индексов

Правило: leading-столбец должен быть столбцом, который присутствует в фильтре большинства горячих запросов. Затем вторичные столбцы — по убыванию частоты фильтрации.

Пример: tenant-scoped дашборд

-- Запрос: список задач в проекте
SELECT id, title FROM tasks
WHERE workspace_id = $1 AND status = 'open'
ORDER BY created_at DESC;

-- Правильный индекс: workspace_id — tenant-граница (всегда присутствует)
CREATE INDEX ON tasks (workspace_id, status, created_at DESC);

-- Неправильный: status первым — только небольшая часть запросов фильтрует ТОЛЬКО по status
CREATE INDEX ON tasks (status, workspace_id, created_at DESC);

Один composite vs два single-column индекса

Один composite (a, b) обычно лучше двух single (a) и (b):

  • Меньше overhead на запись (одна структура вместо двух).
  • Меньше суммарный размер.
  • Composite обслуживает запросы по a (использует только leading-часть) И запросы по a, b.

Два single-column нужны, когда: запросы часто фильтруют только по a ИЛИ только по b (не вместе). Тогда ни один composite не обслуживает оба.

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

Распространённое заблуждение: «ставь столбец с большей кардинальностью первым». Это неверно. Порядок определяется паттерном запросов:

  • Если workspace_id (5 значений) всегда присутствует в фильтре, он идёт первым.
  • Если user_id (1M значений) встречается в фильтре редко, он идёт последним или в отдельный индекс.

Единственный источник правды — EXPLAIN ANALYZE на реальных запросах с реалистичными параметрами.

Проследи
1/3

Команда добавляет индекс (status, workspace_id) для дашборда. Проследи проблему.

1
Step 1 of 3
Шаг 1: дашборд фильтрует по workspace_id = $1. Какой plan?
2
Locked
Шаг 2: как исправить не переписывая запрос?
3
Locked
Шаг 3: как убедиться, что новый индекс используется?
Викторина

Индекс: CREATE INDEX ON events (tenant_id, event_type, created_at). Какой запрос его использует?

Викторина

Команда проводит аудит индексов. Находит (user_id) и (user_id, created_at DESC). Что делать?

Расставь шаги по порядку

Упорядочи шаги проектирования composite-индекса под hot query:

  1. 1 Определи hot queries и их фильтр-паттерны
  2. 2 Найди столбец, который всегда присутствует в фильтре — это leading-столбец
  3. 3 Добавь secondary столбцы по убыванию частоты фильтрации
  4. 4 Убедись, что ORDER BY столбцы идут после filter-столбцов
  5. 5 Добавь INCLUDE для столбцов только в SELECT projection
  6. 6 CREATE INDEX CONCURRENTLY и проверь EXPLAIN ANALYZE
  7. 7 Удали одиночные индексы, которые стали prefix нового composite
Вспомните перед уходом
  1. 01
    Объясни детально, почему leading-column rule существует и какие продакшн-паттерны помогают его обойти.
  2. 02
    Почему CREATE INDEX CONCURRENTLY — продакшн-дефолт, несмотря на медлительность, и каковы его failure mode?
  3. 03
    Один composite дешевле двух одиночных индексов — объясни почему, и когда два одиночных лучше.
Итог

Composite B-tree на (a, b, c) отсортирован лексикографически: сначала по a, затем по b, затем по c. Запросы, которые не фильтруют по leading-столбцу a, не могут использовать индекс — планировщик не имеет точки входа в дерево. Это самое нарушаемое правило в продакшн-индексировании: команды добавляют composite не задумываясь о порядке и получают seq scan.

Правило проектирования: leading-столбец — это столбец, присутствующий в фильтре большинства горячих запросов (обычно tenant-scope вроде workspace_id). Secondary столбцы — по убыванию частоты фильтрации. ORDER BY столбцы — после filter-столбцов в ключе. Столбцы только для projection — в INCLUDE.

Один хорошо спроектированный composite часто заменяет 3-5 одиночных индексов: меньше write-overhead, меньше суммарный размер. EXPLAIN ANALYZE — единственный способ убедиться, что планировщик действительно использует индекс.

Связанные уроки
встречается в174
Продолжить восхождение ↑Partial, expression и covering-индексы
хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.