Метаданные и схема
Метаданные — скрытая суперсила форматов
Разница между CSV и Parquet — не только в columnar layout и компрессии. Главное преимущество modern formats — встроенные метаданные, которые позволяют движку пропускать данные, не читая их.
CSV-файл — это поток байтов. Чтобы найти нужные данные, нужно прочитать весь файл. Parquet-файл — это структура с оглавлением, статистиками и схемой. Движок может решить, какие блоки читать, до обращения к данным.
Анатомия метаданных
Magic Number (PAR1) + Footer Length
Последние 4 байта — magic number 'PAR1'. Перед ним — длина footer. Движок сначала читает хвост файла, потом принимает решение.Обратите внимание: footer — в конце файла. Это позволяет записывать данные streaming-образом (append row groups), а метаданные дописать один раз в конце.
Как работает predicate pushdown
Predicate pushdown — механизм, который использует метаданные для пропуска целых блоков данных.
Footer: min/max статистики по row groups
Footer содержит min/max для каждой колонки в каждом row group. Одно чтение в несколько KB.Прочитан 1 из 3 row groups (экономия 66% I/O)
Из 3 row groups прочитан только 1 — экономия 66% I/O. На больших файлах с десятками row groups экономия может достигать 90%+.Predicate pushdown работает тем лучше, чем больше данные отсортированы по колонке фильтрации. Если salary отсортирован, row groups содержат непересекающиеся диапазоны — pruning пропускает максимум блоков.
Bloom Filters
Min/max статистики не помогают для equality-запросов на колонках с высокой кардинальностью:
SELECT * FROM events WHERE user_id = 'abc-123-def'
Min/max для user_id: min='aaa...', max='zzz...' — бесполезно, любой row group может содержать этот ID.
Bloom filter — вероятностная структура данных, которая отвечает на вопрос “содержит ли этот блок значение X?”:
- “Нет” — гарантированно нет, блок можно пропустить
- “Возможно да” — нужно проверить (может быть false positive)
Читаем только RG 2 (экономия 66% I/O)
Из 3 row groups читаем только 1. Bloom filter занимает ~10 KB на row group — ничтожный overhead.Bloom filters не включены по умолчанию — их нужно явно включить при записи для конкретных колонок. Добавляют ~10 KB overhead на row group per column, но экономят гигабайты I/O на point lookups.
Встроенная схема vs внешний реестр
Форматы хранят информацию о типах данных двумя способами:
| Подход | Примеры | Плюсы | Минусы |
|---|---|---|---|
| Встроенная схема | Parquet, ORC, Avro | Файл самодостаточен, любой может прочитать | Schema evolution сложнее |
| Внешний реестр | Kafka + Schema Registry, Hive Metastore | Централизованное управление, валидация | Зависимость от реестра |
| Без схемы | CSV, JSON | Гибкость | Schema drift, нет валидации |
Встроенная (Parquet/ORC)
Reader: самодостаточное чтение
Любой инструмент (DuckDB, Spark, pandas) может прочитать файл и сразу знает типы колонок.Внешний (Schema Registry)
Schema Registry (HTTP API)
Reader обращается к Schema Registry по сети. Без реестра — данные нечитаемы.Reader: чтение с зависимостью
После получения схемы из реестра — данные можно десериализовать. Зависимость от внешнего сервиса.Зачем всё это нужно: полная картина
Объединим все метаданные в один read path:
Читать только: name, salary, department (3 из 50)
Из 50 колонок читаем только 3: name, salary, department. Остальные 47 не загружаются с диска.Пропустить row groups без подходящих salary/department
Проверяем salary max и department values по каждому row group. Пропускаем row groups, где нет подходящих записей.Пропустить страницы с неподходящими min/max
Внутри выбранных row groups — page-level статистики позволяют пропустить ещё часть страниц.Результат: 1% данных прочитано с диска
Из терабайта данных реально с диска прочитано несколько мегабайт. Всё остальное отсечено метаданными.Метаданные — не магия. Они работают только если данные организованы: отсортированы, партиционированы, записаны с разумным размером row group. На случайно записанных данных min/max перекрываются и pruning не срабатывает.
Ключевые выводы
- Footer — хранит схему, статистики, offsets. Первое, что читает движок. Находится в конце файла.
- Min/max статистики — позволяют пропускать целые row groups (predicate pushdown). Эффективны на отсортированных данных.
- Bloom filters — вероятностная проверка для equality-запросов. Компенсируют ограничения min/max на колонках с высокой кардинальностью.
- Встроенная схема — файл самодостаточен, не нужен внешний реестр. Parquet, ORC, Avro — все используют.
- Metadata-driven read — комбинация column pruning + row group pruning + bloom filters + page pruning позволяет читать 1–5% данных вместо 100%.
- Сортировка данных — ключ к эффективности метаданных. Без сортировки min/max перекрываются, pruning не работает.