Learning Platform
Глоссарий Troubleshooting
Урок 08.02 · 35 мин
Продвинутый
Apache ArrowType SystemNested TypesDictionary EncodingExtension TypesListStructUnion

Type System

Обзор системы типов

Arrow определяет каноническую систему типов, общую для всех реализаций. Любая Arrow-библиотека (C++, Rust, Java, Python, Go) обязана поддерживать один и тот же набор типов с одинаковой семантикой — иначе zero-copy обмен невозможен.

Типы делятся на три категории:

  • Примитивные — фиксированная ширина, один data buffer
  • Переменной ширины — offsets + data buffers
  • Вложенные — комбинация буферов дочерних массивов
Иерархия типов Apache Arrow
Примитивные (Fixed-Width)Каждый элемент — фиксированное число байт. Один data buffer + validity bitmap. O(1) доступ через base + i × size.
Переменной ширины (Variable-Width)Offsets buffer (N+1 элементов) + data buffer. Два варианта: 32-bit offsets (до 2 GB) и 64-bit (LargeUtf8/LargeBinary — до 2^63 байт).
Вложенные (Nested)Содержат дочерние массивы (child arrays). Собственных данных нет — только структурные буферы (offsets, type_ids, validity).

Примитивные типы: буферная раскладка

Все примитивные типы имеют одинаковую структуру: validity bitmap + data buffer.

ТипБайт на элементДиапазонТипичное использование
Int8 / UInt81-128..127 / 0..255Флаги, статусы
Int32 / UInt324±2.1B / 0..4.3BID, количества
Int64 / UInt648±9.2E18 / 0..1.8E19Timestamps, large IDs
Float324±3.4E38Координаты, ML features
Float648±1.8E308Финансы, научные вычисления
Decimal1281638 значащих цифрДенежные суммы (точная арифметика)
FixedSizeBinary(N)NN байтUUID (16B), IP-адреса, хэши
FixedSizeBinary(16): буферы UUID-массива
Массив UUID3 элемента FixedSizeBinary(16). Каждый UUID — 16 байт в raw binary. Элемент 1 — NULL.
Buffer 0: Validity1 байт: 0b00000101 = 0x05. Бит 1 = 0 → элемент 1 NULL.
Buffer 1: Data (16 × 3 = 48B)Непрерывный блок: 48 байт данных. Элемент i по адресу base + i × 16. Байты на позиции NULL — undefined.

Строки: Utf8, LargeUtf8, Utf8View

Arrow имеет три строковых типа с разными трейд-оффами:

Utf8 — классический тройной буфер (validity + offsets Int32 + data). Ограничение: суммарный объём данных до 2 GB (Int32 offsets).

LargeUtf8 — то же, но offsets — Int64. Нет ограничения в 2 GB, но каждый offset занимает 8 байт вместо 4.

Utf8View — альтернативная раскладка без offsets buffer (формат spec Arrow IPC 1.4+, доступно начиная с библиотеки Arrow 15.0; в библиотеках Arrow 23/24, выпущенных в январе и апреле 2026, оптимизировано далее). Каждый элемент описывается 16-байтовой структурой:

  • Первые 4 байта: длина строки
  • Если длина ≤ 12: строка inline (остальные 12 байт)
  • Если длина > 12: первые 4 символа + buffer_index + offset в буфере
Utf8View: inline vs reference
Короткая строка (≤12 байт)Длина 3 байта: строка 'cat' помещается inline. Нет pointer chasing — данные прямо в view структуре.
Длинная строка (>12 байт)Длина 24 байта: строка не помещается inline. Хранится prefix + ссылка на внешний буфер.
TIP

Utf8View даёт ускорение на фильтрации строк: 4-байтовый prefix сравнивается до обращения к основному буферу. Если prefix не совпадает — строка отклоняется без pointer chasing. DataFusion 38+ использует Utf8View по умолчанию.

Temporal Types

Arrow различает несколько временных типов — каждый с конкретной семантикой:

