Learning Platform
Глоссарий Troubleshooting
Урок 13.03 · 30 мин
Начальный
File FormatsDecision TreeBenchmarksORCFormat Comparison

Выбор формата хранения: CSV, JSON, JSONL, Avro, Parquet, ORC

В предыдущих уроках мы разбирали отдельные форматы — JSON в API, Avro в Kafka, Parquet в data lake-ах. Теперь сведём их в единую систему координат и научимся выбирать формат под конкретную задачу. Это решение влияет на стоимость хранения, скорость запросов и удобство интеграции на годы вперёд.

Зачем нужен Schema Registry: проблема бесхозных данных

Шесть основных форматов

ФорматТипСжатиеSchemaHuman-readableЛучше всего для
CSVrow, текстнет (обычно)нетдаad-hoc, экспорт в Excel, малые объёмы
JSONrow, текстнетопционально (JSON Schema)даAPI responses, config файлы
JSONLrow, текстнетопциональнодалоги, потоковая обработка
Avrorow, бинарныйsnappy/zstdобязательнаянетKafka, streaming, schema evolution
Parquetcolumn, бинарныйsnappy/zstdобязательнаянетdata lake, OLAP-аналитика
ORCcolumn, бинарныйsnappy/zstdобязательнаянетальтернатива Parquet, Hive ecosystem

Когда выбрать какой

CSV: текстовый row-формат

id,name,age,city
1,Alice,30,Moscow
2,Bob,25,Kazan

Берите CSV, если:

  • Данные нужно открыть в Excel/Google Sheets.
  • Объём — мегабайты, не гигабайты.
  • Получатель не имеет инфраструктуры для бинарных форматов.
  • Нужна максимальная совместимость с чем угодно.

Не берите, если:

  • Объём больше 1 GB (парсинг будет мучительным).
  • В данных есть запятые/переносы строк (escaping ужасен).
  • Нужны типы (всё парсится как строки, дата угадывается).
  • Аналитика OLAP (CSV scan-ит всё подряд, не имеет statistics).

CSV не имеет строгой спецификации. RFC 4180 — слабая попытка. На практике каждый инструмент трактует quoting и escaping по-своему. Готовьтесь к боли с UTF-8 BOM, CRLF против LF, разделителями (, vs ; в немецких локалях).

JSON: текстовый row-формат с типами

{"id": 1, "name": "Alice", "age": 30, "tags": ["vip", "early"]}

Берите JSON, если:

  • Это API response (стандарт де-факто для REST/GraphQL).
  • Конфиг приложения.
  • Структура иерархическая, со вложенными объектами.

Не берите, если:

  • Вам нужен потоковый формат (целый файл — массив объектов, парсер должен прочитать всё в память). Используйте JSONL.
  • Объём терабайтный (overhead имён полей огромный).
  • Аналитические запросы (никакого pruning, только sequential scan).

JSON-парсинг медленнее, чем кажется: на 1 GB файле — десятки секунд. Используйте orjson или simdjson для ускорения в 5-10 раз.

JSONL (NDJSON): line-delimited JSON

{"id": 1, "name": "Alice"}
{"id": 2, "name": "Bob"}
{"id": 3, "name": "Carol"}

Один JSON-объект на строку. Решает главную проблему JSON: можно читать построчно, не загружая весь файл.

Берите JSONL, если:

  • Логи приложения.
  • Append-only event log.
  • Промежуточный формат в pipeline между ingestion и сериализацией в Parquet.
  • Стриминг ответов больших API (jsonlines в качестве response-формата).

Не берите, если:

  • Нужна аналитика — конвертируйте в Parquet.
  • Есть alternative с сжатием/типами.

JSONL — стандарт MLOps (датасеты в Hugging Face, OpenAI fine-tuning), наблюдаемости (Grafana Loki хранит логи в JSONL-подобном формате), стриминга API (LLM-провайдеры иногда отдают так).

Avro: бинарный row-формат

Берите Avro, если:

  • Это Kafka-сообщения через Schema Registry.
  • Долгосрочное хранение со schema evolution.
  • Streaming pipeline с inter-service контрактами.
  • Hadoop/HDFS экосистема.

Не берите, если:

  • Данные нужно анализировать аналитически (Avro scan-ит всё, нет column pruning).
  • Нет инфраструктуры для управления schema (Registry, codegen).
  • Получатель не понимает Avro (большинство BI-инструментов — нет).

