Техники моделирования данных
Это Часть 3 руководства по миграции с PostgreSQL на ClickHouse. Этот материал можно считать введением с целью помочь пользователям развернуть начальную функциональную систему, которая соответствует лучшим практикам ClickHouse. Он избегает сложных тем и не приведет к полностью оптимизированной схеме; скорее, он предоставляет прочную основу для пользователей, чтобы построить производственную систему и базировать свое обучение.
Мы рекомендуем пользователям, мигрирующим с Postgres, прочитать руководство по моделированию данных в ClickHouse. Это руководство использует тот же набор данных Stack Overflow и рассматривает несколько подходов с использованием возможностей ClickHouse.
Партиции
Пользователи Postgres будут знакомы с концепцией партиционирования таблиц для повышения производительности и управляемости больших баз данных путем деления таблиц на более мелкие, более управляемые части, называемые партициями. Это партиционирование может быть достигнуто с использованием диапазона по указанной колонке (например, даты), определенных списков или с помощью хеширования по ключу. Это позволяет администраторам организовывать данные на основе определенных критериев, таких как диапазоны дат или географические местоположения. Партиционирование помогает улучшить производительность запросов, позволяя более быстрый доступ к данным через обрезку партиций и более эффективную индексацию. Оно также помогает в таких операциях обслуживания, как резервное копирование и удаление данных, позволяя выполнять операции с отдельными партициями, а не с всей таблицей. Кроме того, партиционирование может значительно повысить масштабируемость баз данных PostgreSQL за счет распределения нагрузки по нескольким партициям.
В ClickHouse партиционирование задается для таблицы при ее первоначальном определении с помощью оператора PARTITION BY
. Этот оператор может содержать SQL-выражение по любым колонкам, результаты которого определят, в какую партицию будет отправлена строка.

