Типы данных ClickHouse

Правильный тип колонки — это и меньше места на диске, и выше скорость.

Тип данных в ClickHouse точно задаёт, сколько байт занимает значение и как оно сжимается; экономный тип = меньше чтения с диска.

Числа: размер имеет значение

В отличие от «универсального» INTEGER, ClickHouse предлагает числа разной ширины, и вы выбираете минимально достаточную:

ТипРазмерДиапазон (примерно)
UInt81 байт0..255
UInt324 байта0..4 млрд
Int648 байт±9·10^18
Float648 байтдробные

Если значение колонки — возраст (0–120), хранить его в UInt8, а не в Int64: экономия в 8 раз на каждой строке. На миллиардах строк это гигабайты.

Строки, даты, время

  • String — строка произвольной длины.
  • FixedString(N) — строка ровно N байт (например, код страны).
  • Date — дата (2 байта), DateTime — дата-время до секунды (4 байта), DateTime64 — с долями секунды.

Особые типы ClickHouse

LowCardinality

Если в колонке мало разных значений (страна, браузер, статус), оберните тип в LowCardinality. ClickHouse заведёт словарь уникальных значений и будет хранить компактные номера вместо повторяющихся строк — меньше места и быстрее GROUP BY.

country LowCardinality(String)
status  LowCardinality(String)

Nullable

По умолчанию колонки не допускают NULL. Чтобы разрешить пропуски, оборачивают в Nullable — но это стоит дополнительного байта-флага на значение и замедляет работу. Используйте только когда NULL действительно нужен.

age      UInt8            -- NULL запрещён
email    Nullable(String) -- NULL разрешён, но дороже

Enum

Enum8/Enum16 — фиксированный набор строковых меток, хранящихся как маленькие числа: компактно и с проверкой допустимых значений.

Иллюстрация экономии

Прикинем экономию места при выборе UInt8 вместо Int64 на 100 млн строк (чистый Python):

rows = 100_000_000
int64_bytes = rows * 8
uint8_bytes = rows * 1
print("Int64:", int64_bytes // (1024*1024), "МБ")
print("UInt8:", uint8_bytes // (1024*1024), "МБ")
print("Экономия в", int64_bytes // uint8_bytes, "раз")

Вывод:

Int64: 762 МБ
UInt8: 95 МБ
Экономия в 8 раз

Как работает под капотом

Каждая колонка хранится в своём типе и сжимается. Узкий тип не только занимает меньше места до сжатия, но и лучше векторизуется. LowCardinality фактически реализует словарное кодирование, которое в строковых базах пришлось бы делать руками через справочные таблицы.

Частые ошибки

  • Всё подряд в Nullable. Лишние NULL-флаги раздувают данные и тормозят запросы; включайте Nullable осознанно.
  • Хранить мелкие числа в широких типах. Возраст в Int64 — восьмикратный перерасход.
  • Категории как обычный String. Для колонок с малым числом значений почти всегда лучше LowCardinality.

Итоги

  • Выбирайте минимально достаточную ширину числового типа.
  • LowCardinality — для колонок с малым числом различных значений.
  • Nullable — только когда NULL реально нужен (он стоит ресурсов).
  • Точный тип = меньше диска и быстрее запросы.
Проверьте себя
1. В какой тип лучше обернуть колонку «страна», где всего ~200 уникальных значений?
ANullable(String)
BLowCardinality(String)
CFloat64
DFixedString(100)
2. Почему не стоит делать все колонки Nullable?
ANullable запрещён в ClickHouse
BКаждое значение получает дополнительный байт-флаг и работа замедляется
CNullable удваивает число строк
DNullable отключает сжатие полностью
3. Колонка хранит возраст (0–120). Какой тип экономнее всего?
AInt64
BFloat64
CUInt8
DString