Parquet: бинарный column-формат

Берите Parquet, если:

  • Data lake / data warehouse layer.
  • Аналитические запросы (OLAP, агрегации).
  • Iceberg / Delta Lake / Hudi (все используют Parquet под капотом).
  • Storage tier для batch ML training.

Не берите, если:

  • Streaming (нельзя писать одну запись).
  • Точечные обновления существующих строк (нужен формат с updates: Iceberg/Delta поверх).
  • Малые объёмы (overhead row groups vs CSV).

ORC: альтернатива Parquet

ORC (Optimized Row Columnar) — формат от Hortonworks, часть Hive ecosystem. Концептуально похож на Parquet: tripple-уровневая структура, columnar storage, encoding + compression. Отличается deталями:

  • Stripes вместо row groups.
  • Light-weight indexes на каждые 10000 строк (быстрее skipping).
  • Лучше для очень wide tables (1000+ колонок).
  • Лучшая интеграция с Hive ACID транзакциями.

Берите ORC, если:

  • Вы в Hive/HDFS-стеке.
  • Нужны ACID-транзакции в Hive.

Берите Parquet, если:

  • Вы вне строгого Hive-окружения (S3 + Spark, Snowflake, ClickHouse, DuckDB).

В 2026 Parquet — выигравший стандарт за пределами Hive. ORC сохраняется в Hive-инсталляциях как наследие.

Бенчмарк: 10 миллионов строк продаж

Возьмём типовую DE-таблицу: order_id, user_id, product_id, amount, currency, ts, country, device, status. 9 колонок, 10M строк.

ФорматРазмер на дискеВремя записиВремя чтения всех строкВремя SELECT amount, country WHERE country = 'US'
CSV (gzip)320 MB18 s25 s25 s (full scan)
JSON2.1 GB35 s60 s60 s
JSONL1.9 GB25 s45 s45 s
JSONL + gzip280 MB30 s30 s30 s
Avro (snappy)145 MB8 s12 s12 s (no pruning)
Parquet (snappy)95 MB10 s8 s1.2 s (column + predicate pushdown)
Parquet (zstd)75 MB11 s9 s1.4 s
ORC (zstd)80 MB12 s9 s1.3 s

Цифры — примерные, зависят от данных и железа. Основной вывод: Parquet выигрывает у CSV в 20+ раз на аналитических запросах и в 3 раза на размере. Avro проигрывает Parquet на аналитике, но идеально подходит для Kafka, где нужны row-by-row сообщения.

Decision tree выбора формата

Какая природа данных?

Стартовая точка любого выбора формата
Streaming / event-based

Avro в Kafka + Schema Registry

Каждое сообщение независимо, append-only поток

Аналитика SQL поверх big data?

Большие объёмы, аналитика
Да, больше 1 GB

Parquet с партиционированием

Columnar + statistics + pruning = огромный выигрыш на OLAP

Логи / append-only event log?

Логи, события приложения
Да

JSONL.gz, потом конвертация в Parquet

Простой формат, можно стримить, gzip снижает размер в 5-10 раз

Передача внешнему получателю без Big Data tooling-а?

Надо отправить data scientist-у в виде, понятном Excel
Да

CSV или JSON

Самый универсальный формат, открывается в Excel, Notepad, Python

Антипаттерны

Parquet для row-by-row updates

«Обновим в Parquet строку, где user_id=42». Это не работает: Parquet immutable, нужно перезаписать целый row group. Правильно: использовать слой поверх (Iceberg, Delta Lake, Hudi), который умеет в logical updates через файлы изменений + compaction.

JSON для аналитики 1 TB

Парсинг 1 TB JSON — десятки часов CPU. Storage будет 5-10x больше эквивалентного Parquet. Аналитика — full scan на каждом запросе. Решение: один раз конвертировать в Parquet, затем работать с ним.

CSV для сложных типов

Вложенные объекты, массивы, NULL vs пустая строка, даты в разных форматах — CSV не способна это нормально передать. Получатель будет угадывать, и ошибётся. Используйте JSONL или Parquet.

Avro для аналитики

Avro — row-формат. На запросах с фильтрами и проекциями он читает все колонки и все записи. Это в 10-50 раз медленнее Parquet. Используйте Avro только для transport/streaming, для аналитики — конвертируйте в Parquet.

Parquet для маленьких частых файлов

