Learning Platform
Глоссарий Troubleshooting
Урок 04.04 · 30 мин
Продвинутый
ORCType SystemHive TypesType TreeColumn StatisticsStream Layout

Система типов

Hive Type Tree

ORC хранит схему данных как дерево типов (type tree) — направленное дерево, где каждый узел описывает один тип. Корень дерева — всегда STRUCT, представляющий строку таблицы. Дочерние узлы — колонки.

Каждый тип в дереве получает column ID — целое число, присвоенное при обходе дерева в pre-order (корень → первый потомок → его потомки → второй потомок → …). Column ID определяет порядок потоков и статистик в файле.

Type Tree: pre-order нумерация

ID 0: STRUCT (корень — строка таблицы)

Корень type tree — всегда STRUCT. Column ID = 0. Представляет всю строку таблицы. Потомки = колонки таблицы. Footer.types[0] = STRUCT.

ID 1: INT (id)

Первая колонка. Column ID = 1 (pre-order после корня). Примитивный тип — нет потомков. Stream: DATA (RLEv2).

ID 2: STRING (name)

Column ID = 2. STRING — примитивный тип. Streams: DATA (UTF-8 или dictionary indices), LENGTH, [DICTIONARY_DATA].

ID 3: MAP (tags)

Column ID = 3. MAP — комплексный тип с двумя потомками (key и value). Сам MAP порождает LENGTH stream (число пар в каждой строке).

ID 4: STRING (key)

Ключ map. Column ID = 4. STRING — примитивный тип. Pre-order: после MAP (3) идёт его первый потомок.

ID 5: INT (value)

Значение map. Column ID = 5. INT — примитивный тип. Pre-order: после первого потомка (4) идёт второй.

ID 6: LIST (scores)

Column ID = 6. LIST — комплексный тип с одним потомком (element). Сам LIST порождает LENGTH stream (число элементов в каждой строке).

ID 7: DOUBLE (element)

Элемент списка. Column ID = 7. DOUBLE — примитивный тип. Pre-order: потомок LIST идёт сразу после LIST.
NOTE

Pre-order обход критичен для понимания stream layout. Column ID определяет порядок в ColumnStatistics (Footer), Row Index streams и Stripe Footer stream directory. Если у MAP column ID = 3, то его key = 4, value = 5 — всегда. Ридер полагается на эту нумерацию для навигации.

19 примитивных типов

ORC определяет 19 примитивных типов (Type.Kind enum в спецификации). Каждый тип определяет, какие streams порождает колонка и какую кодировку использует:

Примитивные типы ORC

Целочисленные

BOOLEAN1 бит на значение, Byte RLE. 8 boolean упакованы в 1 байт. Stats: trueCount.
BYTE (TINYINT)8-bit signed integer (-128..127). Byte RLE. Один DATA stream.
SHORT (SMALLINT)16-bit signed integer. RLEv1/RLEv2 с zigzag. Один DATA stream.
INT32-bit signed integer. RLEv2 с zigzag encoding. Один DATA stream. Stats: min, max, sum.
LONG (BIGINT)64-bit signed integer. RLEv2 с zigzag. Один DATA stream. Основной числовой тип для больших значений.

С плавающей точкой и decimal

FLOATIEEE 754 single-precision (4 байта). Записывается as-is, без специальной кодировки. Stats: min, max, sum (double).
DOUBLEIEEE 754 double-precision (8 байт). Записывается as-is. В отличие от Parquet, нет BYTE_STREAM_SPLIT.
DECIMALПроизвольная точность. Два потока: DATA (unscaled value, RLEv2 zigzag) + SECONDARY (scale, если переменный). Stats: min, max, sum.

Строки, даты, бинарные

