Менее известные особенности Clickhouse
Clickhouse
- аналитическая (OLAP) СУБД, главной особенностью которой является подход к хранению данных в виде колонок.
- В отдельных случаях может быть в десятки и даже сотни раз быстрее, чем традиционные SQL-БД.
- SQL синтаксис для выполнения запросов. На текущий момент поддерживает большинство типичных операций.
В данном цикле статей мы рассмотрим некоторые особенности Clickhouse
, которые в чем то отличают его от других баз данных и. Экосистема Clickhouse
развивается уже значительное время, и успела обрасти различными особенностями и возможностями, которые могут быть неочевидны для людей, привыкших к традиционным SQL-ориентированным БД, таким как PostgreSQL
. Возможно, эта информация сделает жизнь аналитиков и инженеров данных немного проще.
Для начала рассмотрим возможности, которые Clickouse
предлагает при выборе типов данных при создании колонок.
Также изучим отличия, характерные для более известных типов данных, при использовании их в Clickhouse
.
Enums/LowCardinality
В силу той особенности, что Clickhouse
является “колоночной” БД, данные в отдельно взятом столбце очень хорошо сжимаются, но есть способ повысить еще сильнее эффективность хранения повторяющихся данных:
Emum
При создании столбца, его тип можно дополнительно “обернуть” в тип хранения Enum
, который по сути аналогичен перечислениям, которые реализованы в большинстве популярных языков программирования.
Суть в том, что при создании таблицы мы вручную “нумеруем” значения, которые физически при хранении будут заменять указанные нами данные, и фактически храним на диске целые числа.
CREATE TABLE enums
(
device_id UInt32,
device_type Enum('server' = 1, 'container' = 2, 'router' = 3)
)
ENGINE = MergeTree
PRIMARY KEY device_id
При вставке данных, возможно вставлять только значения, которые были указаны нами при создании столбца
INSERT INTO enums VALUES (1, 'router')
При попытке вставки непредусмотренных значений будет получена ошибка
-- Приведет к исключению
INSERT INTO enums VALUES (2, 'other')
В остальном, перечисления ведут себя также, как и указанный для перечисления тип данных. При написании запросов на чтение мы можем даже просто указывать выбранные значения в стоковом формате, как в примере:
SELECT * FROM enums WHERE device_type = 'router'
-- получим (1, 'router')
Необходимость вручную указывать значения перечисления, и невозможность последующего добавления новых значений может вызывать значительные неудобства при работе. Поэтому, в Clickhouse
существует еще один тип данных для решения этих проблем.
LowCardinality
- Данный тип схож с
Enums
и создан для эффективного хранения повторяющихся значений. - Создатели
Clckhouse
утверждают, что данный тип однозначно эффективен при хранении до 10,000 уникальных значений. При большем количестве, желательно оценить целесообразность использования, и, можно сказать однозначто, чтоLowCardinality
менее эффективен при более 100 тыс. уникальных значений. - Также как и
Enum
хранит значения в целых числах (Int
), но присваивает их автоматически. - Преимущества над
Enum
:- Возможность динамически добавлять новые значения
- Нет необходимости вручную перечислять все уникальные значения на этапе создания.
CRATE TABLE metrics
(
id UInt32,
-- Эти данные будут храниться как Int
type LowCardinality(String),
value Decimal(30,2)
)
PRIMARY KEY (id, type);
При вставки значений мы также можем просто указывать значения в String
, не беспокоясь о реализации хранения данных в type
.
INSERT INTO metrics VALUES
(1, 'cpu_usage', 0.64)
(1, 'disk_usage', 431.82)
(1, 'avaliable_disk', 17702.44)
В целом, рекомендуется использовать сразу LowCardinality
, так как он лишен недостатков Enum
(кроме, возможно, случаев, когда мы точно уверены, что новые уникальные значения не могут и не должны возникнуть).
Массивы Array(T)
Clickhouse
поддреживает возможность хранения любых типов данных в массивах. Возможно, это не самая уникальная его особенности, однако Кликхаус предлагает также широкий набор функций и инструментов для работы с этим типом (вероятно, мы рассмотрим это в следующих статьях).
Типичный синтаксис для создания столбца массива:
CREATE TABLE array_example
(
id UInt32,
messages Array(String),
timestamps Array(DateTime)
)
ENGINE = MergeTree
ORDER BY tuple()
Существует 2 варинта для определения массивов:
- Используя квадратные скобки [];
- Используя функцию array();
INSERT INTO array_example
VALUES (
1,
array('Hello', ' World!'),
['2023-12-20 20:00:00', 2023-12-20 23:12:21]
)
Nullable тип данных
В целом, Nullable
тип, который широко используется в других базах данных, не очень приветствуется в Clickhouse
, и для этого есть причины.
Столбцы с данными этого типа нельзя использовать в качестве PRIMARY KEY
для таблиц. Также, Nullable
не поддерживается Кликхаусов в “естественном” виде, ввиду особенности хранения данных. Поэтому БД приходится создавать скрытый столбец (из нулей и единиц), невидимый для пользователя, который позволяет базе определять, является ли данное значение NULL или нет. Естественно, в отдельный случаях, такой подход может сказаться напроизводительности работы, поэтому число таких столбцов рекоменуется сводить к минимуму.
Следует также отметить, что по умолчанию, для не-nullable столбцов, Clickhouse
будет преобразовывать NULL значения в “нейтральный” для данного типа элемент (0 для Integer
, пустая строка ’’ для String
и т.д.) .
Синтаксис при создании:
CREATE TABLE nullable_example
(
id UInt32,
device String,
device_type Nullable(String)
)
Вставка данных стандартна для SQL
INSERT INTO nullable_example
VALUES
(1, 'router', 'wired')
(2, 'server', NULL)
Заключение
Понимание особенностей типов данных в Clickhouse
помогает предупредить многие проблемы еще на этапе планирования схем таблиц и добиться большей производительности при работе БД.