Перейти к содержанию
Learning Platform
Глоссарий Troubleshooting
Урок 09.06 · 35 мин
Продвинутый
Encoding SelectionParquet WriterORC WriterDuckDB AnalyzerBtrBlocks CascadePyArrowManual Override

Алгоритмы выбора кодировки

В предыдущих уроках мы разобрали что кодировки делают — dictionary, delta, FOR, bit-packing, prefix, RLE. Теперь ключевой вопрос: кто решает, какую кодировку использовать для конкретной колонки? В большинстве случаев — не человек, а writer.

Каждый формат реализует свой алгоритм выбора: от детерминистических правил Parquet до sampling-based cascade BtrBlocks. Понимание этих алгоритмов объясняет, почему одни и те же данные сжимаются по-разному в разных форматах — и когда ручное переопределение оправдано.

Parquet: детерминистические правила + fallback

Parquet writer использует type-based default + dictionary probe. Алгоритм жёстко зашит в спецификации:

Parquet Writer: алгоритм выбора encoding

Начало column chunk → type из schema

Начало записи column chunk. Writer знает physical type колонки из schema (BOOLEAN, INT32, INT64, FLOAT, DOUBLE, BYTE_ARRAY, FIXED_LEN_BYTE_ARRAY) и logical type (STRING, DATE, TIMESTAMP, DECIMAL, UUID, ...).

BOOLEAN? → RLE (всегда)

Первое решение: тип данных. BOOLEAN всегда → RLE. INT32/INT64 → dictionary по умолчанию (с fallback). BYTE_ARRAY → dictionary по умолчанию. FLOAT/DOUBLE → BYTE_STREAM_SPLIT или PLAIN. Решение детерминистическое — не зависит от данных.
Не BOOLEAN

Начать dictionary encoding

Writer начинает dictionary encoding. Каждое новое значение: lookup в словаре. Если найдено — записать индекс. Если нет — добавить в словарь. Словарь растёт по мере записи.

dict_page > 1 MB?

Проверка: dictionary page size > dictionary_page_size_limit (по умолчанию 1 MB). Это происходит автоматически по мере записи — нет pre-scan данных.

→ RLE_DICTIONARY

Если словарь не превысил лимит к концу column chunk — оставляем RLE_DICTIONARY. Индексы кодируются RLE/Bit-Packed Hybrid. Словарь записывается как dictionary page.

→ Fallback: DELTA_BINARY_PACKED / PLAIN

Если словарь превысил лимит — ВЕСЬ column chunk переписывается с fallback encoding. Для INT: DELTA_BINARY_PACKED. Для STRING: DELTA_BYTE_ARRAY или PLAIN. Атомарное решение: нельзя часть pages оставить dictionary.

Детали fallback по типу

Parquet fallback encoding: type → encoding map
Physical TypeФизический тип в Parquet: определяет набор допустимых кодировок
DefaultКодировка по умолчанию, если writer не настроен иначе
Dict FallbackКодировка при dictionary overflow (dict_page > limit)
Лучший сценарийКакие данные лучше всего сжимаются этой кодировкой
BOOLEAN1-бит значения. Всегда RLE — нет смысла в dictionary для 2 значений.
RLERLE/Bit-Packed Hybrid. Серии: RLE run. Смешанные: bit-packed (1 бит/значение).
Boolean не использует dictionary — нет fallback.
Skewed (95% true)Длинные серии одинаковых значений → RLE runs → минимальный overhead.
INT32 / INT6432/64-bit integers. Включая DATE, TIMESTAMP, DECIMAL через logical types.
RLE_DICTIONARYDictionary по умолчанию. Writer пробует dictionary и переключается на fallback при overflow.
DELTA_BINARY_PACKEDBlock delta + FOR + bit-packing. Fallback для high-cardinality integers. Блоки по 128 значений.
Low card / sortedDictionary: low cardinality (status codes, enum). Delta: sorted/sequential (timestamps, auto-increment IDs).
FLOAT / DOUBLEIEEE 754 floating point. Dictionary часто бесполезен для float.
RLE_DICTIONARYDictionary пробуется, но для float обычно overflow (кардинальность ≈ row_count). Writer быстро переключается.
BYTE_STREAM_SPLIT / PLAINBYTE_STREAM_SPLIT: split IEEE 754 bytes → stream per byte position → compress separately. Exponent bytes повторяются → хорошая компрессия. PLAIN: raw 4/8 bytes — fallback fallback.
Narrow rangeBYTE_STREAM_SPLIT: sensor data с похожими значениями (temperature 20.1–20.9). PLAIN: random floats.
BYTE_ARRAYVariable-length bytes. Включая STRING (UTF-8 logical type).
RLE_DICTIONARYDictionary — лучший выбор для строк с низкой кардинальностью. Probe → fallback при overflow.
DELTA_BYTE_ARRAY / PLAINDELTA_BYTE_ARRAY: prefix encoding для sorted строк. PLAIN: length-prefixed bytes для random строк. Parquet-cpp использует DELTA_LENGTH_BYTE_ARRAY для некоторых строк.
Sorted / low cardDictionary: country codes, department names. Prefix: sorted URLs. PLAIN: high-cardinality random strings.
NOTE