STRINGUTF-8 строки. Dictionary (sorted) или Direct режим. Dictionary: DATA + DICTIONARY_DATA + LENGTH. Direct: DATA + LENGTH.
CHAR / VARCHARФиксированная/переменная длина. Тот же stream layout, что STRING. CHAR дополняется пробелами до maxLength.
DATEДни от epoch (1970-01-01). Один DATA stream, RLEv2 zigzag. Stats: min, max в днях.
TIMESTAMPСекунды + наносекунды от epoch. DATA (секунды, RLEv2) + SECONDARY (наносекунды, RLEv2). Два потока для высокой точности.
TIMESTAMP_INSTANTКак TIMESTAMP, но всегда в UTC. Добавлен в ORC 1.5. Не зависит от timezone сессии — critical для глобальных систем.
BINARYПроизвольные байты. DATA (сырые байты) + LENGTH (длина каждого значения). Без dictionary — binary обычно высокая кардинальность.

4 комплексных типа

Комплексные типы ORC — контейнеры, содержащие вложенные типы. Каждый комплексный тип порождает свои потоки и потомки в type tree:

Комплексные типы ORC
STRUCTГруппа именованных полей. Не порождает собственных DATA/LENGTH потоков — только PRESENT (NULL bitmap). Потомки = поля структуры. Корень type tree — всегда STRUCT.
LIST (ARRAY)Список элементов переменной длины. LENGTH stream: количество элементов в каждой строке (RLEv2). Один потомок — тип элемента.
MAPСловарь ключ-значение. LENGTH stream: количество пар в каждой строке (RLEv2). Два потомка — тип ключа и тип значения. Ключи не обязаны быть уникальными (ORC не проверяет).
UNIONTagged union — каждое значение принадлежит одному из N вариантов. DATA stream: tag (0..N-1), какой вариант активен в каждой строке (Byte RLE). Потомки: N типов-вариантов.
TIP

UNION — уникальный тип ORC, отсутствующий в Parquet. Он реализует tagged union (сумма типов): каждая строка содержит значение одного из N вариантов. Hive использует это для UNIONTYPE. На практике UNION встречается редко — большинство движков предпочитают nullable STRUCT.

Stream layout по типам

Полная карта потоков для каждого типа — ключ к пониманию, как ORC-ридер декодирует данные:

Stream Layout: потоки по типам
ТипORC тип из Type.Kind enum.
PRESENTБитовая маска NULL (Byte RLE). Отсутствует, если колонка NOT NULL.
DATAОсновной поток данных. Кодировка зависит от типа.
Другие потокиLENGTH, SECONDARY, DICTIONARY_DATA — дополнительные потоки для сложных типов.
BOOLEANBOOLEAN: Byte RLE — 8 значений на байт. Простейший layout.
+ (if nullable)Byte RLE битовая маска. Присутствует только если колонка допускает NULL.
Byte RLE8 boolean значений на байт, MSB first. Run-length encoded.
Нет дополнительных потоков.
INT / LONGINT/LONG: один DATA поток с RLEv2. Zigzag для signed.
+Byte RLE
RLEv2 zigzagRLEv2 с zigzag encoding для signed integers. 4 подкодировки выбираются автоматически.
Нет дополнительных потоков.
STRING (dict)STRING со словарём: 4 потока. Самый сложный layout для примитивного типа.
+Byte RLE
RLEv2 (indices)Индексы в словарь. RLEv2 — повторяющиеся индексы отлично сжимаются.
DICT_DATA + LENGTHDICTIONARY_DATA: конкатенация уникальных строк (sorted). LENGTH: длина каждой строки в словаре (RLEv2).
TIMESTAMPTIMESTAMP: два числовых потока для секунд и наносекунд. Высокая точность.
+Byte RLE
RLEv2 (seconds)Секунды от epoch (UTC-adjusted). RLEv2 zigzag.
SECONDARY (nanos)Наносекунды. RLEv2 unsigned. Позволяет наносекундную точность.
MAPMAP: LENGTH stream для количества пар. Key и value — отдельные колонки с собственными потоками.
+Byte RLE
MAP сам не имеет DATA потока — данные в потомках (key, value).
LENGTH (RLEv2)Количество пар ключ-значение в каждой строке. RLEv2 unsigned.

Column Statistics по типам

Statistics — это type-aware: для каждого типа ORC хранит разные метрики. Ридер использует конкретную специализацию для predicate pushdown:

