Learning Platform
Глоссарий Troubleshooting
Урок 06.03 · 25 мин
Начальный
parquetavroorccolumnarrow-baseddecision-matrix

Зачем три формата

В предыдущем уроке мы поняли разницу между row-based и columnar layout. Теперь посмотрим на три конкретных формата, которые data engineer встречает чаще всего: Parquet, Avro и ORC. Все три появились в 2009-2013, и в 2026 они занимают разные ниши в data pipeline.

Цель урока — обзор: где какой формат уместен, что у него внутри на верхнем уровне, как они сочетаются в типичном пайплайне. Глубже устройство, бенчмарки и edge-cases разбираем в отдельном курсе storage-formats.

Parquet: стандарт колоночного хранения

Apache Parquet родился в 2013 году внутри Twitter и Cloudera как ответ на проблему “Hadoop хранит всё в CSV и это нерабочее”. Идея — взять колоночные принципы из прошлого урока, обернуть в формат, дружелюбный к distributed storage (HDFS, S3), и сделать стандартом.

В 2026 году Parquet — это default-формат для data lake и lakehouse: Spark, Snowflake (внутреннее представление), Databricks, BigQuery (external tables), DuckDB, Athena, ClickHouse, Iceberg, Delta Lake — все читают и пишут Parquet. Если в индустрии есть один формат, который data engineer должен знать на верхнем уровне, это Parquet.

Иерархия: File, Row Group, Column Chunk, Page

Parquet-файл имеет четырёхуровневую иерархию.

Структура Parquet-файла

Файл делится на row groups, row group — на column chunks, column chunk — на pages

Parquet File: магические байты PAR1 в начале и в конце, footer с метаданными (schema, statistics по всем row groups, encoding metadata)
Row Group: горизонтальное разбиение таблицы на чанки строк (обычно 128-256 MB). Все колонки внутри одного row group занимают согласованный диапазон строк. Unit для параллельной обработки и predicate pushdown
Row Group 2: следующий блок строк. Каждый row group обрабатывается независимо — Spark task читает один row group
Column Chunk: данные одной колонки в пределах одного row group. Здесь происходит вся компрессия и encoding — словарь, RLE, потом Snappy/ZSTD
Column Chunk amount: значения колонки amount в пределах Row Group 1. Может иметь свой encoding, отличный от других колонок
Page: минимальная единица чтения и компрессии внутри column chunk. Обычно 1 MB. Содержит values, definition levels, repetition levels, header с encoding и compression info
Data Page: содержит сами значения, encoded и compressed. Может быть Plain, Dictionary, RLE encoded. Decompression и decoding на этом уровне
Dictionary Page (опционально): один на column chunk, содержит уникальные значения. Data pages ссылаются по индексу. Для низкокардинальных колонок даёт огромное сжатие

Row Group — горизонтальное разбиение таблицы. Если строк 100 млн, и row group настроен на 1 млн — будет 100 row groups. Каждый — самостоятельная единица для параллельной обработки и для predicate pushdown.

Column Chunk — данные одной колонки внутри одного row group. Внутри chunk данные сжаты и closely-packed.

Page — самый низкий уровень. Внутри column chunk данные нарезаются на pages по примерно 1 MB. Page — это что reader физически читает и декодирует за раз.

Schema, encoding, compression

Parquet несёт схему — типы каждой колонки прописаны в footer файла. Reader не угадывает типы, как в CSV.

Внутри column chunk данные сжимаются в два этапа. Сначала encoding (преобразование значений в более компактный вид):

  • Dictionary encoding — словарь для низкокардинальных колонок (currency, country).
  • RLE (Run-length encoding) — сжатие повторов: пять USD подряд хранятся как (USD, 5).
  • Delta encoding — для возрастающих чисел (timestamp, ID).
  • Bit packing — упаковка маленьких чисел в минимальное число бит.

Поверх encoding — общая компрессия: ZSTD (современный default), Snappy (старый default, всё ещё распространён), GZIP (legacy). В 2024+ индустрия конвергировалась на ZSTD: сжимает на 25-40% лучше Snappy при сопоставимой скорости decompression.

Statistics и predicate pushdown

В footer каждого row group хранятся min/max statistics по каждой колонке. Это позволяет readeru пропускать целые row groups без чтения данных. Запрос с фильтром на amount больше 1000: row group с max=500 пропускается. Это predicate pushdown, мы его разбирали в прошлом уроке.

Дополнительно с Parquet 2.5+ есть опциональные bloom filters для точечных поисков на high-cardinality колонках (user_id, transaction_id).

Partitioning

Parquet-файлы редко лежат “плоско” в одной папке. В data lake используется partitioning — иерархия папок по значению колонки:

