Skip to main content
В этом руководстве рассматриваются два подхода к оптимизации набора данных NYC Taxi. Сначала объём хранимых и обрабатываемых данных сокращается за счёт выбора более точных типов столбцов. Затем добавляется ключ сортировки, позволяющий ClickHouse пропускать данные при выполнении выборочных запросов. Результат каждого изменения сравнивается с одним и тем же базовым уровнем. Общий рабочий процесс, которому следует этот пример, описан в обзоре оптимизации запросов.

Перед началом работы

В примерах используется таблица nyc_taxi.trips_small_inferred. Создайте и загрузите её, если ещё этого не сделали:
Размер исходного файла Parquet составляет примерно 5,8 ГБ. Его загрузка может занять несколько минут в зависимости от скорости сети и доступных ресурсов.
Исходный файл Parquet содержит примерно 329 миллионов строк. Замеры времени в этом руководстве выполнены на одном развертывании и будут отличаться в зависимости от доступных вычислительных ресурсов. Сравнивайте относительные изменения между стадиями, не ожидая идентичных значений времени выполнения. Применяя этот метод к собственной рабочей нагрузке, воспользуйтесь руководством Диагностика медленных запросов, чтобы выявить повторяющийся шаблон запроса и выбрать репрезентативный запуск, прежде чем изменять запрос или схему.

Обзор процесса

В примере используются следующие три стадии:
  1. Выполните три независимых запроса рабочей нагрузки к выведенной схеме, чтобы определить базовый уровень.
  2. Создайте таблицу с более точными типами столбцов, загрузите те же данные и повторно выполните запросы.
  3. Создайте ещё одну таблицу с той же оптимизированной схемой и ключом сортировки, затем снова выполните запросы.
Изменение схемы и ключа сортировки на отдельных стадиях позволяет проще оценить влияние каждого из них. В разделе Подходы к оптимизации объясняется, когда следует вносить эти изменения и как проверять их результат. Дополнительные рекомендации по сбору сопоставимых измерений см. в разделе Изоляция узких мест запросов.

Задайте базовую рабочую нагрузку

В том же сеансе клиента, в котором запускается рабочая нагрузка, отключите файловый кэш для удалённых данных, кэш запросов и кэш условий запросов:
Эти настройки позволяют обеспечить сопоставимость повторных запусков при тестировании. После завершения измерений восстановите прежние значения.
Следующие три независимых запроса составляют базовую рабочую нагрузку. Выполните все три запроса для каждой таблицы, созданной на следующих этапах. Выполните каждый запрос несколько раз в сопоставимых условиях и зафиксируйте репрезентативную длительность, например медианную, а также количество прочитанных строк и пиковое потребление памяти. Полное описание процесса измерений, включая получение этих значений из system.query_log, см. в разделе Создание воспроизводимой базовой линии.

Фильтрация по вычисленной скорости поездки

Этот запрос вычисляет продолжительность и скорость поездки, а затем определяет распределение расстояний для поездок со скоростью более 30 миль в час:

Агрегация поездок за период

Этот запрос вычисляет количество поездок, расстояние и среднюю сумму оплаты за первый квартал 2009 года:

Фильтрация по числу пассажиров

Этот запрос вычисляет среднюю продолжительность поездок с одним или двумя пассажирами:
Исходные измерения: Все три запроса считывают примерно 329 млн строк — почти столько же, сколько строк в таблице. Это позволяет оптимизировать два аспекта рабочей нагрузки: снизить затраты на обработку выбранных столбцов, а затем, когда это позволяют фильтры, сократить число выбираемых строк.

Оптимизируйте схему

Вывод схемы — удобный способ начать изучение набора данных, но выведенные типы могут быть шире или менее строгими, чем требуется для рабочей нагрузки. Прежде чем изменять схему, изучите данные и не делайте вывод, что выведенный тип не нужен.

Избегайте ненужных столбцов с типом Nullable

Столбец Nullable помимо значений хранит маску NULL. Используйте Nullable, когда важно различать NULL и значение типа по умолчанию, но избегайте его для столбцов, которые гарантированно содержат значение. Подсчитайте значения NULL в столбцах, используемых в примере схемы:
Только в столбцах ratecode_id, mta_tax и payment_type этого набора данных есть значения NULL. В оптимизированной схеме Nullable сохраняется для этих столбцов и удаляется из остальных.

Используйте LowCardinality для повторяющихся значений

LowCardinality использует кодирование с использованием словаря и позволяет сократить затраты на хранение и обработку данных в столбцах с большим количеством повторяющихся значений. Перед применением проверьте количество уникальных значений:
Эти четыре столбца содержат значительно меньше уникальных значений, чем строк. Они подходят для LowCardinality, хотя влияние на рабочую нагрузку всё равно следует измерить. Около 10 000 уникальных значений — полезная отправная точка для выявления кандидатов, а не фиксированный предел.

Выбирайте более точные типы данных