ТипСтатистикиИспользование в SARG
BOOLEANtrueCountОптимизация WHERE flag = true
BYTE / SHORT / INT / LONGmin, max, sumRange predicates, равенство
FLOAT / DOUBLEmin, max, sumRange predicates (осторожно с NaN)
STRING / CHAR / VARCHARmin, max, sum (total length)Lexicographic range, equality
DATEmin, max (days from epoch)Date range predicates
TIMESTAMPmin, max (ms from epoch)Timestamp range predicates
DECIMALmin, max, sumDecimal range predicates
BINARY— (только numberOfValues, hasNull)Только IS NULL
Все типыnumberOfValues, hasNull, bytesOnDiskNULL checks, projection pushdown
WARNING

Статистики для FLOAT/DOUBLE не учитывают NaN в min/max. Если колонка содержит NaN, min/max будут вычислены по non-NaN значениям. Это корректно для IEEE 754, но может сбить с толку при отладке: row group с NaN не будет отсечён по range predicate.

Type Tree в Protobuf

Footer хранит type tree как массив Type messages. Каждый Type содержит:

message Type {
 kind: Kind // BOOLEAN, BYTE, SHORT, INT, ... STRUCT, LIST, MAP, UNION
 subtypes: [uint32] // column IDs потомков (для комплексных типов)
 fieldNames: [string] // имена полей (только для STRUCT)
 maximumLength: uint32 // для CHAR/VARCHAR
 precision: uint32 // для DECIMAL
 scale: uint32 // для DECIMAL
}
Type Tree: Protobuf-представление

Таблица

CREATE TABLE users (
  id INT,
  name STRING,
  scores ARRAY<DOUBLE>
)

Footer.types[]

types[0]Корневой STRUCT. subtypes=[1,2,3] — column IDs трёх колонок. fieldNames=['id','name','scores'].
types[1]Колонка id. INT — примитивный тип, нет subtypes.
types[2]Колонка name. STRING — примитивный тип.
types[3]Колонка scores. LIST с одним потомком — DOUBLE (column ID 4). subtypes=[4].
types[4]Элемент массива scores. DOUBLE — примитивный тип. Column ID = 4 (pre-order после LIST).

ORC vs Parquet: система типов

ХарактеристикаORCParquet
Модель схемыType tree (pre-order)Message tree (Dremel/Thrift)
Количество примитивных типов197 (+ logical types)
DECIMALНативный типLogical type поверх INT32/INT64/FIXED_LEN
DATEНативный типLogical type поверх INT32
TIMESTAMPНативный (секунды + наносекунды)Logical type (INT96 или INT64)
UNION (tagged) Да Нет
Вложенные структурыSTRUCT / LIST / MAPgroup / repeated / map (Dremel)
Rep/Def levels — через PRESENT + LENGTH (Dremel model)
Нумерация колонокPre-order column IDsSchema path / column indices
NOTE

ORC использует нативные типы для DATE, DECIMAL и TIMESTAMP — каждый с собственной кодировкой и статистиками. Parquet реализует те же логические типы как аннотации поверх примитивных типов (INT32, INT64, FIXED_LEN_BYTE_ARRAY). Подход ORC проще для ридера — тип однозначно определяет stream layout. Подход Parquet гибче — новые логические типы добавляются без изменения формата.

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

  1. Type tree хранит схему ORC как дерево с pre-order column IDs — нумерация определяет порядок потоков и статистик
  2. 19 примитивных типов: от BOOLEAN и BYTE до TIMESTAMP_INSTANT и DECIMAL — каждый с уникальным stream layout
  3. 4 комплексных типа: STRUCT (именованные поля), LIST (массив), MAP (словарь), UNION (tagged union — уникально для ORC)
  4. Stream layout определяется типом: INT → один DATA stream, STRING → до 4 streams, TIMESTAMP → DATA + SECONDARY
  5. Column statistics — type-specific: IntegerStatistics для числовых, StringStatistics для строк, BooleanStatistics для boolean
  6. UNION — tagged union, отсутствует в Parquet. Каждая строка содержит значение ровно одного из N вариантов

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

Результат: 0 из 0
Концептуальный
Вопрос 1 из 4. ORC использует дерево типов (type tree). Таблица: id INT, name STRING, address STRUCT<city: STRING, zip: INT>. Сколько column IDs в type tree?

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

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

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

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