Learning Platform
Глоссарий Troubleshooting
Урок 03.01 · 20 мин
Средний
Типы данныхIntDateTimeStringFixedStringПроизводительность

Оптимальный выбор типов данных

В row-oriented СУБД выбор между Int32 и Int64 редко влияет на производительность: строки хранятся вместе, и разница в 4 байта на столбец тонет в общем весе строки. В ClickHouse каждый столбец хранится в отдельном .bin файле, и каждый лишний байт умножается на миллиарды строк. Выбор типа данных — это первый и самый простой способ сократить объём хранения и ускорить запросы.


Целочисленные типы: каждый байт на счету

ClickHouse предлагает знаковые (Int8, Int16, Int32, Int64, Int128, Int256) и беззнаковые (UInt8, UInt16, UInt32, UInt64, UInt128, UInt256) целые типы. Разница в размерах значительна:

Размеры целочисленных типов
Int8 / UInt8: 1 байтInt8 / UInt8: 1 байт на значение. Int8: от -128 до 127. UInt8: от 0 до 255. Подходит для статусов, флагов, малых счётчиков. При 1 миллиарде строк столбец занимает ~1 ГБ до сжатия.
Int16 / UInt16: 2 байтаInt16 / UInt16: 2 байта на значение. Int16: от -32768 до 32767. UInt16: от 0 до 65535. Подходит для HTTP-статусов (100-599), портов (0-65535), годов. При 1 миллиарде строк столбец занимает ~2 ГБ до сжатия.
Int32 / UInt32: 4 байтаInt32 / UInt32: 4 байта на значение. Int32: от -2.1 млрд до 2.1 млрд. UInt32: от 0 до 4.29 млрд. Подходит для user_id, order_id, Unix timestamp (UInt32 = DateTime). При 1 миллиарде строк столбец занимает ~4 ГБ до сжатия.
Int64 / UInt64: 8 байтInt64 / UInt64: 8 байт на значение. Int64: от -9.2 квинтиллионов до 9.2 квинтиллионов. Часто используется по привычке вместо Int32 -- двойной расход без необходимости. При 1 миллиарде строк столбец занимает ~8 ГБ до сжатия.

Практический пример: столбец с HTTP-статусами (200, 301, 404, 500). Максимальное значение — 599. UInt8 не подходит (максимум 255), но UInt16 (максимум 65535) — более чем достаточно:

  • UInt16: 2 байта на строку. При 1 миллиарде строк = 2 ГБ
  • Int32: 4 байта на строку. При 1 миллиарде строк = 4 ГБ
  • Int64: 8 байт на строку. При 1 миллиарде строк = 8 ГБ

Разница между UInt16 и Int64 — 6 ГБ на один столбец. И это до сжатия: LZ4 сожмёт оба, но меньшие типы сжимаются эффективнее (меньше энтропии в старших байтах).

TIP

Правило выбора целого типа: определите максимально возможное значение, выберите минимальный тип, вмещающий этот диапазон. Если значения всегда неотрицательны, используйте UInt (беззнаковый) — он вдвое увеличивает диапазон при том же размере.


DateTime vs DateTime64: платите только за нужную точность

ClickHouse предлагает два типа для хранения временных меток:

DateTime vs DateTime64: размер и точность
DateTime4 байтаDateTime: 4 байта (UInt32 внутри). Точность до секунды. Диапазон: 1970-01-01 до 2106-02-07. Хранит Unix timestamp. Достаточно для 99% аналитических задач, где миллисекунды не нужны.
DateTime64(3)8 байтDateTime64(3): 8 байт (Int64 внутри). Точность до миллисекунды (3 знака). Параметр: 0-9 (количество знаков после секунды). Двойной расход памяти по сравнению с DateTime. Используйте только когда миллисекундная (или более точная) точность действительно нужна: трейсинг, финансовые транзакции, event sourcing.

Критерий выбора прост: если данные имеют точность до секунды (логи веб-сервера, аналитические события, регистрации пользователей), DateTime экономит 4 байта на каждую строку. DateTime64 нужен только когда миллисекунды (или микросекунды) действительно несут информацию — трейсинг запросов, HFT-транзакции, IoT-сенсоры с частотой выше 1 Гц.