s3://lake/orders/
  dt=2026-05-15/data-001.parquet
  dt=2026-05-16/data-001.parquet
  dt=2026-05-17/data-001.parquet

Hive-style partitioning понимается всеми движками. Запрос с фильтром по dt сразу пропускает все папки, кроме нужной — partition pruning. Это уровень над predicate pushdown, ещё более грубый и эффективный.

Avro: row-based для streaming

Apache Avro появился в 2009 году внутри Hadoop как формат для сериализации данных между системами. Главная задача — не хранение в data lake, а обмен между процессами: Kafka-сообщения, Hadoop RPC, дампы для бэкапов.

Avro row-based: одна запись лежит целиком, потом следующая. Это делает Avro плохим для аналитики (для агрегации читается всё), но отличным для streaming: Kafka consumer получает по сообщению, парсит целиком, обрабатывает.

Avro-файл состоит из header с JSON-схемой и блоков с записями, разделённых sync markers (16-байтные случайные последовательности для синхронизации при split). Сама схема — это JSON:

{
  "type": "record",
  "name": "Order",
  "fields": [
    {"name": "order_id", "type": "long"},
    {"name": "amount", "type": "double"},
    {"name": "currency", "type": "string"}
  ]
}

Сами данные лежат в бинарном виде. Без схемы прочитать невозможно.

Schema evolution — главная фича

Уникальная особенность Avro — поддержка эволюции схемы. Представь: ты пишешь Avro полгода со схемой v1, потом добавляешь поле discount. Это schema v2. Что делать со старыми данными?

Avro решает: при чтении используются writer schema (с которой данные были записаны) и reader schema (которой данные читают). Если они отличаются, выполняется compatibility resolution:

  • Если в reader schema есть поле с default value, которого нет в writer schema — используется default.
  • Если в writer schema есть поле, которого нет в reader schema — поле игнорируется.
  • Если типы совместимы (int в long, float в double) — автоматическая конвертация.

Это даёт возможность развивать схему без переписывания старых данных. Старый Kafka-consumer на v1 продолжает работать с v2-сообщениями (если изменения backward-compatible).

Где Avro живёт

  1. Kafka сообщения. Confluent Schema Registry хранит Avro-схемы по версиям, producer/consumer договариваются о версии. Стандарт enterprise-Kafka.
  2. Database CDC. Debezium часто пишет CDC-события в Avro в Kafka.
  3. Hadoop ingestion. Sqoop экспортирует RDBMS в Avro.

Avro для долгосрочного хранения аналитики не подходит — row-based проигрывает Parquet в скане в десятки раз. Но как transport в data pipeline — идеален.

ORC: альтернатива в мире Hive

Apache ORC (Optimized Row Columnar) появился в 2013 году внутри Hive как ответ на проблемы предыдущего формата RCFile. Это колоночный формат, концептуально близкий к Parquet, но с другими внутренними решениями.

ORC-файл делится на stripes (аналог row group в Parquet, обычно 64-256 MB). Stripe содержит index data (min/max, bloom filters, position pointers для каждых 10000 строк), row data (колоночные данные) и stripe footer (метаданные). В конце файла — file footer со схемой всего файла.

ORC чуть быстрее на точечных скиппинговых запросах благодаря row index по 10K строк (более тонкая гранулярность skipping). Parquet — на широких сканах. Разница в реальных бенчмарках 10-20%, не порядки.

ORC — формат Hadoop/Hive экосистемы. Если ты работаешь в компании со стэком Hive/Tez/Presto-Trino, скорее всего основной формат — ORC. Если стэк Spark/Databricks/Snowflake — почти всегда Parquet.

В 2026 году ORC заметно теряет долю: новые проекты строятся на Parquet, потому что Iceberg/Delta Lake поддерживают преимущественно Parquet. Но в legacy banking, telecom, retail компаниях ORC всё ещё стандарт.

Decision matrix: что выбирать

Когда какой формат уместен

Карта решений: от exchange до archive

Exchange = передача между системами/командами/партнёрами. Главное: универсальность, читаемость глазами, простая интеграция. Объём небольшой (МБ-ГБ)
Streaming = Kafka, Kinesis, real-time events. Главное: schema evolution, малый размер сообщения. Чтение по одной записи, schema registry
Data Lake = долгосрочное хранение для аналитики. Главное: columnar для projection/predicate pushdown, компрессия, partitioning. Объём от GB до PB
DWH ingestion = batch loads в Snowflake/BigQuery/Redshift. Лучший формат для COPY INTO — Parquet (быстрый, типизированный). CSV принимается, но медленнее в 5-10 раз
Hadoop/Hive legacy = старые стэки на Hive metastore. ORC исторически родной, Parquet тоже работает. Если новый проект — Parquet
Archive = долгосрочный cold storage. Glacier/Coldline. Главное: максимальная компрессия. Parquet с ZSTD на высоком уровне