Используйте наиболее узкий тип, который обеспечивает требуемые диапазон и точность. Например, прежде чем заменять выведенный Int64 или Float64, проверьте минимальные и максимальные значения числовых столбцов:
Оба целочисленных столбца помещаются в UInt8, хотя passenger_count достигает максимального значения 255. В примере также используется Float32 для trip_distance и Decimal32 для денежных значений. Все значения в этом наборе данных укладываются в целевые диапазоны, а для сравнения агрегированных результатов в рабочей нагрузке примера достаточно сниженной точности с плавающей запятой и точности денежных значений до центов. Сохраняйте более широкие исходные типы, если требуются точные исходные значения. В примере выведенные столбцы DateTime64 заменяются на DateTime в том же часовом поясе UTC, поскольку запросам из примера не требуется точность до долей секунды. Этот выбор применим только к данному набору данных. Прежде чем применять те же изменения, проверьте требования к диапазону, точности и допустимости NULL-значений для данных в продакшне.

Примените изменения схемы

Создайте таблицу без ключа сортировки, чтобы на этом этапе оценить изменения схемы отдельно:
В каждом запросе рабочей нагрузки замените nyc_taxi.trips_small_inferred на nyc_taxi.trips_small_no_pk, затем повторно выполните все три запроса. В исходном примере были получены следующие характерные результаты: Запросы по-прежнему читают одинаковое число строк, но оптимизированная схема уменьшает объём данных, приходящийся на эти строки. Поэтому длительность запросов и пиковое потребление памяти сокращаются без изменения выборки данных. Сравните размер двух таблиц на диске:
Для этого набора данных оптимизированная схема сокращает объём сжатых данных примерно на 34% — с 7,38 GiB до 4,89 GiB.

Оптимизация ключа сортировки

В семействе MergeTree ключ сортировки определяет, как строки располагаются на диске. ClickHouse строит по этому порядку разреженный первичный индекс, позволяющий пропускать гранулы, не соответствующие фильтрам запроса. В отличие от первичного ключа во многих транзакционных базах данных, он не обеспечивает уникальность. Ключ сортировки должен соответствовать фильтрам, используемым в важных регулярно выполняемых запросах. Порядок столбцов имеет значение: ключ наиболее эффективен, когда запрос фильтрует по его полезному префиксу. Столбцы с небольшой мощностью могут быть эффективными первыми элементами ключа, если по ним часто выполняется фильтрация; для рабочих нагрузок, связанных со временем, также часто полезен компонент времени. Подробные рекомендации см. в разделе Выбор первичного ключа. В этом примере используйте (passenger_count, pickup_datetime, dropoff_datetime). passenger_count имеет мало уникальных значений и используется в фильтре по количеству пассажиров, а pickup_datetime — в агрегации по диапазону дат. Хотя pickup_datetime не является первым столбцом, ClickHouse всё равно может использовать значения из последующих столбцов ключа для исключения данных, когда по ведущему столбцу нет ограничений. Фильтрация по полезному префиксу ключа сортировки обычно обеспечивает более эффективное отсечение данных.

Примените изменение ключа сортировки

Создайте таблицу с той же оптимизированной схемой, что и на предыдущей стадии. Измените только ключ сортировки:
В каждом запросе рабочей нагрузки замените имя таблицы на nyc_taxi.trips_small_pk, затем повторно выполните все три запроса.

Сравните результаты

В исходном руководстве приведены следующие показатели для трёх этапов: Оптимизация схемы сокращает объём хранимых данных и снижает затраты на обработку выбранных значений. Ключ сортировки обеспечивает наибольшее дополнительное ускорение агрегации по диапазону дат, поскольку ClickHouse может пропускать гранулы за пределами этого диапазона. Фильтр по количеству пассажиров также считывает меньше строк, так как фильтрация выполняется по первому ключевому столбцу. Фильтр по вычисленной скорости по-прежнему считывает всю таблицу, поскольку условие фильтрации вычисляется на основе pickup_datetime, dropoff_datetime и trip_distance, а не по полезному префиксу ключа сортировки. Проверьте агрегацию по диапазону дат с помощью EXPLAIN indexes = 1:
В ClickHouse 25.9 и более поздних версиях эти настройки обеспечивают, что EXPLAIN показывает используемые индексы, а также исключаемые ими части и гранулы.
Первичный индекс отбирает 5 061 из 40 167 гранул. За счёт этого при агрегации по диапазону дат обрабатывается 41,46 миллиона строк вместо всех 329,04 миллиона.

Примените метод к своей рабочей нагрузке

Используйте ту же последовательность действий для своей рабочей нагрузки:
  1. Зафиксируйте базовые значения длительности, числа прочитанных строк и байтов, а также пикового потребления памяти.
  2. Проверьте, не используют ли выбранные столбцы неоправданно широкие или излишне универсальные типы.
  3. Внесите изменения в схему и оцените их эффект, не меняя структуру данных.
  4. Проверьте ключ сортировки, основанный на фильтрах, используемых в важных регулярно выполняемых запросах.
  5. Сравните объём выбираемых данных с помощью EXPLAIN indexes = 1, затем повторно выполните базовые запросы в сопоставимых условиях.
Не следует считать, что типы или ключ сортировки из этого примера подойдут для другого набора данных. Принимайте эти решения на основе наблюдаемых значений и фильтров запросов.

Следующие шаги

Вернитесь к разделу Подходы к оптимизации, чтобы рассмотреть проекции, materialized views, индексы пропуска данных или предварительные вычисления, если изменение схемы и ключа сортировки не устраняет выявленное узкое место.
Последнее изменение 28 августа 2026 г.