Перед началом работы
nyc_taxi.trips_small_inferred, если ещё не сделали этого:
Настройка демонстрационного набора данных
Настройка демонстрационного набора данных
Размер исходного файла Parquet составляет примерно 5,8 ГБ. Его загрузка может занять несколько минут в зависимости от скорости сети и доступных ресурсов.
ORDER BY (), поэтому фильтр по дате не может задействовать ключ сортировки для исключения данных при чтении. Используйте этот пример для отработки метода сравнения, а не как ориентир по производительности.
Принцип работы
- Выполните исходный запрос, чтобы получить базовые значения.
- Сохраните
GROUP BY, замените агрегатные вычисления в запросе наcountи удалите последующие операции, например сортировку и форматирование вывода. - Удалите группировку и выполните
countбез группировки, чтобы приблизительно оценить объём работы, связанный со сканированием, фильтрацией и любыми JOIN.
SELECT за раз: сохраняйте эквивалентные источники данных и фильтры, удаляйте по одной операции и проверяйте план выполнения после каждого изменения.
Эти различия — диагностические оценки, а не точные измерения стадий выполнения ClickHouse. Изменение запроса может изменить его план выполнения, считываемые столбцы и данные, передаваемые между стадиями. Используйте результаты для формирования гипотезы. Затем проверьте её с помощью журнала запросов и
EXPLAIN.Создайте воспроизводимую базовую конфигурацию
- Не изменяйте секции
FROM,JOIN,PREWHEREиWHERE, чтобы во всех сравнениях использовались одни и те же данные и временной диапазон. - Запускайте каждую версию запроса несколько раз при схожей нагрузке на систему.
- Обеспечьте одинаковые условия кэширования. Либо выполняйте каждый вариант запроса с предварительным прогревом перед фиксацией измерений, либо отключите перечисленные ниже кэши. Не сравнивайте запуски с кэшем и без него.
- Фиксируйте репрезентативную длительность, например медиану результатов повторных запусков после прогрева, а не ориентируйтесь на самый быстрый или самый медленный результат.
- Изменяйте по одной переменной за раз, чтобы можно было связать различие в производительности с конкретным изменением.
count в запуске C не использовал оптимизированный план выполнения, обходящий сканирование, которое вы намерены сравнить.
Эти операторы
SET действуют только в текущем сеансе. Выполняйте все сравнительные запросы в этом сеансе или применяйте одинаковые настройки при каждом запуске. Настройка файлового кэша не отключает кэш страниц операционной системы и не отключает все кэши ClickHouse. По завершении закройте выделенный сеанс или верните каждой настройке прежнее значение.-
Назначьте каждому запуску уникальный ID запроса или запишите ID, созданный интерфейсом выполнения запросов. Например, обозначайте повторные запуски как
bottleneck-a-1,bottleneck-a-2иbottleneck-a-3. При использованииclickhouse-clientпередавайте--query_id your-query-idпри выполнении запроса. - Выполните каждый сравнительный запрос несколько раз в одинаковых условиях. Отделяйте прогревочные запуски от измеряемых.
-
Сбросьте журнал запросов перед поиском недавно завершённых запросов:
Если вы не можете выполнить
SYSTEM FLUSH LOGS, дождитесь автоматического сброса журнала запросов, затем повторите поиск. Если запись так и не появилась, убедитесь, что журналирование запросов включено, у вас есть доступ на чтениеsystem.query_logи вы обращаетесь к узлу, на котором выполнялся запрос. -
Найдите завершённую запись для каждого ID запроса.
system.query_logзаписывает событияQueryStartиQueryFinishдля завершённого запроса. Отфильтруйте записи поQueryFinish: оно содержит окончательную длительность, число прочитанных строк и байт, а также пиковое потребление памяти: -
Для каждой версии запроса используйте медианную длительность измеряемых запусков. Запишите
read_rows,read_bytesи пиковое потребление памяти для запуска, наиболее близкого к этой медиане, чтобы измерения были привязаны к фактическому запуску.
Для распределённых запросов значение
memory_usage в записи QueryFinish инициирующего запроса не отражает пиковое потребление памяти по всему кластеру. Используйте initial_query_id для проверки дочерних записей QueryFinish на участвующих узлах.system.query_log.
- Таблица
- CSV
Последовательно выполняйте всё более простые запросы
GROUP BY, пропустите запуск B, как описано ниже.
1
Запуск A: измерение исходного запроса
Выполните полный запрос, не изменяя его фильтры, группировку, агрегатные выражения, сортировку или вывод. Это позволит установить базовые значения длительности выполнения, количества прочитанных строк и байтов, а также пикового потребления памяти.Этот запрос группирует поездки по типу оплаты и вычисляет несколько агрегатных значений:Запишите полученные показатели как запуск A.
2
Запуск B: сохранение группировки с count
Сохраните в запросе Запуск B по-прежнему сканирует и фильтрует данные, выполняет все JOIN и формирует группы. Сравните его длительность с запуском A, чтобы оценить вклад исходных агрегатных выражений и операций после агрегации. Также сравните
FROM, JOIN, PREWHERE, WHERE и ключи группировки. Замените агрегатные выражения на сгруппированный count. Удалите операции после агрегации, включая исходные выражения сортировки и вывода.read_bytes, поскольку удаление агрегатных выражений может исключить некоторые столбцы из чтения.Если исходный запрос не содержит GROUP BY, изолировать этап группировки невозможно. Пропустите запуск B и сравните исходный запрос непосредственно с запуском C.3
Запуск C: удаление группировки
Удалите Запуск C даёт базовый уровень для операций, сохраняемых в его плане выполнения, а не изолированную оценку сканирования или фильтрации. Сравните его с запуском B, чтобы оценить вклад группировки. Также сравните
GROUP BY и верните один count. Оставьте секции FROM, JOIN, PREWHERE и WHERE без изменений, чтобы оставшаяся работа была сопоставимой.read_bytes, поскольку удаление ключа группировки может сократить число читаемых столбцов. Возвращаемый count показывает, сколько строк поступает на агрегацию после применения сохранённых фильтров и JOIN.Прежде чем интерпретировать результаты запуска C, убедитесь, что его план выполнения читает нужный источник данных и применяет сохранённые фильтры. Проекция или подсчёт на основе метаданных могут изменить выполняемую работу. Чтобы получить базовый уровень на основе сканирования, отключите оптимизацию, указанную в плане, для всех трёх запусков: используйте optimize_use_implicit_projections = 0 для неявной проекции, optimize_use_projections = 0 для явной проекции или optimize_trivial_count_query = 0 для подсчёта без фильтра на основе метаданных таблицы.Если запуск C по-прежнему выполняется медленно, исследуйте сохраняемые в нём операции, начиная со сканирования и фильтрации. Используйте журнал запросов и EXPLAIN, чтобы подтвердить предполагаемое узкое место, прежде чем изменять запрос.Интерпретация различий
Сравните число прочитанных строк с результатом count
read_rows для запуска C со значением, возвращаемым его count. Например, если read_rows равно 100 миллионам, а count возвращает 1 миллион, ClickHouse просканировал примерно 100 исходных строк на каждую подсчитанную строку. Это означает, что фильтр отсеял большую часть строк, прочитанных из таблицы, но не позволяет определить причину. Это соотношение предназначено для простого сканирования одной таблицы. Для запросов с несколькими источниками данных или проекциями интерпретируйте read_rows с учетом плана выполнения.
В ClickHouse 25.9 и более поздних версиях перед проверкой использования индексов отключите кэш условий запроса и динамическое применение индексов пропуска данных:
EXPLAIN indexes = 1 посмотрите, какие индексы использовал ClickHouse и сколько частей и гранул отсёк каждый из них. Если ClickHouse выбрал больше гранул, чем ожидалось, проверьте, соответствуют ли фильтры ключу сортировки таблицы и позволяют ли отсечение партиций или индекс пропуска данных исключить больше гранул. Если в плане нет раздела Indexes, EXPLAIN не сообщил об отсечении по индексам для этого запроса. В отличие от этого, при аналитическом запросе ко всей таблице ожидается чтение большей её части.
Проверьте предполагаемое узкое место
- Если узкое место связано со сканированием или фильтрацией, используйте
EXPLAIN indexes = 1с описанными выше настройками, чтобы увидеть, какие индексы использует ClickHouse и сколько частей и гранул исключает каждый индекс. Проверьте, использует ли план неявную проекцию вместо ожидаемого сканирования. - Если узкое место связано с группировкой или агрегацией, изучите соответствующие события профиля запроса и пиковое потребление памяти.
- Если запуск C по-прежнему выполняется медленно и содержит JOIN, сравните его с диагностическим запросом, в котором JOIN удаляются по одному. Значительное сокращение длительности указывает на то, что удалённый JOIN создаёт существенную нагрузку. Поскольку удаление JOIN меняет смысл запроса, используйте это сравнение только для изоляции времени выполнения, а изменения количества строк интерпретируйте отдельно.
- Если узкое место связано с другой операцией, сохранённой в запуске C, изучите план выполнения и соответствующие события профиля запроса.
EXPLAIN, см. в руководстве по диагностике медленных запросов. Внесите одно целевое изменение, затем повторите запуски A, B и C в тех же условиях. Убедитесь, что изменение сократило объём целевой работы и не переместило узкое место в другое место.