«Я пишу события по одному в S3 как Parquet». В итоге миллионы маленьких файлов, footer overhead больше payload-а, query engine задыхается на listing-е. Правильно: батчить — собирать в Avro/JSONL, потом раз в час/день конвертировать в Parquet.

Custom формат

«Мы изобрели свой бинарный формат». Через год: никакой инструмент его не поддерживает, документация устарела, разработчик уволился. Используйте стандарты — выгода от specialty форматов почти никогда не окупает потерю экосистемы.

WARNING

Самый частый антипаттерн в DE — оставлять данные в JSON «потому что так пришло». Конвертация в Parquet занимает один раз 10 минут и экономит часы на каждом будущем запросе. Делайте её сразу в bronze->silver слое pipeline-а.

Гибридный подход в pipeline

Реальный DE pipeline использует разные форматы на разных уровнях:

1. Ingestion       -> JSONL.gz   (простота, append, small files OK)
2. Bronze layer    -> Parquet    (raw, partitioned by ingestion date)
3. Silver layer    -> Parquet    (cleaned, partitioned by business date)
4. Gold layer      -> Parquet    (aggregated, partitioned by query pattern)
5. Serving         -> Parquet + materialized views в OLAP-движке
6. Streaming       -> Avro в Kafka между сервисами
7. API responses   -> JSON (REST), Protobuf (gRPC)
8. Reports         -> CSV для бизнеса, Parquet для DS

Каждый формат на своём месте. Не пытайтесь использовать один формат везде — это всегда компромисс не в вашу пользу.

Memo для junior DE

Что нужно уметь практически:

  • Конвертировать между форматами через pyarrow и pandas: pd.read_csv().to_parquet(), pd.read_json(lines=True).to_parquet().
  • Понимать сжатие: snappy быстро, zstd компактнее, gzip универсально, brotli редко.
  • Уметь партиционировать Parquet по дате/региону через partition_cols.
  • Различать сценарии: streaming = Avro, analytics = Parquet, передача наружу = CSV/JSON.
  • Не выбирать формат «навсегда» — на разных слоях pipeline-а оптимальны разные форматы.
Проверка знанийKnowledge check
Pipeline выгружает данные из Salesforce REST API (~5M записей в день). Дальше используется в двух сценариях: 1) ML-команда тренирует модель раз в неделю по выборке за 2 года, 2) BI-команда строит daily dashboard за последние 30 дней. Команда сейчас хранит выгрузки как JSON-файлы в S3. Что предложите изменить и почему?
ОтветAnswer
Сценарий -- типичный multi-purpose data lake, где JSON неоптимален для обоих use-case-ов. Изменения: (1) Слой ingestion (raw из API) -- оставить JSONL.gz. Это совместимо со streaming-style выгрузкой Salesforce, gzip сжимает в 5-10 раз, append-only. Один файл на день: salesforce-raw/year=2026/month=05/day=14.jsonl.gz. (2) Слой bronze -- добавить шаг конвертации в Parquet раз в день, партиционированный по date: salesforce-bronze/year=2026/month=05/day=14/part-N.parquet. Это даёт 5-10x меньший размер и 10-50x ускорение аналитики. Compression -- zstd. (3) Слой silver/gold для BI -- daily aggregates в Parquet с партиционированием по date, отсортированные по часто фильтруемым полям (account_id, region). Это даёт partition pruning для 30-дневного dashboard-а -- движок откроет только 30 директорий из 730 за 2 года. (4) Для ML -- выборка через pyarrow.dataset с фильтром по дате; ML код будет читать только нужные колонки (column pruning) и использовать predicate pushdown. Для тренировки 2-летнего среза -- 730 партиций, но column pruning сократит I/O в 5-10 раз против чтения всех полей. Эффект: размер хранения снижается в 5-10 раз (зависит от типа данных), BI-запросы -- с минут до секунд, ML training I/O -- в 5-10 раз быстрее. Стоимость S3 + compute падает в разы. Antipattern, которого избегаем: писать каждую API-страницу как отдельный Parquet-файл (миллионы маленьких файлов = катастрофа). Правильно -- батчить в JSONL за день, потом один раз конвертировать в Parquet.

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

Результат: 0 из 0
Прикладной
Вопрос 1 из 4. Какой формат оптимален для каждого сценария: 1) Kafka-сообщения между микросервисами с эволюцией схемы. 2) S3 data lake для аналитики по 5 ТБ исторических событий. 3) Экспорт топ-10 клиентов финансовому директору в Excel.

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

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

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

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