ТипХранениеСемантика
Date32Int32Дни с Unix epoch (1970-01-01)
Date64Int64Миллисекунды с Unix epoch
Time32(unit)Int32Время дня (секунды или миллисекунды)
Time64(unit)Int64Время дня (микро- или наносекунды)
Timestamp(unit, tz)Int64Момент времени с точностью до unit, опциональная timezone
Duration(unit)Int64Разница между двумя моментами
Interval(MonthDayNano)16 bytesКалендарный интервал: месяцы + дни + наносекунды
Timestamp: физическое хранение
Timestamp(Microsecond, 'UTC')Момент времени с точностью до микросекунды. Timezone — метаданные (не влияет на хранимое значение), но определяет семантику отображения.
Хранение: Int64Значение: 1710498600000000 микросекунд с 1970-01-01T00:00:00Z. Занимает 8 байт. Little-endian.
Метаданные типаUnit (MICROSECOND) и timezone ('UTC') хранятся в Schema → Field → Type, а не в data buffer. Разные timezone — разные типы.
WARNING

Timestamp без timezone и Timestamp с timezone — разные типы. Arrow не конвертирует между ними неявно. Timestamp(μs, None) — локальное время без привязки к часовому поясу. Timestamp(μs, “UTC”) — конкретный момент на шкале UTC. Если вы конкатенируете массивы с разными timezone — получите ошибку типов.

Вложенные типы: List

List — массив списков переменной длины. Содержит:

  1. Validity bitmap — NULL для целого списка
  2. Offsets buffer (Int32) — N + 1 смещений в дочерний массив
  3. Child array — единый массив всех элементов всех списков
List<Int32>: раскладка вложенного типа
Логическое представление3 списка. Второй — NULL. Первый содержит [10, 20, 30], третий — [40, 50].
Validity0b00000101 = 0x05. Бит 1 = 0 → список 1 — NULL (весь список, а не элемент).
Offsets (Int32 × 4)4 значения (N+1=4). offsets[0]=0, offsets[1]=3 (список 0 = child[0..3]), offsets[2]=3 (NULL — длина 0), offsets[3]=5 (список 2 = child[3..5]).
Child: Int32 Array (5 элементов)Все элементы всех списков в одном плоском массиве: [10, 20, 30, 40, 50]. Свой validity bitmap (все valid).

Доступ к элементам списка i: child[offsets[i] .. offsets[i+1]]. Рекурсивно — child array может быть любым Arrow-типом, включая другой List.

Вложенные типы: Struct

Struct — аналог строки таблицы или JSON-объекта. Все поля одного Struct имеют одинаковое количество элементов.

Struct не имеет собственного data buffer — только validity bitmap и дочерние массивы по одному на каждое поле.

Struct: раскладка полей
Struct<name: Utf8, age: Int32>2 поля: name (строка) и age (целое). 3 элемента. Элемент 1 — NULL (весь struct NULL).
Struct Validity0b00000101 = 0x05. Бит 1 = 0 → struct[1] NULL. Дочерние массивы на позиции 1 содержат undefined данные.
Child 0: name (Utf8)Полноценный Utf8 массив из 3 элементов. offsets: [0, 5, 5, 8]. data: 'AliceBob'. Элемент 1 — undefined (struct NULL).
Child 1: age (Int32)Int32 массив из 3 элементов: [30, ??, 25]. Элемент 1 — undefined (struct NULL).
NOTE

Struct NULL и field NULL — разные вещи. Если struct[1] = NULL, то child arrays на позиции 1 могут содержать любые значения — они игнорируются. Но если struct valid, а name[1] = NULL — это NULL в конкретном поле.

Map, Union, RunEndEncoded

Map — синтаксический сахар над List<Struct<key, value>>. Ключи не дедуплицируются — Map в Arrow это ordered multimap.

DenseUnion — tagged union. Каждый элемент хранит type_id (какой тип) + offset (позиция в дочернем массиве этого типа). Нет validity bitmap — NULL моделируется через Null-тип.

SparseUnion — как DenseUnion, но без offsets. Все дочерние массивы одной длины, большинство слотов unused. Быстрее для чтения (нет offset lookup), но расход памяти × количество типов.

RunEndEncoded — run-length encoding (формат spec Arrow IPC 1.3+, доступно начиная с библиотеки Arrow 12.0). Два дочерних массива: run_ends (Int16/32/64) + values. Если колонка содержит [A, A, A, B, B, C, C, C, C], хранится run_ends=[3, 5, 9] + values=[A, B, C]. Эффективно для колонок с длинными повторами (статусы, категории).

Dictionary Encoding

Dictionary — не отдельный тип, а обёртка над любым типом. Состоит из:

  1. Dictionary — массив уникальных значений (Utf8, Int32, etc.)
  2. Indices — массив целых чисел (Int8/16/32), указывающих на позиции в dictionary
Dictionary<Int8, Utf8>: кодирование категорий
Логическое представление6 строковых значений с повторами. 3 уникальных значения: 'active', 'pending', 'closed'.
Dictionary (Utf8, 3 элемента)Уникальные значения. Порядок произвольный (зависит от порядка вставки). Каждое значение хранится ровно один раз.
Indices (Int8, 6 элементов)Индексы в dictionary. Каждый — 1 байт (Int8 хватает для до 128 уникальных значений). Вместо 6 строк — 6 байт.

Выигрыш: вместо хранения строк по 6–7 байт × 6 = ~40 байт → 3 строки + 6 × 1 байт индексов = ~24 байта + меньше cache misses при фильтрации.

TIP

Dictionary encoding в Arrow отличается от Parquet dictionary. В Parquet dictionary кодируется на уровне page с fallback на PLAIN. В Arrow dictionary — на уровне всего массива, и indices массив — полноценный Arrow array с собственным validity bitmap.

Extension Types

Extension type — механизм расширения без изменения формата. Физическая раскладка — один из стандартных типов, но метаданные содержат имя расширения и сериализованные параметры.

Примеры:

  • arrow.uuid → FixedSizeBinary(16) + metadata “arrow.uuid”
  • arrow.json → Utf8 + metadata “arrow.json”
  • Geo: geoarrow.point → FixedSizeList(2, Float64) + metadata
Field {
 name: "user_id",
 type: FixedSizeBinary(16),
 metadata: {
 "ARROW:extension:name": "arrow.uuid",
 "ARROW:extension:metadata": ""
 }
}

Библиотека, не знающая extension type, читает данные как базовый тип (FixedSizeBinary(16)). Библиотека с поддержкой — десериализует в UUID-объект. Zero-copy совместимость сохраняется.

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

  1. Примитивные типы (Int32, Float64, Boolean) — validity + data buffer, O(1) доступ через pointer arithmetic
  2. Utf8View (формат Arrow IPC 1.4+, библиотека Arrow 15.0+ → 23/24 в 2026) — inline короткие строки (≤12 байт), prefix comparison для длинных — быстрее классического Utf8
  3. List / Struct — рекурсивная композиция: offsets + child arrays, произвольная глубина вложенности
  4. Dictionary — indices (Int8/16/32) + dictionary array, кратное сжатие для колонок с повторами
  5. Extension types — пользовательская семантика поверх стандартной раскладки, обратно совместимо
  6. RunEndEncoded — run-length encoding для колонок с длинными повторами, встроен в Arrow IPC формат spec 1.3+ (библиотека Arrow 12.0+); базовая поддержка стабильна вплоть до текущих Arrow 23 (январь 2026) и Arrow 24 (апрель 2026)

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

Результат: 0 из 0
Прикладной
Вопрос 1 из 4. Колонка status содержит 10 миллионов строк с 5 уникальными значениями ('active', 'pending', 'closed', 'draft', 'archived'). Какой Arrow тип даст минимальный расход памяти?

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

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

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

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