Learning Platform
Глоссарий Troubleshooting
Урок 07.06 · 20 мин
Средний
read_in_orderoptimize_read_in_orderORDER BYSortingTransformEXPLAIN PIPELINEPrefix Matching

read_in_order: пропускаем сортировку

Сортировка — одна из самых дорогих операций в pipeline запроса. Она требует чтения всех данных в память, построения структуры сортировки и записи результата. Но если данные на диске уже отсортированы в нужном порядке, сортировку можно пропустить полностью.

ClickHouse делает это автоматически через оптимизацию read_in_order: если ORDER BY запроса совпадает с ORDER BY таблицы (или является его префиксом), данные читаются уже в правильном порядке.


Как работает read_in_order

MergeTree хранит данные физически отсортированными по ORDER BY ключу таблицы. Каждый part содержит строки в порядке, определённом при CREATE TABLE.

Когда запрос требует ORDER BY, ClickHouse проверяет: совпадает ли запрошенная сортировка с физическим порядком данных? Если да — SortingTransform в pipeline не нужен.

-- Таблица отсортирована по (country, event_date, user_id)
CREATE TABLE events (
    country String,
    event_date Date,
    user_id UInt32,
    value UInt32
) ENGINE = MergeTree()
ORDER BY (country, event_date, user_id);

Prefix matching: совпадение по префиксу

Настройка optimize_read_in_order включена по умолчанию. Она активируется, когда ORDER BY запроса является префиксом ORDER BY таблицы:

ORDER BY таблицыORDER BY запросаread_in_order?Почему
(country, event_date, user_id)country, event_date, user_idДаПолное совпадение
(country, event_date, user_id)country, event_dateДаПрефикс: первые 2 столбца
(country, event_date, user_id)countryДаПрефикс: первый столбец
(country, event_date, user_id)event_date, countryНетПорядок не совпадает
(country, event_date, user_id)user_idНетНе префикс (3-й столбец)
(country, event_date, user_id)country, user_idНетПропущен event_date

Правило: ORDER BY запроса должен быть непрерывным префиксом ORDER BY таблицы. Пропуск столбца в середине нарушает порядок.


Constant column skipping

Интересный случай: столбец в ORDER BY таблицы зафиксирован через WHERE. В этой ситуации ClickHouse пропускает его и применяет read_in_order к следующему столбцу:

-- ORDER BY таблицы: (country, event_date, user_id)
-- country зафиксирован через WHERE = фактически константа

SELECT user_id, value
FROM events
WHERE country = 'RU'
ORDER BY country, event_date;
-- read_in_order: YES
-- country = 'RU' => константа, event_date -- следующий в ORDER BY

Ещё более интересный пример:

-- WHERE фиксирует country, ORDER BY начинается с event_date
SELECT user_id, value
FROM events
WHERE country = 'RU'
ORDER BY event_date;
-- read_in_order: YES
-- country -- константа (WHERE country = 'RU'), пропускается
-- event_date -- следующий в ORDER BY таблицы после country
-- Данные внутри partition country='RU' уже отсортированы по event_date

Без constant column skipping этот запрос потребовал бы полной сортировки. С ним — данные читаются в правильном порядке, потому что внутри фиксированного country строки уже упорядочены по event_date.


Подтверждение через EXPLAIN PIPELINE

Как убедиться, что read_in_order активен? Используйте EXPLAIN PIPELINE:

read_in_order активен (сортировка пропущена):

EXPLAIN PIPELINE
SELECT country, event_date, user_id
FROM events
ORDER BY country, event_date;
(Expression)
ExpressionTransform
  (ReadFromMergeTree)
  MergeTreeInOrder 0 → 1

Ключевой индикатор: MergeTreeInOrder вместо MergeTreeThread. Нет SortingTransform — данные читаются в правильном порядке, сортировка не требуется.

read_in_order НЕ активен (требуется сортировка):

EXPLAIN PIPELINE
SELECT country, event_date, user_id
FROM events
ORDER BY user_id, country;
(Expression)
ExpressionTransform
  (Sorting)
  MergeSortingTransform
    (Expression)
    ExpressionTransform
      (ReadFromMergeTree)
      MergeTreeThread 0 → 1

Ключевой индикатор: MergeSortingTransform — ClickHouse добавляет этап сортировки в pipeline, потому что ORDER BY запроса (user_id, country) не совпадает с ORDER BY таблицы (country, event_date, user_id).


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

Для таблицы в 1 миллиард строк:

МетрикаС read_in_orderБез read_in_order
Потребление RAMМинимальное (потоковое чтение)До нескольких ГБ (буфер сортировки)
Время выполненияПропорционально объёму данных+30-50% на сортировку
CPUЧтение + фильтрацияЧтение + фильтрация + сортировка

Выигрыш особенно заметен при:

  • LIMIT N запросах (ClickHouse может остановиться после N строк без чтения всей таблицы)
  • Больших таблицах, где сортировка требует spill на диск
  • Частых запросах с одинаковым ORDER BY

Практические рекомендации

TIP

Проектируйте ORDER BY таблицы с учётом типичных запросов. Если 80% запросов используют ORDER BY event_date, event_date должен быть в начале ORDER BY таблицы.

Выбор ORDER BY таблицы — это баланс между:

  • Selectivity для фильтрации (primary key granule skipping)
  • read_in_order для сортировки запросов
  • Compression ratio (одинаковые значения подряд сжимаются лучше)

Если ORDER BY для фильтрации и для запросов конфликтуют, рассмотрите Projections: дополнительная копия данных с другим ORDER BY, автоматически выбираемая оптимизатором.


Ключевые выводы

  1. read_in_order пропускает SortingTransform в pipeline, когда ORDER BY запроса совпадает с ORDER BY таблицы или является его префиксом.
  2. Настройка optimize_read_in_order включена по умолчанию — дополнительных действий не требуется.
  3. Prefix matching: ORDER BY запроса должен быть непрерывным префиксом ORDER BY таблицы. Пропуск столбца в середине нарушает порядок.
  4. Constant column skipping: WHERE column = value фиксирует столбец как константу. ClickHouse пропускает его и применяет read_in_order к следующему столбцу ORDER BY.
  5. Проверка: EXPLAIN PIPELINE покажет MergeTreeInOrder (read_in_order активен) или MergeSortingTransform (сортировка требуется).
Merge Join: зип двух отсортированных потоков Parquet Row Groups: физическая структура и порядок

Проверьте понимание

Результат: 0 из 0
Прикладной
Вопрос 1 из 4. Таблица создана с ORDER BY (a, b, c). Запрос: SELECT * FROM t ORDER BY a, b. Активируется ли read_in_order?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 8