Форматы файлов и хранение данных
Формат хранения решает, сколько запрос прочитает с диска. Собес проверяет, понимаешь ли ты, почему parquet - стандарт аналитики, а CSV в озере - только в зоне приёма.
Стержень: колоночный формат читает нужные колонки и скипает блоки по статистикам; строчные и текстовые форматы - для обмена, не для хранения аналитики.
// Формулировки: «почему parquet, а не CSV?», «что такое predicate pushdown?», «какой формат сплиттабельный?».
Parquet, ORC, Avro
Parquet - колоночный стандарт аналитики: читает только избранные колонки, сжимает по колонкам (однотипные данные жмутся лучше) и хранит статистики min/max в футере, чтобы пропускать целые row groups при чтении.
ORC - колоночный аналог из мира Hive (stripes, индексы, ACID в Hive); в Spark-стеке дефолт parquet, в Hive-наследии встречается ORC. Avro - строчный формат со схемой в файле: удобен для поточной записи и эволюции схемы, стандарт обмена для Kafka, но для аналитики проигрывает колоночным.
// Row group - единица чтения parquet (обычно 128 МБ): predicate pushdown скипает группы по статистикам, а сортировка данных по фильтруемой колонке усиливает этот скип.
- row group
- блок строк parquet со статистиками по колонкам
- predicate pushdown
- пропуск блоков по min/max при чтении
- ACID
- atomicity, consistency, isolation, durability
Текст, сжатие, сплиттабельность
CSV и JSON - форматы обмена, а не хранения: нет типов и схемы, нет колоночного сжатия, парсинг дорог. В озере им место только в landing-зоне, где данные лежат как пришли.
Сжатие влияет на параллелизм: snappy и zstd в parquet сплиттабельны (жмут внутри блоков), поэтому файл читается кусками параллельно. А gzip-файл целиком не сплиттабелен - его читает одна таска, и параллелизм теряется.
// Отсюда двойная беда гигантского gzip-CSV: и парсинг текста дорог, и весь файл жуёт один поток. Аналитику держат в parquet.
- splittable
- файл можно читать параллельно кусками
- landing zone
- зона приёма сырых файлов как пришли
Размер файла и партиции-каталоги
Размер файла держат в диапазоне 128 МБ – 1 ГБ. Мелкие файлы - оверхед метаданных и тасков (тысяча файлов = тысяча задач на листинг и открытие); гигантские несплиттабельные - потеря параллелизма.
Партиционирование каталогами (dt=2026-07-08/) даёт pruning уже на уровне листинга директорий - запрос за дату не листит чужие папки.
// Но высококардинальный ключ в каталогах-партициях (user_id) убивает файловую систему миллионами директорий. Партиционируют по низкокардинальным осям вроде даты, а стриминг мелкими файлами регулярно компактят.
- каталог-партиция
- dt=.../ - pruning на уровне листинга директорий
- compaction
- склейка мелких файлов стриминга в крупные
Как отвечать: «Почему parquet, а не CSV для аналитики?»
Потому что аналитические запросы читают несколько колонок из широкой таблицы и фильтруют по диапазонам, и колоночный parquet ровно под это заточен. Он хранит данные по колонкам, поэтому запрос читает с диска только нужные поля, а не всю строку. Однотипные данные в колонке жмутся в разы лучше, чем вперемешку. И у него есть статистики min/max по row group в футере, так что при фильтре движок пропускает целые блоки, не читая их, это predicate pushdown. CSV всего этого лишён: нет схемы и типов, значит каждый запрос парсит текст, читает весь файл целиком, колонки не отделить и толком не сжать. Плюс большой gzip-CSV ещё и не сплиттабелен - его жуёт одна таска. Поэтому CSV я держу только в landing-зоне как формат приёма, а для хранения и чтения аналитики - parquet.
Почему это сильный ответ: три конкретных преимущества parquet (колоночное чтение, сжатие, pushdown по статистикам) против недостатков CSV (парсинг, полное чтение, несплиттабельность) с правильной ролью CSV - приёмная зона.
На чём валят
- −Хранить аналитику в CSV - платишь парсингом и полным чтением каждый запрос.
- −Гигантский gzip-CSV - весь файл читает одна таска.
- −Партиционировать каталоги по user_id - файловая система умирает от директорий.
- −Писать parquet россыпью килобайтных файлов из стриминга без компакции.
- −Менять типы колонок между записями без плана эволюции схемы - читатель падает на merge схем.
Проверьте себя
Пять вопросов из банка по этой подтеме. Всего их 12, остальные разбираются в тренажёре.
- Что такое predicate pushdown и partition pruning при чтении данных?A)Фильтр применяется при чтении: лишние партиции и колонки не читаютсяB)Spark сначала читает весь датасет целиком, а фильтр применяет в концеC)Pushdown ускоряет запись данных, но не их чтениеD)Partition pruning работает для строковых форматов вроде CSV
показать ответ и разбор
+A)Фильтр применяется при чтении: лишние партиции и колонки не читаются// разбор: Движок «проталкивает» условие фильтра к источнику: по статистике Parquet и структуре партиций он пропускает целые файлы, row-группы и колонки ещё до загрузки в память. Если данные партиционированы по колонке, по которой фильтруешь, читается лишь нужная партиция — это часто важнее самого движка вычислений.
- Что такое проблема мелких файлов (small files problem)?A)Множество мелких файлов читается быстрее, чем несколько крупныхB)Тысячи крошечных файлов = оверхед открытия и раздутые метаданныеC)Размер и число файлов вообще не влияют на скорость распределённого чтенияD)Проблема мелких файлов есть в реляционных СУБД, но не в Spark
показать ответ и разбор
+B)Тысячи крошечных файлов = оверхед открытия и раздутые метаданные// разбор: Каждый файл — это задача на планирование, открытие и запись метаданных. Тысячи крошечных файлов (типичный результат частых стриминговых записей или переизбытка партиций) забивают планировщик и namenode метаданными, и джоба тратит время на оверхед, а не на счёт. Лечение — периодическая компакция мелких файлов в крупные.
- Чем bucketing отличается от партиционирования при хранении данных?A)Bucketing и партиционирование — это два близких названия одного приёмаB)Партиционирование раскладывает по хешу в фиксированное число корзин, а bucketing — по значению в папкиC)Bucketing применим к текстовым CSV-файлам, но не к ParquetD)Партиции — папки по значению колонки; бакеты — фиксированное число корзин по хешу (ускоряет join)
показать ответ и разбор
+D)Партиции — папки по значению колонки; бакеты — фиксированное число корзин по хешу (ускоряет join)// разбор: Партиционирование кладёт данные в папки по значению колонки (хорошо для фильтрации, но плодит папки при высокой кардинальности). Bucketing распределяет данные по фиксированному числу корзин по хешу ключа. Если обе таблицы забакетить по ключу join одинаково, соединение по нему не требует shuffle — записи с одним ключом уже лежат в парных бакетах.
- Чем ORC похож на Parquet и когда что выбирают?A)ORC — строковый (row) формат, а Parquet — колоночный, отсюда и вся разница в скорости между нимиB)Parquet не поддерживает сжатие и статистику блоков, тогда как ORC поддерживает и то, и другоеC)Оба колоночные, сжатые, со статистикой и pushdown; выбор чаще по экосистеме (ORC — мир Hive, Parquet — Spark/шире)D)ORC и Parquet идентичны во всём, это по сути один формат под двумя именами
показать ответ и разбор
+C)Оба колоночные, сжатые, со статистикой и pushdown; выбор чаще по экосистеме (ORC — мир Hive, Parquet — Spark/шире)// разбор: ORC и Parquet — оба колоночные форматы со сжатием, встроенной статистикой (min/max по блокам для skipping), поддержкой вложенных типов и predicate pushdown; по характеристикам они близки, и для аналитики оба сильно лучше строкового CSV. Различия в основном экосистемные и по нюансам: ORC исторически тесно связан с Hive/Hadoop-стеком и иногда чуть лучше жмёт, Parquet — фактический стандарт в Spark и шире поддержан инструментами/облаком. На практике выбор диктует окружение и совместимость, а не радикальная разница в скорости.
- Когда выбирают строковый Avro вместо колоночного Parquet?A)Avro колоночный, поэтому он быстрее Parquet на аналитических запросах по колонкамB)Avro не поддерживает схему, поэтому его берут для временных данных без структурыC)Avro применяют для сжатия готовых Parquet-файлов перед архивацией на дискD)Когда пишут/читают запись целиком и важна эволюция схемы: стриминг, обмен сообщениями, сериализация — там Avro (row) удобнее
показать ответ и разбор
+D)Когда пишут/читают запись целиком и важна эволюция схемы: стриминг, обмен сообщениями, сериализация — там Avro (row) удобнее// разбор: Avro — строковый (row-oriented) бинарный формат со схемой и хорошей поддержкой её эволюции (добавление/удаление полей с дефолтами). Он выигрывает там, где работают с записью целиком и много пишут: потоковая передача (Kafka + Schema Registry обычно на Avro/Protobuf), обмен сообщениями, сериализация событий, write-heavy загрузка. Колоночный Parquet, наоборот, оптимален для аналитических чтений подмножества колонок по многим строкам. Типичный конвейер: сырьё/поток — в Avro, а для аналитики его перекладывают в Parquet.
дальше
Теорию прочитали. Навык ставится повторением
В Сеньорчике эта подтема идёт в ежедневных сессиях: движок возвращает её, пока ответы не станут уверенными, и ведёт прогресс отдельно по каждой подтеме. Теория внутри тоже бесплатна, лимит только на количество вопросов в день.