Перейти к содержанию
Learning Platform
Глоссарий Troubleshooting
Урок 02.01 · 12 мин
Начальный
Columnar FormatRow FormatOLAPApache ArrowZero-Copy

Колоночный формат: почему аналитика требует другой памяти

Проблема строчного хранения

Традиционные OLTP-базы (PostgreSQL, MySQL) хранят данные по строкам. Каждая строка таблицы лежит в памяти непрерывным блоком: все поля одной записи рядом.

Для транзакционных операций это идеально: INSERT, UPDATE, DELETE работают с целой строкой, и все её поля доступны за одно обращение к памяти.

Но аналитический запрос устроен иначе. SELECT AVG(salary) FROM employees читает одну колонку из десятка. При строчном хранении процессор загружает все поля каждой строки, чтобы извлечь единственное нужное значение. На таблице с 10 колонками 90% прочитанных байт выбрасываются.

Row Storage vs Column Storage
Row Storage (OLTP)Строчное хранение: все поля записи рядом в памяти, оптимально для INSERT/UPDATE/DELETE
Column Storage (OLAP)Колоночное хранение: значения одной колонки непрерывно, оптимально для аналитики

Четыре преимущества колоночного формата

1. Column Pruning

При SELECT salary, age FROM employees колоночный движок читает ровно 2 буфера из 10. Строчный движок читает все 10 полей каждой строки и отбрасывает 8.

2. SIMD-векторизация

Значения одной колонки лежат в памяти последовательно (contiguous). Процессор обрабатывает 4-8 значений за одну SIMD-инструкцию (AVX2 / AVX-512 / NEON). При строчном хранении значения одного поля разбросаны через всю ширину строки — векторизация невозможна.

3. Компрессия

Однотипные значения в одной колонке сжимаются эффективнее. Run-Length Encoding для колонки department с повторяющимися “IT”, “HR”, “Sales” даёт 10-50x сжатие. Dictionary Encoding для low-cardinality строк заменяет строки индексами.

4. Cache Locality

CPU cache line — 64 байт. Для int64 колонки это 8 значений за одну загрузку. При строчном хранении каждое следующее значение salary отделено полной шириной строки — cache miss на каждом обращении.

Apache Arrow: универсальный in-memory стандарт

Apache Arrow — спецификация колоночного формата данных в оперативной памяти. Не библиотека, не движок, а именно спецификация byte layout.

NOTE

Arrow vs Parquet: Parquet — формат хранения на диске (row groups, page compression). Arrow — формат обработки в RAM (буферы фиксированного размера, мгновенный произвольный доступ). Parquet оптимизирован для I/O, Arrow — для CPU.

Arrow определяет точный побайтовый layout для каждого типа данных. Это означает:

  • Данные в Arrow в Python, Rust, Java, C++, Go занимают одни и те же байты
  • Передача между языками — передача указателя, не сериализация
  • Нет overhead конвертации при смене runtime

Zero-Copy: ключевое свойство

Zero-copy означает, что данные не копируются при передаче между компонентами. Python-процесс может передать Arrow-буфер Rust-расширению без единого memcpy. DataFusion получает данные из внешнего источника в Arrow формате и сразу обрабатывает — без десериализации.

Zero-Copy в действии
Python (PyArrow)Python-реализация Arrow через C++ backend. Интеграция с Pandas, Polars, DuckDB
Rust (arrow-rs)Чистая Rust-реализация Arrow. Основная зависимость DataFusion
Java (Arrow Java)Java-реализация Arrow с off-heap памятью для Spark и Flink
Arrow Memory (один byte layout)Все реализации работают с одним byte layout. Shared memory pointer вместо сериализации
Обработка без десериализацииДанные уже в нужном формате — нет overhead конвертации между языками

Arrow до и после: мир без стандарта

До Arrow каждая система определяла собственный in-memory формат. Pandas использовал NumPy arrays, Spark — Tungsten binary format, R — SEXP vectors. Передача данных между системами означала сериализацию и десериализацию на каждой границе.

Pandas → (serialize) → bytes → (deserialize) → Spark → (serialize) → bytes → (deserialize) → R

С Arrow:

Pandas (Arrow) → shared memory → Spark (Arrow) → shared memory → DataFusion (Arrow)

Это не теоретическое преимущество. DataFusion построен на Arrow с самого начала: каждый оператор принимает и возвращает RecordBatch — основную единицу данных Arrow. Промежуточных форматов нет.

RecordBatch: основная единица данных

RecordBatch — группа колонок одинаковой длины с общей схемой. Это аналог “страницы” данных в Arrow мире.

// Структура RecordBatch (концептуально)
RecordBatch {
    schema: Schema,          // имена и типы колонок
    columns: Vec<ArrayRef>,  // сами данные (по колонке)
    row_count: usize,        // количество строк
}

DataFusion обрабатывает данные потоково: каждый оператор (filter, projection, join) получает поток RecordBatch, трансформирует и передаёт дальше. Типичный размер batch — 8192 строки, достаточно для эффективной векторизации и при этом помещается в L2 cache.

TIP

В следующем уроке мы разберём внутреннюю структуру Arrow-буферов: validity bitmap, offset buffer и data buffer — три компонента, из которых состоит каждая колонка.

Итоги

  • Строчное хранение оптимально для OLTP, колоночное — для OLAP
  • Колоночный формат даёт column pruning, SIMD, компрессию, cache locality
  • Apache Arrow — единая спецификация колоночного in-memory формата
  • Zero-copy: данные передаются указателем, не копированием
  • RecordBatch — основная единица данных, поток RecordBatch — модель обработки DataFusion “text-emerald-400” size=“1.2em” /> RecordBatch — основная единица данных, поток RecordBatch — модель обработки DataFusion �м
  • RecordBatch — основная единица данных, поток RecordBatch — модель обработки DataFusion ь обработки DataFusion

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

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

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

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