Learning Platform
Глоссарий Troubleshooting
Урок 04.02 · 35 мин
Продвинутый
ORCRLEv1RLEv2Short RepeatDirectPatched BaseDeltaZigzagDictionary Encoding

Кодировки колонок

Потоковая модель ORC

ORC хранит данные колонок как набор streams (потоков). Каждая колонка порождает от 1 до 5 потоков, в зависимости от типа данных. Это фундаментальное отличие от Parquet, где данные колонки живут в одной data page.

Каждый stream — последовательность байтов с конкретным назначением. Stripe Footer хранит stream directory: для каждого stream — column ID, stream kind и length.

Типы потоков (streams) в ORC

Основные потоки

PRESENTBit stream: 1 = значение есть, 0 = NULL. Кодируется Byte RLE. Отсутствует, если колонка NOT NULL (все значения present).
DATAОсновной поток данных. Для integer — RLEv1/RLEv2. Для string без словаря — UTF-8 байты подряд. Для string со словарём — индексы в словарь (RLEv2).
LENGTHДлины для типов переменной длины: string (без словаря), binary, list (число элементов), map (число пар). RLEv2-encoded.
DICTIONARY_DATAСловарь уникальных строковых значений. Хранится как конкатенация UTF-8 строк. Длины — в отдельном потоке. Сортирован лексикографически.
SECONDARYДополнительный поток для типов с двумя компонентами: наносекунды для timestamp, длины ключей для map. RLEv2-encoded.

Потоки по типу колонки

INTEGER / LONGPRESENT (если nullable) + DATA (RLEv2 с zigzag для signed). Два потока максимум. Самый компактный layout.
STRING (low cardinality)Словарный режим: PRESENT + DATA (индексы в словарь, RLEv2) + DICTIONARY_DATA (уникальные строки) + LENGTH (длины строк в словаре).
STRING (high cardinality)Прямой режим: PRESENT + DATA (UTF-8 байты) + LENGTH (длина каждой строки). Без словаря — если уникальных значений слишком много.
TIMESTAMPPRESENT + DATA (секунды от epoch, RLEv2) + SECONDARY (наносекунды, RLEv2). Два числовых потока для высокой точности.
NOTE

В отличие от Parquet, ORC не хранит repetition/definition levels. Вложенные структуры кодируются через потоки LENGTH для list/map и PRESENT для nullable-элементов. Это проще, чем Dremel-модель Parquet, но менее гибко для глубоко вложенных структур.

RLEv1: базовая кодировка

RLEv1 — оригинальная кодировка ORC (Hive 0.11, формат v0). Два подрежима:

Run (повторение): header byte ≥ 0, значение = header + 3 повторений одного значения или дельты.

Literal (литералы): header byte < 0, значение = abs(header) литеральных значений.

RLEv1: Run и Literal режимы

Run (повторение / дельта)

Header (0–127) → count = header + 3

Первый байт run: 0–127 → количество повторений = value + 3 (от 3 до 130). Два режима: constant (дельта=0) и delta (фиксированный шаг).

Delta (zigzag-encoded)

Zigzag-encoded delta между значениями. 0 = все значения одинаковы. Любое другое — фиксированный шаг: base, base+delta, base+2*delta, ...

Base value

Первое значение последовательности (base-encoded). Остальные вычисляются: value[i] = base + i * delta.

Пример: [100, 100, 100, 100, 100] → header=2 (5 повторений), delta=0, base=100

Literal (без паттерна)

Header (-128 to -1) → count = |header|

Отрицательный header: -1 до -128 → количество литералов = abs(header) (от 1 до 128). Каждое значение записано как есть.
Values[0], Values[1], ..., Values[N-1]Значения записаны по одному, base-128 varint encoded. Никакой компрессии — raw values. Используется, когда данные хаотичны.

Пример: [42, 7, 99, 3] → header=-4, values=[42, 7, 99, 3]

RLEv2: четыре подкодировки

RLEv2 (Hive 0.12+, формат v1) — основная кодировка для integer-данных в ORC. Она включает четыре подкодировки, каждая оптимальна для своего паттерна данных. Encoder выбирает подкодировку автоматически для каждого run.

Первые два бита каждого заголовка определяют подкодировку:

  • 00 — Short Repeat
  • 01 — Direct
  • 10 — Patched Base
  • 11 — Delta
RLEv2: четыре подкодировки
00: Short RepeatОдно значение, повторённое 3–10 раз. 1 байт header + 1–8 байт значения. Идеально для коротких серий одинаковых значений (NULL-count, enum codes).
01: DirectBit-packed значения с фиксированной шириной (1–64 бита). Header задаёт bit width и count. Для хаотичных данных без повторений.
10: Patched BaseДля данных с outliers: base value + bit-packed deltas + patches для исключений. Основная масса значений кодируется узкой шириной, выбросы — отдельными патчами.
11: DeltaBase value + base delta + дельты дельт (second-order). Идеально для монотонных последовательностей (timestamps, auto-increment IDs). Дельты кодируются bit-packed.

Short Repeat (00)

Самая простая подкодировка: одно значение, повторённое 3–10 раз.

Формат: [header: 1 byte] [value: 1-8 bytes]

  • Биты 7–6: 00 (идентификатор подкодировки)
  • Биты 5–3: ширина значения в байтах - 1 (0–7 → 1–8 байт)
  • Биты 2–0: количество повторений - 3 (0–7 → 3–10)

Используется для: коротких серий одинаковых значений, часто встречающихся в enum-колонках, boolean-счётчиках, дефолтных значениях.

Direct (01)

Bit-packed значения с вычисленной фиксированной шириной. Encoder определяет максимальное значение в run, вычисляет минимальное количество бит для его представления и пакует все значения с этой шириной.

