Перейти к основному содержимому
Перейти к основному содержимому

Выбор типов данных

Одна из основных причин производительности запросов в ClickHouse — это его эффективное сжатие данных. Меньший объем данных на диске приводит к более быстрым запросам и вставкам за счет минимизации накладных расходов на ввод-вывод. Столбцовая архитектура ClickHouse естественным образом располагает похожие данные рядом, позволяя кодекам и алгоритмам сжатия значительно уменьшать размер данных. Чтобы максимально использовать эти преимущества сжатия, важно тщательно выбирать подходящие типы данных.

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

Некоторые простые рекомендации могут значительно улучшить схему:

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

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

  • Минимизируйте точность чисел: Выбирайте числовые типы с минимальной шириной бит, которая все же охватывает ожидаемый диапазон данных. Например, предпочтите UInt16 вместо Int32, если отрицательные значения не нужны, и диапазон вписывается в 0–65535.

  • Оптимизируйте точность дат и времени: Выбирайте самый грубый тип даты или времени, который соответствует требованиям запроса. Используйте Date или Date32 для полей только с датами, и предпочтите DateTime над DateTime64, если миллисекундная или более высокая точность не является обязательной.

  • Используйте LowCardinality и специализированные типы: Для колонок с количеством уникальных значений менее примерно 10,000 используйте типы LowCardinality, чтобы значительно сократить объем хранения за счет кодирования словарей. Аналогично, используйте FixedString только в том случае, если значения колонки являются строго строками фиксированной длины (например, коды стран или валют), и предпочтите типы Enum для колонок с конечным набором возможных значений для создания эффективного хранения и встроенной проверки данных.

  • Enums для проверки данных: Тип Enum может быть использован для эффективного кодирования перечисляемых типов. Enums могут занимать 8 или 16 бит в зависимости от числа уникальных значений, которые они должны хранить. Рассматривайте возможность использования этого типа, если вам нужна либо связанная проверка во время вставки (необъявленные значения будут отклонены), либо вы хотите выполнять запросы, которые используют естественный порядок значений Enum, например, представьте колонку обратной связи, содержащую ответы пользователей Enum(':(' = 1, ':|' = 2, ':)' = 3).

Пример

ClickHouse предлагает встроенные инструменты для упрощения оптимизации типов. Например, вывод схемы может автоматически определить начальные типы. Рассмотрим набор данных Stack Overflow, общедоступный в формате Parquet. Запуск простого вывода схемы с помощью команды DESCRIBE предоставляет первоначальную не оптимизированную схему.

примечание

По умолчанию ClickHouse отображает их в эквивалентные Nullable типы. Это предпочтительно, так как схема основана только на выборке строк.

примечание

Обратите внимание, что ниже мы используем шаблон glob *.parquet, чтобы прочитать все файлы в папке stackoverflow/parquet/posts.

Применяя наши ранние простые правила к таблице постов, мы можем определить оптимальный тип для каждой колонки:

КолонкаЧисловаяМин, МакУникальные значенияНулиКомментарийОптимизированный тип
PostTypeIdДа1, 88НетEnum('Question' = 1, 'Answer' = 2, 'Wiki' = 3, 'TagWikiExcerpt' = 4, 'TagWiki' = 5, 'ModeratorNomination' = 6, 'WikiPlaceholder' = 7, 'PrivilegeWiki' = 8)
AcceptedAnswerIdДа0, 7828517012282094ДаРазличение Null с 0 значениемUInt32
CreationDateНет2008-07-31 21:42:52.667000000, 2024-03-31 23:59:17.697000000-НетМиллисекундная точность не требуется, используйте DateTimeDateTime
ScoreДа-217, 349703236НетInt32
ViewCountДа2, 13962748170867НетUInt32
BodyНет--НетString
OwnerUserIdДа-1, 40569156256237ДаInt32
OwnerDisplayNameНет-181251ДаРассматривайте Null как пустую строкуString
LastEditorUserIdДа-1, 99999931104694Да0 неиспользуемое значение может быть использовано для NullsInt32
LastEditorDisplayNameНет-70952ДаРассматривайте Null как пустую строку. Проверили LowCardinality и не было пользыString
LastEditDateНет2008-08-01 13:24:35.051000000, 2024-04-06 21:01:22.697000000-НетМиллисекундная точность не требуется, используйте DateTimeDateTime
LastActivityDateНет2008-08-01 12:19:17.417000000, 2024-04-06 21:01:22.697000000-НетМиллисекундная точность не требуется, используйте DateTimeDateTime
TitleНет--НетРассматривайте Null как пустую строкуString
TagsНет--НетРассматривайте Null как пустую строкуString
AnswerCountДа0, 518216НетРассматривайте Null и 0 как одно и то жеUInt16
CommentCountДа0, 135100НетРассматривайте Null и 0 как одно и то жеUInt8
FavoriteCountДа0, 2256ДаРассматривайте Null и 0 как одно и то жеUInt8
ContentLicenseНет-3НетLowCardinality превосходит FixedStringLowCardinality(String)
ParentIdНет-20696028ДаРассматривайте Null как пустую строкуString
CommunityOwnedDateНет2008-08-12 04:59:35.017000000, 2024-04-01 05:36:41.380000000-ДаРассматривайте значение по умолчанию 1970-01-01 для Nulls. Миллисекундная точность не требуется, используйте DateTimeDateTime
ClosedDateНет2008-09-04 20:56:44, 2024-04-06 18:49:25.393000000-ДаРассматривайте значение по умолчанию 1970-01-01 для Nulls. Миллисекундная точность не требуется, используйте DateTimeDateTime
tip

Определение типа для колонки зависит от понимания её числового диапазона и количества уникальных значений. Чтобы найти диапазон всех колонок и количество уникальных значений, пользователи могут использовать простой запрос SELECT * APPLY min, * APPLY max, * APPLY uniq FROM table FORMAT Vertical. Мы рекомендуем выполнять это на меньшем подмножестве данных, так как это может быть ресурсоемко.

В результате получается следующая оптимизированная схема (с учетом типов):

Избегайте nullable колонок

Nullable column (e.g. Nullable(String)) creates a separate column of UInt8 type. This additional column has to be processed every time a user works with a Nullable column. This leads to additional storage space used and almost always negatively affects performance.

To avoid Nullable columns, consider setting a default value for that column. For example, instead of:

use

Consider your use case, a default value may be inappropriate.