Parquet writer не анализирует данные перед выбором encoding. Он начинает с dictionary и переключается по порогу. Это означает: writer не знает, что dictionary будет бесполезен, пока не потратит CPU на insertion 500K unique значений в hash map. Для заведомо high-cardinality колонок — manual override на PLAIN/DELTA экономит CPU при записи.

ORC: stripe-level statistics

ORC принимает решение per-stripe (по умолчанию ~256 MB) на основе статистик, собранных во время записи:

ORC Writer: решение на уровне stripe

Начало stripe: буферизация в memory

Начало записи stripe. ORC writer буферизирует данные в memory — не записывает до stripe flush. Это позволяет анализировать данные перед выбором encoding.

Сбор статистик: cardinality, min/max, run_lengths

По мере записи ORC собирает статистики per-column: cardinality (через HLL или exact), min/max, total bytes, run lengths. Статистики используются для решения о encoding при flush.

STRING: dict_ratio > 0.8? → Direct

При stripe flush: для каждой строковой колонки — если dictionary ratio > threshold (dictionary_bytes / total_data_bytes), переключиться на direct encoding. По умолчанию threshold ≈ 0.8 (если словарь > 80% от raw данных — бесполезен).

INTEGER: RLEv2 (авто-подкодировка)

Integer колонки: ORC всегда использует RLEv2. Автоматический выбор подкодировки: Short Repeat для серий, Direct для dense packed, Patched Base для outliers, Delta для sequential. Writer анализирует паттерн данных и выбирает подкодировку per-chunk.

STRING: Dictionary или Direct per-stripe

Решение о кодировке фиксируется per-stripe. Следующий stripe может выбрать другую кодировку — данные меняются между stripes.

RLEv2: автоматический выбор подкодировки

ORC RLEv2: выбор подкодировки по данным
ПодкодировкаОдна из 4 подкодировок RLEv2
Паттерн данныхКакой паттерн в данных приводит к этой подкодировке
Header bitsПервые 2 бита header определяют подкодировку: 00, 01, 10, 11
ПримерТипичные данные для этой подкодировки
Short Repeat (00)Серия из 3–10 одинаковых значений. Самая компактная: 1 byte header + value. Для enum-колонок с длинными runs.
3–10 одинаковых подрядWriter сканирует серию одинаковых значений. Если длина 3–10 — Short Repeat. Если длиннее 10 — тоже (разбивается на chunks).
002-bit header = 00. 3-bit width + 3-bit repeat count (3–10).
[5, 5, 5, 5, 5]5 повторов значения 5. Header: repeat=5, width=3 бита, value=5. Total: 2 байта.
Direct (01)Значения без повторов и без delta-паттерна. Bit-packed с минимальной шириной. Для random данных.
Random, no patternWriter не находит ни runs, ни delta-паттерн. Fallback: упаковать значения в минимальное число бит.
012-bit header = 01. 5-bit width + 9-bit length + packed values.
[42, 7, 99, 13, 55]Случайные значения. Width: ceil(log₂(100)) = 7 бит. 5 × 7 = 35 бит + header.
Patched Base (10)FOR с outlier patches. Основная масса в узком диапазоне + редкие выбросы. Уникальная возможность ORC.
Narrow range + outliersWriter вычисляет 90-й перцентиль bit-width. Если разница с 100-м перцентилем значительна — Patched Base выгоднее Direct.
102-bit header = 10. base value + reduced width + patch width + patch list.
[100,102,101,999999,103]4 значения в [100–103] + 1 outlier 999999. Base=100, reduced_width=2 бит, 1 patch.
Delta (11)Second-order delta: base + delta + delta-of-deltas. Для монотонных данных с постоянным или медленно меняющимся шагом.
Sequential / monotonicWriter вычисляет дельты и delta-of-deltas. Если ΔΔ ≈ 0 — Delta mode. Для timestamps с постоянным интервалом: ΔΔ = 0 → 0 бит на значение.
112-bit header = 11. base_value + base_delta + delta-of-deltas (zigzag + bit-packed).
[1000,1005,1010,1015]Шаг = 5. ΔΔ = [0, 0, 0]. 0 бит на ΔΔ — идеальный случай. Base=1000, delta=5, ΔΔ все нули.