Формат: [header: 2 bytes] [values: bit-packed]

  • Биты 15–14: 01
  • Биты 13–9: encoded bit width (W)
  • Биты 8–0: length - 1 (0–511 → 1–512 значений)

Patched Base (10)

Самая сложная подкодировка. Для данных, где большинство значений близки, но есть outliers (выбросы):

  1. Вычисляется base value (минимум серии)
  2. Все значения = value - base (shifted to positive)
  3. Определяется reduced bit width — ширина для 90-го перцентиля
  4. Outliers, не помещающиеся в reduced width, записываются как patches
Patched Base: обработка outliers
Данные: [100, 102, 101, 103, 999, 100]Исходная последовательность с выбросом. Значение 999 — outlier, остальные в узком диапазоне 100–103.
Base = 100 → [0, 2, 1, 3, 899, 0]Шаг 1: вычитаем минимум (base = 100). Получаем набор неотрицательных дельт от base.
Основной массив (2-bit): [0, 2, 1, 3, 0*, 0]Значения 0–3 помещаются в 2 бита. 899 — outlier, не помещается. В основном массиве на его месте записывается 0 (маска), а реальное значение уходит в patch list.
Patches: [(pos=4, value=899)]Список патчей: позиция outlier в массиве + его полное значение. Патчи кодируются отдельно с более широкой bit width.

Delta (11)

Для монотонных или quasi-монотонных последовательностей: timestamps, auto-increment IDs, sorted columns.

Кодирует second-order deltas: дельты между дельтами. Для строго монотонной последовательности (1, 2, 3, 4, …) все second-order deltas = 0, что даёт максимальную компрессию.

Данные: [10, 13, 16, 19, 22, 25]
Дельты (Δ): [3, 3, 3, 3, 3]
Дельты дельт: [0, 0, 0, 0] → все нули → минимальный bit width

Zigzag-кодирование

ORC использует zigzag-кодирование для signed integer, чтобы маленькие отрицательные числа не занимали максимальное количество байт в varint-представлении:

SignedZigzagПричина
00Нуль остаётся нулём
-11Маленькие отрицательные → маленькие положительные
12
-23
24
-35Паттерн: 0, -1, 1, -2, 2, -3, …

Формула: zigzag(n) = (n << 1) ^ (n >> 63) для int64.

TIP

Zigzag — та же идея, что в Protocol Buffers. В стандартном varint число -1 (int64) занимает 10 байт (все биты = 1). После zigzag: -1 → 1, что занимает 1 байт. Для данных с дельтами близкими к нулю (±5, ±10) это колоссальная экономия.

Dictionary Encoding

Для строковых колонок с низкой кардинальностью ORC использует dictionary encoding:

  1. DICTIONARY_DATA stream — отсортированный список уникальных строк
  2. LENGTH stream — длина каждой строки в словаре
  3. DATA stream — индексы в словарь (RLEv2-encoded)
Dictionary Encoding в ORC

Исходные данные

STRING column7 строковых значений с 3 уникальными. Повторения — идеальный кандидат для словарного кодирования.

После dictionary encoding

DICTIONARY_DATAУникальные значения, отсортированные лексикографически. Сортировка улучшает prefix-compression и range predicate pushdown.
LENGTHДлина каждой строки в словаре в байтах: apple=5, banana=6, cherry=6. RLEv2-encoded.
DATA (indices)Индексы в словарь вместо строк. 0=apple, 1=banana, 2=cherry. RLEv2-encoded — повторяющиеся индексы отлично сжимаются.
WARNING

ORC сортирует словарь лексикографически. Это отличие от Parquet, где словарь хранит значения в порядке появления. Сортированный словарь позволяет ORC выполнять range-predicates (WHERE name BETWEEN 'a' AND 'c') прямо по словарю, без сканирования данных.

Byte RLE и Boolean RLE

Byte RLE — простейшая кодировка для потоков PRESENT (NULL bitmap):

  • Run: header byte ≥ 0 → value + 3 повторений одного байта
  • Literal: header byte < 0 → abs(header) литеральных байтов

Boolean RLE — Byte RLE, где каждый байт содержит 8 бит (8 boolean значений). Биты идут от MSB к LSB.

Выбор кодировки: ORC vs Parquet

ХарактеристикаORCParquet
Integer encodingRLEv1/RLEv2 (4 подкодировки)RLE, DELTA_BINARY_PACKED
String encodingDictionary (sorted) / DirectRLE_DICTIONARY (insertion order)
Boolean encodingByte RLE (8 bit per byte)RLE/Bit-Packing Hybrid
Outlier handlingPatched Base (RLEv2) аналога
Signed integersZigzag + RLEv2Zigzag + DELTA_BINARY_PACKED
Float/DoubleIEEE 754 as-isBYTE_STREAM_SPLIT
Кодировка метаданныхПротокол автоматическийУказывается в metadata

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

  1. ORC хранит данные как streams — каждая колонка порождает 1–5 потоков (PRESENT, DATA, LENGTH, DICTIONARY_DATA, SECONDARY)
  2. RLEv2 — основная кодировка с 4 подкодировками: Short Repeat, Direct, Patched Base, Delta
  3. Patched Base — уникальная подкодировка для данных с outliers, не имеет аналога в Parquet
  4. Zigzag encoding превращает маленькие отрицательные числа в маленькие положительные для эффективного varint
  5. Dictionary encoding в ORC сортирует словарь лексикографически (Parquet — в порядке появления)
  6. Byte RLE кодирует NULL bitmap (PRESENT stream) — 8 boolean на байт

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

Результат: 0 из 0
Концептуальный
Вопрос 1 из 4. Колонка status (8 уникальных строк) в ORC. Какие streams создаёт writer?

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

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

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

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