Краткая шпаргалка:

СценарийФорматПочему
Обмен с партнёром (МБ-ГБ)CSVУниверсально, любая система читает
REST API ответJSONЕстественно для web
Лог одного сервисаJSONLStream-friendly, простой парсинг
Kafka topicAvroSchema evolution, типизированный
Data lake (S3)ParquetColumnar, compression, pushdown
Hive table (legacy)ORC или ParquetЗависит от стэка
DWH ingestionParquetБыстрый COPY с типами
Archive (cold)Parquet + ZSTDМаксимальная компрессия
ML trainingParquetСтандарт DataLoader

Главное правило: текстовые форматы (CSV/JSON) на границах, бинарные колоночные (Parquet) в середине, row-based бинарные (Avro) в transport.

Типичный pipeline

Пример реального data pipeline в e-commerce, где все три формата играют свою роль:

[OLTP Postgres] -> [Debezium CDC] -> [Kafka: Avro] -> [Spark Streaming]
                                                          |
                                                          v
                                          [S3 Data Lake: Parquet, partitioned by date]
                                                          |
                                                          v
                                          [DWH: Snowflake / BigQuery]
  • На входе: Avro в Kafka (schema evolution для эволюции схемы заказов).
  • В лейке: Parquet (compressed, partitioned, optimized for analytics).
  • В DWH: native columnar storage Snowflake/BigQuery (формат не виден пользователю).

ORC в этом пайплайне обычно нет, если только это не Hive-based стэк.

Debezium: как CDC захватывает изменения из Postgres Avro в Kafka: Schema Registry и бинарная сериализация Spark Structured Streaming: читаем Kafka и пишем Parquet
WARNING

Распространённый антипаттерн — хранить аналитические данные в Avro “потому что есть schema evolution”. Avro не оптимизирован для аналитики (row-based, нет projection/predicate pushdown). Schema evolution есть и в Parquet через table formats (Iceberg, Delta Lake), причём в более гибком виде на уровне таблицы.

Table formats поверх Parquet

В 2026 году одиночный Parquet редко используется как production storage — поверх живут table formats: Apache Iceberg, Delta Lake, Apache Hudi. Они добавляют:

  • ACID транзакции.
  • Schema evolution на уровне таблицы (не файла).
  • Time travel (запросы к данным “как было N часов назад”).
  • Partition evolution.
  • Branching (Iceberg).

Это следующая ступень после Parquet. Эту тему подробно разбираем в модуле 14 (data lakes и lakehouse).

Попробуй сам

  1. Возьми датасет 1-10 GB. Сохрани в CSV, JSON, JSONL, Parquet (Snappy), Parquet (ZSTD), Avro. Сравни размеры. Замерь время чтения каждого формата в pandas/DuckDB.
  2. Открой Parquet через pyarrow.parquet.ParquetFile и выведи schema, num_row_groups, размеры column chunks. Посмотри, какие encoding-и Parquet выбрал для каждой колонки.
  3. Поэкспериментируй со schema evolution Avro: создай v1 со схемой (id, amount), добавь поле currency с default. Прочитай v1-данные с v2-схемой.
  4. Найди в открытом dbt-проекте или Databricks-ноутбуке pipeline. Определи, какие форматы где живут — transport, storage, ingestion.
Проверка знанийKnowledge check
Архитектор предлагает: хранить 200 ТБ исторических логов в JSON, запросы аналитиков идут медленно — давай перейдём на CSV, это быстрее и проще, чем Parquet. Что не так и какой правильный план?
ОтветAnswer
CSV здесь не поможет — он тоже текстовый, row-based, без типов и statistics. Запросы будут такие же медленные. Правильный план: миграция в Parquet с (1) партиционированием по дате, (2) ZSTD compression, (3) row group 128-256 MB, (4) опционально table format поверх (Iceberg для schema evolution и ACID). После миграции 200 ТБ JSON превратятся в 20-40 ТБ Parquet (5-10x компрессия от текстового размера), типичный запрос за день — секунды вместо минут (projection + predicate pushdown + partition pruning). Это стандартная миграция data lake, окупается за месяц на снижении S3 costs и ускорении запросов.

Деталь по форматам — углубление в нашем курсе storage-formats.

Parquet internals: row groups, column chunks, pages Avro: контейнерный формат и binary encoding

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

Результат: 0 из 0
Концептуальный
Вопрос 1 из 6. Какая корректная иерархия структуры Parquet-файла сверху вниз?

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

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

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

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