Ключевое отличие ORC от Parquet: ORC не пробует dictionary и не откатывает. Writer анализирует статистики и принимает решение до записи stripe. Нет wasted CPU на dictionary insertion для high-cardinality данных.

DuckDB: Analyze → Try → Measure → Commit

DuckDB использует самый продвинутый подход — sample-based analysis per segment:

DuckDB Analyzer: pipeline выбора encoding

Segment buffer: ~122K values в памяти

Данные буферизируются в Vector (2048 values) → Segment (~122K values). При flush сегмента — запускается Analyzer. Данные уже в памяти — можно анализировать без I/O.

Analyze: cardinality, min/max, null_count, runs

Шаг 1: Analyze — быстрый scan данных (или sample). Определяет: cardinality (approximate), min/max, null_count, constant check, run_length distribution. Не вычисляет энтропию — только быстрые O(n) статистики.

Candidate selection: Constant → Dict → RLE → Delta → FOR → FSST

Шаг 2: Candidate selection — на основе статистик определяется список кандидатов. Constant? → Constant encoding (1 value). Low cardinality? → Dictionary. Long runs? → RLE. Sequential? → Delta. Else? → FOR+BitPacking / FSST / Uncompressed.

Estimate size для каждого кандидата

Шаг 3: Try — для каждого кандидата, вычислить предполагаемый размер. Не пробное кодирование — а estimate на основе статистик. Dictionary: dict_size + index_bits × count. FOR: header + bit_width × count.

Выбрать минимальный размер

Шаг 4: Measure — выбрать кандидат с минимальным estimated size. В случае tie — предпочтение decode speed (Constant > Dictionary > RLE > FOR > Delta).

Commit: закодировать + записать сегмент

Шаг 5: Commit — закодировать сегмент выбранной кодировкой и записать. Решение per-segment — разные сегменты одной колонки могут использовать разные кодировки. Row group 0: Dictionary, Row group 1: FSST, Row group 2: Constant.

DuckDB: encoding candidates по типу

DuckDB encoding candidates: type → candidates
Тип данныхЛогический тип колонки в DuckDB
Candidates (в порядке приоритета)Кодировки, которые Analyzer рассматривает. Первый подходящий с минимальным размером побеждает.
Ключевое решениеЧто определяет выбор между кандидатами
INTEGER / BIGINT32/64-bit integers. Самый богатый набор кандидатов.
Constant → Dict → RLE → Delta → FOR+BP → UncompressedConstant: все значения одинаковы. Dict: < threshold unique. RLE: длинные runs. Delta: монотонные. FOR+BitPacking: default. Uncompressed: ничего не помогает.
Кардинальность vs run length vs rangeLow card → Dict. Long runs → RLE. Narrow range → FOR. Sequential → Delta. Analyzer выбирает по estimated size.
VARCHARСтроки переменной длины.
Constant → Dict → FSST → UncompressedConstant: одна строка. Dict: < threshold unique. FSST: high cardinality long strings. Uncompressed: short random strings.
FSST выигрывает при high card + long stringsFSST: ~3x на длинных строках (URL, JSON, logs). Для коротких строк (3–5 символов) FSST overhead > savings → Uncompressed.
BOOLEANTrue/false values.
Constant → RLE → UncompressedConstant: all true/all false. RLE: skewed distribution. Uncompressed: random 50/50.
Constant (0 бит) vs RLE vs bitmapIf all same → Constant. If runs → RLE. If random → bitmap (1 bit/value).
FLOAT / DOUBLEIEEE 754 floating-point.
Constant → Dict → Patas → ALP → UncompressedALP: Adaptive Lossless Float-Point (integrated in DuckDB). Decimal floats → integer → FOR encoding. High-precision → left-part dictionary. Подробно в Модуле 09.
ALP для decimal floats (10.12 → 1012)ALP определяет, можно ли float представить как integer × 10^(-n). Если да — integer encoding. Если нет — left-part dictionary.
TIMESTAMPMicrosecond timestamps.
Constant → Dict → Delta → FOR+BP → UncompressedTimestamps обычно sequential → Delta с минимальными дельтами. Regular interval → Delta с ΔΔ ≈ 0. Irregular → FOR.
Delta почти всегда побеждаетEvent timestamps: дельты в микросекундах = маленькие числа. 1M timestamps с ms-precision: delta ~20 бит/значение vs raw 64 бит.
NOTE

DuckDB принимает решение per-segment (~122K строк), а не per-file или per-stripe. Это означает: если первые 122K строк — department names (10 unique → Dictionary), а следующие 122K — UUID (122K unique → FSST) — DuckDB использует разные кодировки для разных сегментов одной колонки. Ни Parquet, ни ORC этого не умеют.

BtrBlocks: Sampling Cascade

BtrBlocks (SIGMOD 2023) использует самый агрессивный подход — sampling + cascade evaluation. Вместо одной кодировки — цепочка кодировок, где выход одной становится входом следующей:

BtrBlocks: sampling-based cascade evaluation

Блок данных (~65K values)

BtrBlocks получает блок данных (обычно ~65K values). Вместо полного analysis — берёт sample (~1-5% значений, random).

Sample 1–5%: cardinality, distribution, patterns

Шаг 1: Sample — выбрать случайные 1-5% значений. Вычислить быстрые статистики на sample: cardinality estimate, value distribution, run pattern. O(sample_size) вместо O(n).

Evaluate на sample: 8 кандидатов из пула

Шаг 2: Evaluate pool — для каждой кодировки из пула (8 кандидатов) оценить compression ratio на sample. Пул: Uncompressed, OneValue, Dictionary, RLE, Frequency, Pseudodecimal, FOR, FSST. Каждый кандидат — O(sample_size) evaluation.

Cascade: best₁ → encode → evaluate best₂ → …

Шаг 3: Cascade — лучший первый кандидат применяется, его выход подаётся обратно в пул для второго уровня. Пример: Dictionary → FOR на индексах → BitPacking. Cascade останавливается, когда дальнейшее улучшение < threshold.

Commit: encoding chain (e.g. Dict → FOR → BP)

Результат: цепочка кодировок, применённых последовательно. Decode — в обратном порядке. Пример: FOR(Dictionary(data)) → decode: un-FOR → un-Dictionary. Metadata хранит цепочку.

BtrBlocks encoding pool

BtrBlocks: 8 кодировок в пуле
EncodingКодировка из пула BtrBlocks
Что делаетТрансформация данных
Cascade roleРоль в cascade: first (применяется к raw данным), inner (на выходе первой), terminal (финальная)
Best forДанные, для которых кодировка оптимальна
OneValueВсе значения одинаковы → 1 value + count. Аналог DuckDB Constant. Наивысший приоритет — если подходит, ничего лучше нет.
1 value + countОдно значение + количество повторений. 0 бит на данные. Только metadata.
TerminalТерминальная: если все значения одинаковы — cascade не нужен.
Constant columnsDefault values, partition columns, status fields где все значения одинаковы.
DictionaryЗамена значений на индексы словаря. Выход: массив integer индексов — можно каскадировать с FOR/RLE.
values → indicesКаждое значение → index в dictionary. Словарь хранится отдельно.
FirstОбычно первый шаг cascade: Dictionary → FOR → BitPacking.
Low cardinality< ~10K unique values. Словарь компактен, индексы — маленькие integers.
RLERun-Length Encoding: серии одинаковых значений → (value, run_length) пары.
runs → (val, len)Серия [A,A,A,B,B] → [(A,3), (B,2)]. Длины — маленькие integers, можно каскадировать.
First / InnerМожет быть первым (на raw данных) или inner (на dictionary indices с runs).
Long runsSorted данные, repeated values, partition columns.
FORFrame-of-Reference: base + offsets. Вычитает минимум, хранит разности. Выход: narrow-range integers.
values → base + offsetsbase = min(block). offsets = values - base. Bit-width = ceil(log₂(max(offsets)+1)).
InnerОбычно inner: Dictionary → FOR (на индексах). Или first: FOR → BitPacking (на raw integers).
Narrow rangeЗначения в узком диапазоне: prices 9990–10050, timestamps с одинаковым часом.
FrequencyУникальная BtrBlocks кодировка: выделяет TOP-K частых значений в отдельный encoding, остальные — exception list. Гибрид dictionary + exception.
top-K → encoded + exceptionsTOP-K значений (по частоте) кодируются компактно. Остальные — в exception list (sparse). Для данных с power-law distribution.
FirstПервый шаг: выделить частые, остальные отложить. Исключения каскадируются отдельно.
Power-law distributionДанные с zipf-like распределением: 10 значений = 90% данных, остальные = 10% хвост.
PseudodecimalУникальная BtrBlocks кодировка для doubles, которые на самом деле decimal. $3.25 (double 3.25) → integer 325. Работает для monetary data, sensor data с фиксированной точностью.
double → integer × 10^(-n)Определяет: можно ли double точно представить как integer × 10^(-n). Если да — хранить как integer (FOR/Dict). Предшественник ALP.
FirstПервый шаг для doubles. Выход — integers, которые каскадируются с FOR/Dictionary.
Monetary / sensor$3.25, $10.99, 20.5°C — doubles с 1-2 десятичными знаками. Значительная доля real-world float колонок.
FSSTFast Static Symbol Table — string compression. Заменяет частые подстроки (1-8 байт) на 1-byte символы. Подробно в Модуле 09.
strings → compressed bytesСтроит таблицу 256 символов из training data. Каждая строка: замена частых подстрок на 1-byte коды. ~3x compression.
Terminal (strings)Терминальная для строк: выход — compressed bytes, дальнейшее каскадирование не выгодно.
High card stringsURL, JSON, log messages — high cardinality, long strings. Заполняет gap, где dictionary не работает.
UncompressedНет кодирования. Raw bytes. Baseline для cascade evaluation — если ничего не даёт выигрыша.
passthroughДанные записываются как есть. Overhead: только metadata (block header).
FallbackЕсли ни один кандидат не сжимает лучше raw — Uncompressed. Random binary data, encrypted columns.
Random / encryptedДанные с максимальной энтропией: random bytes, encrypted columns, hashes.

Manual Override: когда переопределять

Иногда автоматический выбор неоптимален. API для ручного управления:

Manual encoding override: API по форматам
PyArrow (Parquet)PyArrow позволяет задать encoding per-column через column_encoding dict при записи Parquet. Ключ — имя колонки, значение — строка encoding.
ORC WriterOptions (Java)ORC Java writer: dictionary fallback threshold настраивается через OrcConf.DICTIONARY_KEY_SIZE_THRESHOLD (default 0.8). Также можно отключить dictionary per-column.
DuckDB PRAGMADuckDB позволяет контролировать compression через PRAGMA force_compression. Применяется глобально, не per-column. Полезно для тестирования.
Spark (Parquet)Spark позволяет задать parquet.enable.dictionary per-column через Hadoop configuration. Также: parquet.writer.version для v1/v2 encoding support.

Когда manual override оправдан

Правила ручного переопределения encoding
СитуацияКогда автоматический выбор неоптимален
OverrideРекомендуемое ручное переопределение
ВыигрышЧто даёт override по сравнению с auto
UUID/hash колонка в ParquetParquet writer тратит CPU на dictionary insertion для каждого уникального UUID, потом откатывает на PLAIN. Wasted work.
column_encoding='PLAIN'Пропустить dictionary probe. Сразу PLAIN. Экономия CPU при записи, тот же результат.
−30% write CPUНет hash map insertions для 1M+ unique strings. Нет rollback при overflow.
Sorted timestamps в ParquetParquet по умолчанию пробует dictionary для INT64 timestamps. Sorted timestamps с unique values → dictionary overflow → fallback на DELTA.
column_encoding='DELTA_BINARY_PACKED'Сразу DELTA. Пропуск dictionary probe. Для sorted timestamps — optimal encoding.
−20% write CPUПропуск dictionary probe для timestamps (обычно unique).
Float sensor dataParquet по умолчанию: dict → overflow → PLAIN для floats. BYTE_STREAM_SPLIT значительно лучше для narrow-range sensor data.
column_encoding='BYTE_STREAM_SPLIT'Split IEEE 754 bytes → stream per position → compress. Exponent bytes одинаковы для narrow-range data.
2–3x лучше сжатиеPLAIN: 4/8 bytes per float. BYTE_STREAM_SPLIT + Zstd: ~1-2 bytes per float для narrow-range.
High-card strings в ORCORC dictionary threshold = 0.8 по умолчанию. Некоторые колонки имеют 60-70% unique → dictionary выбирается, но неэффективен (большой словарь).
dictionary.threshold=0.4Снизить порог: если > 40% unique → direct. Раньше переключиться на direct для medium-cardinality.
−20% file sizeДля medium-cardinality (1K–100K unique in stripe): direct encoding + compression может быть компактнее dictionary + compressed indices.

