Сначала зафиксируйте grain данных, допущения, метрику и риск leakage, затем обсуждайте модель, инструмент или инфраструктуру.
Вопросы и ответы
9 подробных ответов
01Чем колоночное хранение отличается от строкового и почему для аналитики быстрее и дешевле именно колоночный формат?
middle
Короткий ответ: В строковом формате значения одной записи лежат рядом, в колоночном — рядом лежат все значения одной колонки. Для аналитических запросов (агрегаты по нескольким колонкам из десятков) колоночный формат читает только нужные колонки, лучше сжимается и поддерживает предикатный пушдаун — отсюда меньше I/O и ниже стоимость.
Подробно:
- Проекция колонок (projection pushdown) — запросу нужны 3 колонки из 50, и колоночный движок читает с диска только эти три. Строковый формат вынужден прочитать строку целиком.
- Сжатие — в колонке лежат однотипные значения (один тип, похожие данные), поэтому dictionary-, run-length- и delta-кодирование дают степень сжатия в разы выше, чем на разнородной строке.
- Предикатный пушдаун (predicate pushdown) — по хранимым min/max статистикам блока фильтр
WHERE date = ...пропускает целые блоки, не читая их. - Строковый формат выигрывает при точечных операциях: прочитать/записать запись целиком (OLTP, стриминг по одной записи).
Строковый (row): [id=1,name=A,ts=..][id=2,name=B,ts=..][id=3,...]
└── запись 1 ──┘└── запись 2 ──┘
Колоночный (columnar):
id: [1, 2, 3, ...] ← читаем только нужные
name: [A, B, C, ...] колонки, каждая
ts: [.., .., .., ...] сжата отдельно
⚠️ Частая ошибка: считать колоночный формат universally лучшим. Для построчной записи и чтения записи целиком (стриминг, транзакционные апдейты) строковый формат (Avro) эффективнее.
02Как устроен Parquet изнутри — row groups, column chunks, кодирование — и как он делает predicate и projection pushdown?
middle
Короткий ответ: Parquet — колоночный формат, где файл разбит на горизонтальные row groups, внутри каждого данные хранятся по колонкам (column chunks), а колонка — из страниц (pages). В футере лежат схема и статистики (min/max, счётчики null) по каждому column chunk, за счёт которых движок пропускает лишние данные.
Подробно:
- Row group — горизонтальный срез (обычно 128–512 МБ), единица параллелизма: разные воркеры читают разные row groups.
- Column chunk — данные одной колонки внутри row group; читаются только колонки из проекции.
- Pages — колонка бьётся на страницы (~1 МБ) со своими статистиками и кодированием (dictionary, RLE, bit-packing) поверх сжатия (Snappy/Zstd).
- Footer — метаданные в конце файла: схема, смещения, min/max по chunk. Читается первым, поэтому фильтр отсекает row groups до чтения данных.
Parquet file
┌──────────────────────────────────────────┐
│ Row Group 0 │
│ col A chunk [page|page|...] (min/max) │
│ col B chunk [page|page|...] (min/max) │
│ Row Group 1 │
│ col A chunk ... col B chunk ... │
├──────────────────────────────────────────┤
│ Footer: schema + row-group/column stats │
└──────────────────────────────────────────┘
⚠️ Частая ошибка: ждать пушдауна, а на деле генерировать тысячи мелких файлов по паре row groups — статистики почти не отсекают данные, и накладные расходы на открытие файлов съедают выигрыш.
03Parquet, ORC, Avro, CSV/JSON — в чём разница и когда какой формат выбирать?
middle
Короткий ответ: Parquet и ORC — колоночные форматы для аналитики (сжатие, пушдаун). Avro — строковый бинарный формат для стриминга и обмена сообщениями с сильной эволюцией схемы. CSV/JSON — текстовые форматы для обмена и отладки, но не для аналитики на объёме.
Подробно:
| Формат | Тип | Схема | Сильная сторона | Когда брать |
|---|---|---|---|---|
| Parquet | колоночный | встроена | сжатие, projection/predicate pushdown | аналитика, data lake, Spark/Trino |
| ORC | колоночный | встроена | сжатие + встроенные индексы | экосистема Hive |
| Avro | строковый | отдельная (JSON) | эволюция схемы, компактность | Kafka, стриминг, обмен между сервисами |
| CSV | строковый текст | нет | простота, читаем глазами | ручной обмен, мелкие выгрузки |
| JSON | строковый текст | нет | вложенность, гибкость | API, логи, отладка |
- Аналитика по колонкам — Parquet или ORC: читаешь мало колонок из многих, статистики отсекают блоки.
- Стриминг и обмен сообщениями — Avro: пишешь запись целиком, схема эволюционирует без переписывания данных.
- Текстовые CSV/JSON — удобны и переносимы, но нет типов, статистик и эффективного сжатия — для больших таблиц дорого.
⚠️ Частая ошибка: держать аналитический слой в CSV или «сыром» JSON. На объёме это кратно больше I/O и денег, чем тот же датасет в Parquet.
04Открытые табличные форматы: Apache Iceberg, Delta Lake, Hudi — что они дают поверх «сырого» Parquet?
senior
Короткий ответ: Табличный формат — это слой метаданных над файлами Parquet/ORC в объектном хранилище, который превращает набор файлов в таблицу с ACID-транзакциями, атомарными коммитами, time travel, эволюцией схемы и партиций. Iceberg, Delta и Hudi решают одно и то же, различаясь деталями движка и экосистемы.
Подробно:
- ACID и атомарные коммиты — читатели видят либо старую, либо новую версию таблицы, без «полусырых» файлов на середине записи.
- Time travel — снапшоты по версии/времени: можно прочитать таблицу «как было» и откатиться.
- Эволюция схемы и партиций — добавить/переименовать колонку и даже сменить схему партиционирования без переписывания данных.
- Hidden partitioning (Iceberg) — партиция выводится из значения (например, из timestamp), пользователю не нужно вручную фильтровать по колонке-партиции.
| Формат | Модель метаданных | Особенность |
|---|---|---|
| Iceberg | manifest-файлы, снапшоты | hidden partitioning, движко-независимость |
| Delta Lake | транзакционный лог _delta_log |
тесная связка со Spark, MERGE |
| Hudi | таймлайн коммитов | upsert-и и incremental pull из коробки |
-- time travel в Iceberg: читаем таблицу на конкретный снапшот
SELECT * FROM orders
FOR SYSTEM_VERSION AS OF 3821550127947089009;
⚠️ Частая ошибка: думать, что «положил Parquet в S3 — уже есть таблица». Без табличного формата нет ни атомарности при конкурентной записи, ни time travel, ни безопасной эволюции схемы.
05Как партиционировать данные в объектном хранилище и какие ловушки у мелких файлов и переизбытка партиций?
middle
Короткий ответ: Партиционирование — раскладка данных по директориям вида col=value, чтобы запрос по фильтру читал только нужные партиции (partition pruning). Выбирайте колонку с умеренной кардинальностью, по которой реально фильтруете; избегайте слишком мелких партиций и переизбытка партиций.
Подробно:
- Партиция = префикс пути — движок видит
year=2026/month=06и пропускает остальные партиции без чтения данных. - Выбор ключа — брать колонку из частых
WHERE(дата, регион) с умеренной кардинальностью; высококардинальные ключи (user_id) плодят миллионы партиций. - Размер файла — целитесь в файлы 128 МБ–1 ГБ; много крошечных файлов = проблема мелких файлов и медленный листинг в S3.
- Не переусердствовать — тысячи партиций по паре файлов дают дорогой листинг метаданных и почти не ускоряют чтение.
s3://lake/events/
year=2026/
month=06/
day=30/
part-0001.parquet (≈256 МБ)
part-0002.parquet (≈256 МБ)
month=07/
day=01/
part-0001.parquet
⚠️ Частая ошибка: партиционировать по высококардинальной колонке (например, по user_id) — получаются тысячи директорий с крошечными файлами, и листинг метаданных становится дороже самого запроса.
06Кодеки сжатия Snappy, Zstd, Gzip: как выбирать между скоростью и степенью сжатия и почему важна splittable-разбиваемость?
middle
Короткий ответ: Кодек — это компромисс между скоростью и степенью сжатия. Snappy — быстрый, но слабее жмёт; Gzip жмёт сильнее, но медленный и не splittable в сыром виде; Zstd даёт лучший баланс с настраиваемым уровнем. Для аналитики важна разбиваемость, чтобы читать файл параллельно.
Подробно:
- Скорость vs степень — Snappy оптимизирует CPU (частое чтение), Gzip — размер (архив/выгрузка), Zstd покрывает диапазон уровнем сжатия.
- Splittable-разбиваемость — можно ли читать разные части файла параллельно. Сырой Gzip неразбиваем: один воркер тянет весь файл, теряя параллелизм.
- Спасение внутри Parquet — в Parquet/ORC сжатие идёт по страницам/чанкам, поэтому даже Gzip внутри Parquet читается параллельно по row groups.
- Практика — Snappy как дефолт для «горячих» данных, Zstd когда важнее хранение при приемлемом CPU.
| Кодек | Скорость | Степень | Splittable (сырой) |
|---|---|---|---|
| Snappy | очень высокая | средняя | нет (сам по себе) |
| Zstd | высокая, настраиваемая | высокая | нет (сам по себе) |
| Gzip | низкая | высокая | нет |
| внутри Parquet | — | — | да (по row groups) |
⚠️ Частая ошибка: класть большой .csv.gz как единый несплитируемый файл — весь гигабайт читает один воркер, и параллелизм кластера простаивает. Дробите на файлы или используйте Parquet.
07Что такое эволюция схемы и как безопасно добавлять, переименовывать и удалять колонки в Avro и табличных форматах?
senior
Короткий ответ: Эволюция схемы — изменение структуры данных без переписывания уже записанных файлов и без поломки старых читателей/писателей. Безопасность зависит от совместимости: добавление колонки со значением по умолчанию обычно безопасно, удаление и переименование — рискованны. Avro решает это через разрешение схем (schema resolution), табличные форматы — через отслеживание колонок по стабильным id.
Подробно:
- Добавление колонки — безопасно при наличии default: старые файлы читаются, отсутствующее значение подставляется дефолтом.
- Удаление колонки — безопасно для читателей нового кода, но старый читатель, требующий поле, сломается — нужна обратная совместимость.
- Переименование — самое опасное «в лоб»: по имени это удаление + добавление. Avro решает через алиасы, Iceberg — через стабильные column id, а не имена.
- Совместимость — backward (новая схема читает старые данные), forward (старая схема читает новые данные), full — обе сразу; в Kafka её стережёт Schema Registry.
| Операция | Avro | Iceberg/Delta | Риск |
|---|---|---|---|
| Добавить колонку | default в схеме | безопасно, id новый | низкий |
| Удалить колонку | reader игнорирует | помечается удалённой | средний |
| Переименовать | алиас | по column id, не по имени | высокий без поддержки |
⚠️ Частая ошибка: переименовать колонку в «сыром» Parquet-датасете по имени — старые файлы под старым именем перестают читаться как новая колонка, и данные молча пропадают из выборок.
08Объектное хранилище (S3/GCS) как основа data lake: чем оно отличается от HDFS/POSIX и что важно знать про консистентность и стоимость?
middle
Короткий ответ: Объектное хранилище — это плоское key-value хранилище объектов по HTTP, а не файловая система: нет настоящих директорий, нет дешёвого переименования, оплата идёт за хранение и за запросы. В отличие от HDFS/POSIX оно отделяет хранение от вычислений и масштабируется практически безгранично, но переименование папки — это копирование всех объектов.
Подробно:
- Плоское пространство ключей — «директории» это лишь префиксы в имени объекта; листинг идёт по префиксу, а не обходом дерева.
- Нет атомарного rename — переименование/перемещение = copy + delete по каждому объекту, поэтому коммит через переименование папки (как в HDFS) дорог; отсюда committers и табличные форматы.
- Консистентность — современный S3 read-after-write строго консистентен, но исторически была eventual consistency; закладывайтесь на возможные задержки в старых системах.
- Модель стоимости — платите за GB хранения, за GB трафика и за количество запросов (GET/PUT/LIST) — отсюда штраф за миллионы мелких файлов.
| Свойство | Объектное (S3/GCS) | HDFS/POSIX |
|---|---|---|
| Модель | key-value по HTTP | файловая система |
| Rename папки | copy+delete (дорого) | атомарный, дешёвый |
| Масштаб/стоимость | почти безграничный, платишь за запросы | ограничен кластером |
| Хранение vs compute | разделены | связаны с узлами |
⚠️ Частая ошибка: относиться к S3 как к POSIX-ФС и делать много LIST/rename на миллионах ключей — счёт за запросы и латентность листинга неожиданно становятся узким местом пайплайна.
09В чём проблема мелких файлов, почему она убивает производительность и как её лечить компакцией?
middle
Короткий ответ: Проблема мелких файлов — это когда таблица состоит из тысяч крошечных файлов вместо немногих крупных. Каждый файл требует отдельного открытия, чтения футера и планирования задачи, поэтому накладные расходы превышают полезное чтение. Лечится компакцией — периодическим слиянием мелких файлов в файлы целевого размера.
Подробно:
- Откуда берётся — частые микро-батчи и стриминг, слишком мелкие партиции, много воркеров, каждый пишет свой крошечный файл.
- Почему больно — на каждый файл: запрос к S3, чтение метаданных, отдельный split/таск планировщика; тысячи файлов = тысячи открытий и медленный листинг.
- Компакция (compaction) — фоновая задача читает мелкие файлы и переписывает их в крупные (целимся в 128 МБ–1 ГБ); в Iceberg/Delta есть встроенные операции (
OPTIMIZE,rewrite_data_files). - Профилактика — писать реже и крупнее, дозагрузка батчей, разумная гранулярность партиций, не плодить лишние партиции.
До компакции: [f1][f2][f3][f4][f5]...[f999] ← 999 файлов × ~1 МБ
tiny files problem
│ compaction
▼
После: [ big-0001.parquet ][ big-0002.parquet ] ← 2 файла × ~256 МБ
⚠️ Частая ошибка: гнать стриминг напрямую в озеро микро-батчами по секунде без последующей компакции — таблица за сутки обрастает сотнями тысяч файлов, и любой аналитический запрос по ней замедляется в разы.
Источники
Источники и редакционная политика
Материалы RecallDeck сопоставлены с официальной документацией и открытыми публикациями компаний, когда первичный источник доступен. Мы не связаны с упомянутыми работодателями, не публикуем конфиденциальные задания и не продаём места в подборках. Формат найма может меняться — уточняйте его у рекрутера.
От чтения к воспроизведению
Отрепетируйте полный цикл интервью.
RecallDeck возвращает сложные темы по расписанию и помогает удерживать в памяти язык, SQL, архитектуру и поведенческие истории.