Кодеки сжатия
Правильный выбор кодека сжатия по типу столбца может уменьшить объём хранения в 10 раз и ускорить запросы (меньше I/O). ClickHouse поддерживает конвейеры кодеков — несколько кодеков, применяемых последовательно. Кодек задаётся в DDL для каждого столбца.
Доступные кодеки
КодекКодек: название алгоритма. Кодеки делятся на две группы: кодеки данных (Delta, DoubleDelta, Gorilla, T64 — преобразуют данные перед сжатием) и кодеки сжатия (LZ4, ZSTD — собственно сжимают). Рекомендуется комбинировать: сначала кодек данных, затем кодек сжатия. | Лучше всего дляЛучше всего подходит для: типы данных и паттерны, при которых кодек даёт максимальный эффект. Применение кодека к неподходящим данным может ухудшить результат. | Степень сжатияСтепень сжатия: насколько кодек уменьшает объём данных. Высокая степень сжатия = меньше I/O при чтении, но потенциально больше CPU на распаковку. | СкоростьСкорость: скорость сжатия и распаковки. Для OLAP-запросов важнее скорость распаковки — данные читаются чаще, чем пишутся. | ПрименениеТипичное применение: конкретные сценарии, где кодек показывает себя лучше всего. Используйте как отправную точку, затем измерьте на реальных данных. |
|---|---|---|---|---|
LZ4DefaultLZ4: кодек сжатия общего назначения. Используется по умолчанию если кодек не указан явно. Алгоритм LZ4 — block-oriented lossless compression. Чрезвычайно быстрая распаковка (~3 GB/s на одном ядре). Хорошая степень сжатия для большинства данных. | Большинство столбцов | Средняя | Очень быстро | Все столбцы без особых требований |
ZSTD(N)N=1..22ZSTD: Zstandard — алгоритм сжатия с настраиваемым уровнем. N=1 — быстро, хорошее сжатие. N=3 — рекомендуемый баланс. N=22 — максимальное сжатие, очень медленно. Для read-heavy таблиц используйте N=1-3: высокие уровни замедляют распаковку и убивают latency запросов. | Архивные данные, строки | Высокая (ZSTD(3)) | Медленнее LZ4 | Холодные данные, URL, строки высокой кардинальности |
DeltaКодек данныхDelta: преобразует значения в разности между соседними значениями. Вместо [100, 101, 102, 103] хранит [100, 1, 1, 1]. Для монотонно возрастающих последовательностей (счётчики, timestamp в Unix time) разности близки к константе — последующее LZ4/ZSTD сжимает их почти идеально. | Монотонные целые, timestamp | Очень высокая (с LZ4/ZSTD) | Быстро | DateTime, auto-increment ID, Unix epoch |
DoubleDeltaКодек данныхDoubleDelta: разности разностей (вторые производные). Для данных с регулярным интервалом (IoT-сенсоры, метрики с фиксированным шагом) первые дельты почти одинаковы, вторые — почти нули. Это обеспечивает исключительное сжатие для временных рядов с равномерным интервалом. | IoT, метрики с фиксированным интервалом | Отлично для IoT | Быстро | Timestamp с фиксированным шагом (каждые 10 сек) |
GorillaКодек данныхGorilla: кодек для float-значений. Использует XOR между соседними значениями — для медленно меняющихся float (температура, цена, метрики) XOR даёт много нулей и повторяющихся паттернов. Вдохновлён оригинальным Gorilla TSDB (Facebook). Эффективен когда значение меняется незначительно между соседними строками. | Float с малыми изменениями | Высокая для сенсорных данных | Быстро | Температура, цена акции, метрики CPU/RAM |
T64Кодек данныхT64: транспонирует матрицу битов 64 значений. Эффективен для целых чисел с ограниченным диапазоном значений, где старшие биты одинаковы. При диапазоне 0..255 все значения помещаются в 8 бит — T64 хранит только значимые биты, остальные 56 бит на значение отбрасывает. | Целые с ограниченным диапазоном | Высокая | Очень быстро | Enum-like целые, статус-коды, малые счётчики |
Конвейеры кодеков
CODEC(Delta, ZSTD(3)) — конвейер из двух кодеков. Применяются слева направо:
- Delta — преобразует исходные значения в разности (кодек данных)
- ZSTD(3) — сжимает поток разностей (кодек сжатия)
Почему это работает: Delta encoding для монотонных данных превращает последовательность вида [1000000, 1000001, 1000002, ...] в [1000000, 1, 1, 1, ...]. Затем ZSTD сжимает последовательность из почти одинаковых маленьких значений практически идеально.
-- Пример объявления кодеков в DDL таблицы
CREATE TABLE metrics (
ts DateTime64(3) CODEC(DoubleDelta, LZ4),
user_id UInt32 CODEC(Delta, LZ4),
session_id UInt64 CODEC(Delta, ZSTD(1)),
temperature Float64 CODEC(Gorilla, LZ4),
status_code UInt8 CODEC(T64, LZ4),
url String CODEC(ZSTD(3))
) ENGINE = MergeTree()
ORDER BY (ts, user_id);
Практические рекомендации по типам столбцов
| Тип столбца | Рекомендуемый кодек | Почему |
|---|---|---|
DateTime / DateTime64 с регулярным интервалом | CODEC(DoubleDelta, LZ4) | Регулярный интервал = константные вторые дельты |
DateTime / DateTime64 с нерегулярным интервалом | CODEC(Delta, ZSTD(1)) | Первые дельты малы, ZSTD(1) быстрый |
UInt32 / UInt64 счётчик (монотонный) | CODEC(Delta, LZ4) | Разности ~1, LZ4 сжимает идеально |
UInt32 / UInt64 ограниченного диапазона | CODEC(T64, LZ4) | Малый диапазон = мало значимых бит |
Float64 медленно меняющийся | CODEC(Gorilla, LZ4) | XOR между соседними значениями даёт много нулей |
String URL, путь (высокая кардинальность) | CODEC(ZSTD(3)) | ZSTD даёт хорошее сжатие для строк |
| По умолчанию (без явного кодека) | LZ4 | Применяется автоматически |
Измерение степени сжатия
Прежде чем менять кодеки, измерьте реальную ситуацию:
-- Степень сжатия по столбцам
SELECT
column,
formatReadableSize(data_compressed_bytes) AS compressed,
formatReadableSize(data_uncompressed_bytes) AS uncompressed,
round(data_uncompressed_bytes / data_compressed_bytes, 2) AS ratio
FROM system.columns
WHERE table = 'your_table'
ORDER BY data_uncompressed_bytes DESC;
-- Детально по parts и столбцам
SELECT
column,
sum(column_data_compressed_bytes) AS compressed,
sum(column_data_uncompressed_bytes) AS uncompressed,
round(sum(column_data_uncompressed_bytes) / sum(column_data_compressed_bytes), 2) AS ratio
FROM system.parts_columns
WHERE table = 'your_table' AND active = 1
GROUP BY column
ORDER BY uncompressed DESC;
После смены кодека ALTER TABLE не перепишет существующие parts — изменения применяются только к новым parts. Для полной перезаписи используйте ALTER TABLE ... MATERIALIZE COLUMN или OPTIMIZE TABLE ... FINAL (в dev среде).
Начинайте с настроек по умолчанию (LZ4). Меняйте кодеки только после измерений на реальных данных. Преждевременная оптимизация кодеков редко окупается: эффект проявляется только на характерных паттернах данных. Gorilla на случайных float ничем не лучше LZ4.
Ключевые выводы
- LZ4 по умолчанию — хороший выбор для большинства столбцов. Меняйте только с измерениями.
- Конвейер L-to-R:
CODEC(Delta, ZSTD(3))— сначала Delta (кодек данных), затем ZSTD (сжатие). - Delta + LZ4/ZSTD — для монотонных целых и timestamp с нерегулярным шагом.
- DoubleDelta — для IoT и метрик с фиксированным интервалом выборки.
- Gorilla — для float, меняющихся плавно (температура, цена, метрики).
- ZSTD(N) высокого уровня (N=10+) — только для архивных, редко читаемых данных. Высокие уровни замедляют распаковку и увеличивают latency запросов.