Кодировки колонок
Потоковая модель ORC
ORC хранит данные колонок как набор streams (потоков). Каждая колонка порождает от 1 до 5 потоков, в зависимости от типа данных. Это фундаментальное отличие от Parquet, где данные колонки живут в одной data page.
Каждый stream — последовательность байтов с конкретным назначением. Stripe Footer хранит stream directory: для каждого stream — column ID, stream kind и length.
Основные потоки
Потоки по типу колонки
В отличие от 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) литеральных значений.
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). Каждое значение записано как есть.Пример: [42, 7, 99, 3] → header=-4, values=[42, 7, 99, 3]
RLEv2: четыре подкодировки
RLEv2 (Hive 0.12+, формат v1) — основная кодировка для integer-данных в ORC. Она включает четыре подкодировки, каждая оптимальна для своего паттерна данных. Encoder выбирает подкодировку автоматически для каждого run.
Первые два бита каждого заголовка определяют подкодировку:
00— Short Repeat01— Direct10— Patched Base11— Delta
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 (выбросы):
- Вычисляется base value (минимум серии)
- Все значения = value - base (shifted to positive)
- Определяется reduced bit width — ширина для 90-го перцентиля
- Outliers, не помещающиеся в reduced width, записываются как patches
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-представлении:
| Signed | Zigzag | Причина |
|---|---|---|
| 0 | 0 | Нуль остаётся нулём |
| -1 | 1 | Маленькие отрицательные → маленькие положительные |
| 1 | 2 | |
| -2 | 3 | |
| 2 | 4 | |
| -3 | 5 | Паттерн: 0, -1, 1, -2, 2, -3, … |
Формула: zigzag(n) = (n << 1) ^ (n >> 63) для int64.
Zigzag — та же идея, что в Protocol Buffers. В стандартном varint число -1 (int64) занимает 10 байт (все биты = 1). После zigzag: -1 → 1, что занимает 1 байт. Для данных с дельтами близкими к нулю (±5, ±10) это колоссальная экономия.
Dictionary Encoding
Для строковых колонок с низкой кардинальностью ORC использует dictionary encoding:
- DICTIONARY_DATA stream — отсортированный список уникальных строк
- LENGTH stream — длина каждой строки в словаре
- DATA stream — индексы в словарь (RLEv2-encoded)
Исходные данные
После dictionary encoding
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
| Характеристика | ORC | Parquet |
|---|---|---|
| Integer encoding | RLEv1/RLEv2 (4 подкодировки) | RLE, DELTA_BINARY_PACKED |
| String encoding | Dictionary (sorted) / Direct | RLE_DICTIONARY (insertion order) |
| Boolean encoding | Byte RLE (8 bit per byte) | RLE/Bit-Packing Hybrid |
| Outlier handling | Patched Base (RLEv2) | аналога |
| Signed integers | Zigzag + RLEv2 | Zigzag + DELTA_BINARY_PACKED |
| Float/Double | IEEE 754 as-is | BYTE_STREAM_SPLIT |
| Кодировка метаданных | Протокол автоматический | Указывается в metadata |
Ключевые выводы
- ORC хранит данные как streams — каждая колонка порождает 1–5 потоков (PRESENT, DATA, LENGTH, DICTIONARY_DATA, SECONDARY)
- RLEv2 — основная кодировка с 4 подкодировками: Short Repeat, Direct, Patched Base, Delta
- Patched Base — уникальная подкодировка для данных с outliers, не имеет аналога в Parquet
- Zigzag encoding превращает маленькие отрицательные числа в маленькие положительные для эффективного varint
- Dictionary encoding в ORC сортирует словарь лексикографически (Parquet — в порядке появления)
- Byte RLE кодирует NULL bitmap (PRESENT stream) — 8 boolean на байт