Оптимизация производительности: стратегии, проверенные сообществом
Это руководство — часть подборки материалов, основанных на результатах встреч сообщества. Для получения дополнительных практических решений и идей вы можете просматривать материалы по конкретным проблемам. Столкнулись с проблемами материализованных представлений? Ознакомьтесь с руководством сообщества по Materialized Views. Если вы сталкиваетесь с медленными запросами и вам нужно больше примеров, у нас также есть руководство по оптимизации запросов.
Располагаете столбцы по кардинальности (от низкой к высокой)
Первичный индекс ClickHouse работает лучше всего, когда в ключе сначала идут столбцы с низкой кардинальностью — это позволяет эффективно пропускать большие блоки данных. Столбцы с высокой кардинальностью, расположенные дальше в ключе, обеспечивают более детальную сортировку внутри этих блоков. Начинайте со столбцов с небольшим числом уникальных значений (например, status, category, country) и заканчивайте столбцами с большим числом уникальных значений (например, user_id, timestamp, session_id).
См. дополнительную документацию по кардинальности и первичным индексам:
Важна временная гранулярность
При использовании меток времени в предложении ORDER BY учитывайте компромисс между кардинальностью и точностью. Метки времени с микросекундной точностью создают очень высокую кардинальность (почти одно уникальное значение на каждую строку), что снижает эффективность разреженного первичного индекса ClickHouse. Округлённые метки времени создают меньшую кардинальность, что позволяет эффективнее пропускать данные при чтении за счёт индекса, но при этом вы теряете точность для временных запросов.
Сосредоточьтесь на отдельных запросах, а не на средних значениях
При отладке производительности ClickHouse не полагайтесь на среднее время выполнения запросов или общесистемные метрики. Вместо этого выясняйте, почему конкретные запросы выполняются медленно. Система может демонстрировать хорошую среднюю производительность, в то время как отдельные запросы страдают от нехватки памяти, неэффективной фильтрации или операций с высокой кардинальностью.
По словам Алексея, CTO ClickHouse: «Правильный подход — спросить себя, почему именно этот запрос был обработан за пять секунд... Меня не волнует, если медианное значение и другие запросы обрабатываются быстро. Меня волнует только мой запрос»
Когда запрос выполняется медленно, не ограничивайтесь анализом средних значений. Спросите себя: «Почему ИМЕННО этот запрос был медленным?» и изучите фактический характер использования ресурсов.
Работа с памятью и сканированием строк
Sentry — это ориентированная на разработчиков платформа отслеживания ошибок, ежедневно обрабатывающая миллиарды событий от более чем 4 млн разработчиков. Их ключевое наблюдение: «Именно кардинальность ключа группировки в этой ситуации определяет потребление памяти» — агрегации с высокой кардинальностью уничтожают производительность из‑за исчерпания памяти, а не из‑за объёма сканируемых строк.
Когда запросы завершаются с ошибкой, определите, является ли это проблемой памяти (слишком много групп) или проблемой сканирования (слишком много строк).
Запрос вроде GROUP BY user_id, error_message, url_path создаёт отдельное состояние в памяти для каждой уникальной комбинации всех трёх значений вместе. При большом количестве пользователей, типов ошибок и URL‑путей вы легко можете получить миллионы состояний агрегации, которые нужно одновременно удерживать в памяти.
В экстремальных случаях Sentry использует детерминистическое семплирование. Выборка 10% снижает использование памяти на 90%, при этом обеспечивая точность порядка 5% для большинства агрегатов:
Это гарантирует, что одни и те же пользователи появляются в каждом запросе, обеспечивая сопоставимые результаты для разных периодов времени. Ключевая идея: cityHash64() выдаёт стабильные хэш-значения для одного и того же ввода, поэтому user_id = 12345 всегда будет хэшироваться в одно и то же значение, гарантируя, что этот пользователь либо всегда будет присутствовать в вашей 10% выборке, либо никогда — без мерцания результатов между запросами.
Оптимизация битовых масок в Sentry
При агрегации по высококардинальным столбцам (например, URL) каждое уникальное значение создаёт отдельное состояние агрегации в памяти, что может привести к её исчерпанию. Решение Sentry: вместо группировки по фактическим строкам URL выполнять группировку по логическим выражениям, которые сворачиваются в битовые маски.
Вот запрос, который вы можете выполнить на своих таблицах, если эта ситуация актуальна для вас:
Вместо того чтобы хранить в памяти каждую уникальную строку, вы храните ответы на вопросы об этих строках в виде целых чисел. Состояние агрегации становится ограниченным и очень маленьким, независимо от разнообразия данных.
От инженерной команды Sentry: «Эти ресурсоёмкие запросы выполняются более чем в 10 раз быстрее, а использование памяти в 100 раз ниже (и, что ещё важнее, ограничено). Наши крупнейшие клиенты больше не сталкиваются с ошибками при поиске реплеев, и теперь мы можем поддерживать клиентов любого размера, не исчерпывая память».
Видеоматериалы
- Lost in the Haystack - Optimizing High Cardinality Aggregations - практический опыт Sentry по оптимизации использования памяти в продакшене
- ClickHouse Performance Analysis - Алексей Миловидов о методологии отладки
- ClickHouse Meetup: Query Optimization Techniques - стратегии оптимизации от сообщества
Читать далее: