Type System
Обзор системы типов
Arrow определяет каноническую систему типов, общую для всех реализаций. Любая Arrow-библиотека (C++, Rust, Java, Python, Go) обязана поддерживать один и тот же набор типов с одинаковой семантикой — иначе zero-copy обмен невозможен.
Типы делятся на три категории:
- Примитивные — фиксированная ширина, один data buffer
- Переменной ширины — offsets + data buffers
- Вложенные — комбинация буферов дочерних массивов
Примитивные типы: буферная раскладка
Все примитивные типы имеют одинаковую структуру: validity bitmap + data buffer.
| Тип | Байт на элемент | Диапазон | Типичное использование |
|---|---|---|---|
| Int8 / UInt8 | 1 | -128..127 / 0..255 | Флаги, статусы |
| Int32 / UInt32 | 4 | ±2.1B / 0..4.3B | ID, количества |
| Int64 / UInt64 | 8 | ±9.2E18 / 0..1.8E19 | Timestamps, large IDs |
| Float32 | 4 | ±3.4E38 | Координаты, ML features |
| Float64 | 8 | ±1.8E308 | Финансы, научные вычисления |
| Decimal128 | 16 | 38 значащих цифр | Денежные суммы (точная арифметика) |
| FixedSizeBinary(N) | N | N байт | UUID (16B), IP-адреса, хэши |
Строки: 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 даёт ускорение на фильтрации строк: 4-байтовый prefix сравнивается до обращения к основному буферу. Если prefix не совпадает — строка отклоняется без pointer chasing. DataFusion 38+ использует Utf8View по умолчанию.
Temporal Types
Arrow различает несколько временных типов — каждый с конкретной семантикой:
| Тип | Хранение | Семантика |
|---|---|---|
| Date32 | Int32 | Дни с Unix epoch (1970-01-01) |
| Date64 | Int64 | Миллисекунды с Unix epoch |
| Time32(unit) | Int32 | Время дня (секунды или миллисекунды) |
| Time64(unit) | Int64 | Время дня (микро- или наносекунды) |
| Timestamp(unit, tz) | Int64 | Момент времени с точностью до unit, опциональная timezone |
| Duration(unit) | Int64 | Разница между двумя моментами |
| Interval(MonthDayNano) | 16 bytes | Календарный интервал: месяцы + дни + наносекунды |
Timestamp без timezone и Timestamp с timezone — разные типы. Arrow не конвертирует между ними неявно. Timestamp(μs, None) — локальное время без привязки к часовому поясу. Timestamp(μs, “UTC”) — конкретный момент на шкале UTC. Если вы конкатенируете массивы с разными timezone — получите ошибку типов.
Вложенные типы: List
List — массив списков переменной длины. Содержит:
- Validity bitmap — NULL для целого списка
- Offsets buffer (Int32) —
N + 1смещений в дочерний массив - Child array — единый массив всех элементов всех списков
Доступ к элементам списка i: child[offsets[i] .. offsets[i+1]]. Рекурсивно — child array может быть любым Arrow-типом, включая другой List.
Вложенные типы: Struct
Struct — аналог строки таблицы или JSON-объекта. Все поля одного Struct имеют одинаковое количество элементов.
Struct не имеет собственного data buffer — только validity bitmap и дочерние массивы по одному на каждое поле.
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 — не отдельный тип, а обёртка над любым типом. Состоит из:
- Dictionary — массив уникальных значений (Utf8, Int32, etc.)
- Indices — массив целых чисел (Int8/16/32), указывающих на позиции в dictionary
Выигрыш: вместо хранения строк по 6–7 байт × 6 = ~40 байт → 3 строки + 6 × 1 байт индексов = ~24 байта + меньше cache misses при фильтрации.
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 совместимость сохраняется.
Ключевые выводы
- Примитивные типы (Int32, Float64, Boolean) — validity + data buffer, O(1) доступ через pointer arithmetic
- Utf8View (формат Arrow IPC 1.4+, библиотека Arrow 15.0+ → 23/24 в 2026) — inline короткие строки (≤12 байт), prefix comparison для длинных — быстрее классического Utf8
- List / Struct — рекурсивная композиция: offsets + child arrays, произвольная глубина вложенности
- Dictionary — indices (Int8/16/32) + dictionary array, кратное сжатие для колонок с повторами
- Extension types — пользовательская семантика поверх стандартной раскладки, обратно совместимо
- RunEndEncoded — run-length encoding для колонок с длинными повторами, встроен в Arrow IPC формат spec 1.3+ (библиотека Arrow 12.0+); базовая поддержка стабильна вплоть до текущих Arrow 23 (январь 2026) и Arrow 24 (апрель 2026)