String vs FixedString(N): переменная длина против фиксированной

String — тип переменной длины без ограничений. Каждое значение хранит фактические байты плюс длину. Подходит для большинства текстовых данных.

FixedString(N) — ровно N байт на значение. Короткие значения дополняются нулевыми байтами. Нет overhead на хранение длины. Подходит для данных с гарантированно фиксированной длиной:

ДанныеТипРазмер на строку
ISO country code (‘RUS’, ‘USA’)FixedString(3)3 байта
MD5 hash (hex, 32 символа)FixedString(32)32 байта
UUID (без дефисов, 32 символа)UUID (нативный тип)16 байт
Email адрес (переменная длина)Stringпеременная
URL (переменная длина)Stringпеременная
WARNING

FixedString(N) дополняет короткие значения нулевыми байтами до N. Если передать строку длиннее N байт, ClickHouse выбросит ошибку. Используйте FixedString только для данных с гарантированно одинаковой длиной (коды стран, хэши). Для обычного текста используйте String.


Float32/Float64 vs Decimal: точность вычислений

Типы с плавающей запятой (Float32, Float64) хранят приблизительные значения. Для большинства метрик (температура, latency, CPU usage) это допустимо. Но для денежных вычислений приближение недопустимо:

-- Float64 теряет точность
SELECT toFloat64(0.1) + toFloat64(0.2) AS result
-- Результат: 0.30000000000000004 (не 0.3)

-- Decimal64(2) гарантирует точность
SELECT toDecimal64(0.1, 2) + toDecimal64(0.2, 2) AS result
-- Результат: 0.30
ТипРазмерТочность (всего цифр)Применение
Decimal32(S)4 байтадо 9 цифрЦены до миллионов с копейками
Decimal64(S)8 байтдо 18 цифрФинансовые суммы, бухгалтерия
Decimal128(S)16 байтдо 38 цифрКриптовалюта (18 decimals wei)
Decimal256(S)32 байтадо 76 цифрЭкзотические случаи

Параметр S — количество знаков после запятой. Decimal64(2) хранит до 18 цифр, из которых 2 после запятой.


Проверка реального размера через system.columns

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

SELECT
    name AS column_name,
    type,
    formatReadableSize(sum(data_compressed_bytes)) AS compressed,
    formatReadableSize(sum(data_uncompressed_bytes)) AS uncompressed,
    round(sum(data_uncompressed_bytes) / sum(data_compressed_bytes), 2) AS ratio
FROM system.columns
WHERE table = 'events'
GROUP BY name, type
ORDER BY sum(data_uncompressed_bytes) DESC

Compression ratio зависит от распределения данных. Столбец UInt8 со значениями 0 и 1 сжимается в десятки раз. Столбец String с UUID сжимается слабо. Реальные замеры всегда важнее теории.

Числовые типы SQL: INT, BIGINT, NUMERIC и почему деньги — не FLOAT Integer Encodings: как кодирование зависит от выбора типа
TIP

Проверяйте реальный размер через system.columns: compression ratio зависит от распределения данных. Столбец с тысячей уникальных значений сожмётся лучше, чем столбец с миллионом уникальных значений, даже при одинаковом типе.


Ключевые выводы

  1. Минимальный тип по диапазону — определите максимальное значение и выберите наименьший вмещающий тип. Int8 вместо Int32 экономит 3 байта на строку, что при миллиарде строк равно 3 ГБ.
  2. DateTime по умолчанию — используйте DateTime64 только когда точность ниже секунды действительно нужна. Экономия: 4 байта на строку.
  3. Decimal для денег, Float для метрик — Float64 теряет копейки. Decimal64(2) гарантирует точность финансовых вычислений.
  4. Измеряйте, а не угадывайте — system.columns показывает реальные размеры после сжатия. Теоретический выигрыш не всегда совпадает с практическим.

Проверьте понимание

Результат: 0 из 0
Прикладной
Вопрос 1 из 4. Таблица events имеет столбец http_status_code со значениями 200, 301, 404, 500. Какой тип минимизирует объём хранения?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 8