Сводная таблица: алгоритмы выбора

Четыре подхода к encoding selection
Аспект алгоритма выбора encoding
ParquetParquet: type-based default + dictionary probe + fallback
ORCORC: stripe-level statistics + threshold-based
DuckDBDuckDB: sample-based analyze → candidates → estimate → commit
BtrBlocksBtrBlocks: sampling cascade with pool of 8 encodings
Анализ данныхКогда и как данные анализируются
Нет (probe)Parquet не анализирует: начинает dictionary, откатывает при overflow. Нет pre-scan.
Per-stripe statsСтатистики собираются во время буферизации stripe. Решение при flush.
Per-segment analysisПолный scan или sample ~122K значений. Estimate size для каждого кандидата.
1–5% sampleБыстрый random sample. Evaluate каждого кандидата на sample. Cascade evaluation.
GranularityНа каком уровне принимается решение
Per-column-chunkВсе pages в column chunk используют одну кодировку. Атомарный fallback.
Per-stripeРешение per-stripe (~256 MB). Разные stripes — разные кодировки.
Per-segmentРешение per-segment (~122K rows). Самая мелкая гранулярность.
Per-blockРешение per-block (~65K values). Cascade chain per-block.
CascadeМожет ли одна кодировка быть входом для другой
НетParquet: одна кодировка per column. Dict → indices → RLE/BP — но это один encoding, не cascade.
НетORC: одна кодировка. RLEv2 с подкодировками — но не cascade.
НетDuckDB: одна кодировка per segment. Нет cascade.
Да (2–3 уровня)BtrBlocks: Dictionary → FOR → BitPacking. 2–3 уровня cascade. Уникальная возможность.
Pool sizeКоличество доступных кодировок
6–8PLAIN, RLE, RLE_DICTIONARY, DELTA_BINARY_PACKED, DELTA_LENGTH_BYTE_ARRAY, DELTA_BYTE_ARRAY, BYTE_STREAM_SPLIT.
2 × 4Dictionary или Direct × RLEv2 с 4 подкодировками. Фактически 2 стратегии с 4 вариантами.
~8Constant, Dictionary, RLE, Delta, FOR, BitPacking, FSST, ALP, Uncompressed.
8Uncompressed, OneValue, Dictionary, RLE, Frequency, Pseudodecimal, FOR, FSST.
Wasted workCPU потраченный впустую при неудачном выборе
Dict probe → rollbackWorst case: dictionary probe для 1M unique strings → rollback → re-encode as PLAIN. Дублирование CPU.
МинимальныйStats собираются inline. Решение до encoding — нет rollback.
Sample overheadОдин scan для analysis + один для encoding. Overhead: ~10-20% extra CPU на analysis.
Sample + multi-evalSample extraction + evaluation каждого из 8 кандидатов + cascade. Но sample маленький (1-5%), так что абсолютный overhead низкий.

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

  1. Parquet: probe-and-fallback — начинает с dictionary, откатывает при overflow. Простой, но тратит CPU на заведомо бесполезный dictionary для high-cardinality колонок. Manual override (column_encoding) экономит CPU.
  2. ORC: statistics-driven — собирает stats во время буферизации, решает при stripe flush. Нет wasted work, но гранулярность = stripe (~256 MB) — крупная.
  3. DuckDB: analyze-and-pick — per-segment analysis с candidate evaluation. Самая мелкая гранулярность (122K rows), самый богатый набор кандидатов (включая FSST и ALP). Разные сегменты одной колонки могут использовать разные кодировки.
  4. BtrBlocks: sampling cascade — единственный подход с цепочками кодировок. Dictionary → FOR → BitPacking даёт лучшую compression ratio, чем любая одиночная кодировка. Sampling (1–5%) делает evaluation быстрым.
  5. Manual override оправдан когда: (a) данные заведомо high-cardinality (UUID → skip dictionary probe), (b) тип данных указывает на оптимальную кодировку (sorted timestamps → DELTA), (c) benchmarks показывают выигрыш конкретной кодировки для вашего workload.
  6. Тренд: от детерминистических правил к data-driven selection. Parquet (2012) → ORC (2016) → DuckDB (2020) → BtrBlocks (2023): каждое поколение делает более информированный выбор на основе анализа данных.

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

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

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

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