Части данных логически связаны с каждой партицией на диске и могут запрашиваться изолированно. В приведенном ниже примере мы партиционируем таблицу posts
по годам, используя выражение toYear(CreationDate)
. По мере вставки строк в ClickHouse это выражение будет оцениваться для каждой строки и направляться в результирующую партицию, если она существует (если строка является первой для года, партиция будет создана).
Применение партиций
Партиционирование в ClickHouse имеет аналогичные применения, как и в Postgres, но с некоторыми тонкими отличиями. Более конкретно:
- Управление данными - В ClickHouse пользователи должны в первую очередь рассматривать партиционирование как функцию управления данными, а не как технику оптимизации запросов. Разделяя данные логически по ключу, с каждой партицией можно работать независимо, например, удалять. Это позволяет пользователям перемещать партиции и, таким образом, подмножества, между уровнями хранения эффективно по времени или истекать данные/эффективно удалять из кластера. В приведенном примере мы удаляем посты из 2008 года.
- Оптимизация запросов - Хотя партиции могут способствовать повышению производительности запросов, это зависит от штурвалов доступа. Если запросы нацелены только на несколько партиций (желательно одну), производительность может потенциально улучшиться. Это обычно полезно, если ключ партиционирования не входит в первичный ключ, и вы фильтруете по нему. Однако запросы, которые должны охватывать многие партиции, могут работать хуже, чем если бы партиционирование не использовалось (так как может быть больше частей в результате партиционирования). Польза от нацеливания на одну партицию будет еще менее выраженной или вообще отсутствовать, если ключ партиционирования уже является ранним элементом в первичном ключе. Партиционирование также может быть использовано для оптимизации запросов GROUP BY, если значения в каждой партиции уникальны. Тем не менее, в общем, пользователи должны убедиться, что первичный ключ оптимизирован и рассматривать партиционирование как технику оптимизации запросов только в исключительных случаях, когда шаблоны доступа обращаются к конкретному предсказуемому подмножеству дня, например, партиционирование по дням, с большинством запросов за последний день.
Рекомендации по партициям
Пользователи должны рассматривать партиционирование как технику управления данными. Это идеально подходит, когда данные необходимо истекать из кластера при работе с данными временных рядов, например, самую старую партицию можно просто удалить.
Важно: Убедитесь, что ваше выражение ключа партиционирования не приводит к высокому кардинальному множеству, т.е. создание более 100 партиций следует избегать. Например, не партиционируйте ваши данные по столбцам с высокой кардинальностью, таким как идентификаторы клиентов или имена. Вместо этого сделайте идентификатор клиента или имя первым столбцом в выражении ORDER BY.
Внутри ClickHouse создает части для вставленных данных. По мере вставки большего количества данных количество частей увеличивается. Чтобы предотвратить чрезмерное количество частей, что приведет к ухудшению производительности запросов (больше файлов для чтения), части объединяются в фоновом асинхронном процессе. Если количество частей превышает предварительно установленный предел, ClickHouse выдаст исключение при вставке - в виде ошибки "слишком много частей". Это не должно происходить в нормальных условиях работы и происходит только в случае неправильной настройки или некорректного использования ClickHouse, например, при слишком многих мелких вставках.
Поскольку части создаются для каждой партиции изолированно, увеличение количества партиций приводит к увеличению количества частей, т. е. оно является кратным количеству партиций. Ключи партиционирования с высокой кардинальностью могут, следовательно, вызывать эту ошибку и должны быть избегаемы.
Материализованные представления против проекций
Postgres позволяет создавать несколько индексов на одной таблице, что позволяет оптимизировать различные шаблоны доступа. Эта гибкость позволяет администраторам и разработчикам настраивать производительность базы данных под конкретные запросы и операционные нужды. Концепция проекций ClickHouse, хотя и не полностью аналогична этому, позволяет пользователям задавать несколько операторов ORDER BY
для таблицы.
В документах по моделированию данных ClickHouse мы исследуем, как можно использовать материализованные представления в ClickHouse для предвычисления агрегатов, преобразования строк и оптимизации запросов для различных шаблонов доступа.
Для последнего из них мы предоставили пример, где материализованное представление отправляет строки в целевую таблицу с другим ключом упорядочивания, чем у оригинальной таблицы, получающей вставки.
Например, рассмотрим следующий запрос:
Этот запрос требует сканирования всех 90 миллионов строк (хотя и быстро), поскольку UserId
не является ключом упорядочивания. Ранее мы решили эту задачу с помощью материализованного представления, выступающего в качестве справочника для PostId
. Ту же проблему можно решить с помощью проекции. Команда ниже добавляет проекцию для ORDER BY user_id
.
Обратите внимание, что сначала мы должны создать проекцию, а затем материализовать ее. Эта последняя команда приводит к тому, что данные будут храниться дважды на диске в двух разных порядках. Проекция также может быть определена при создании данных, как показано ниже, и будет автоматически поддерживаться по мере вставки данных.
Если проекция создается с помощью ALTER
, создание осуществляется асинхронно, когда выдается команда MATERIALIZE PROJECTION
. Пользователи могут подтвердить ход этой операции с помощью следующего запроса, ожидая, что is_done=1
.
Если мы повторим вышеприведенный запрос, мы увидим, что производительность значительно улучшилась за счет дополнительного пространства для хранения.
С помощью команды EXPLAIN
мы также подтверждаем, что проекция была использована для выполнения этого запроса:
Когда использовать проекции
Проекции являются привлекательной функцией для новых пользователей, поскольку они автоматически поддерживаются по мере вставки данных. Более того, запросы можно отправлять лишь в одну таблицу, где проекции используются, когда это возможно, чтобы ускорить время ответа.

Это в отличие от материализованных представлений, где пользователю необходимо выбрать соответствующую оптимизированную целевую таблицу или переписать свой запрос, в зависимости от фильтров. Это создает большую нагрузку на клиентские приложения и увеличивает их сложность.
Несмотря на эти преимущества, у проекций есть некоторые внутренние ограничения, о которых пользователи должны быть в курсе, и поэтому они должны использоваться экономно.
- Проекции не позволяют использовать разные TTL для исходной таблицы и (скрытой) целевой таблицы, в то время как материализованные представления позволяют разные TTL.
- Проекции в настоящее время не поддерживают
optimize_read_in_order
для (скрытой) целевой таблицы. - Легкие обновления и удаления не поддерживаются для таблиц с проекциями.
- Материализованные представления могут быть связаны: целевая таблица одного материализованного представления может быть исходной таблицей другого материализованного представления и так далее. Это невозможно с проекциями.
- Проекции не поддерживают соединения; материализованные представления поддерживают.
- Проекции не поддерживают фильтры (WHERE); материализованные представления поддерживают.
Мы рекомендуем использовать проекции, когда:
- Необходима полная переработка данных. Хотя выражение в проекции может теоретически использовать
GROUP BY
, материализованные представления более эффективны для поддержания агрегатов. Оптимизатор запросов также с большей вероятностью использует проекции, которые используют простую переработку, т.е.SELECT * ORDER BY x
. Пользователи могут выбрать подмножество колонок в этом выражении, чтобы уменьшить объем занимаемого пространства. - Пользователи готовы к сопутствующему увеличению объема хранения и накладным расходам на запись данных дважды. Проверьте влияние на скорость вставки и оцените накладные расходы на хранение.