Learning Platform
Глоссарий Troubleshooting
Урок 11.01 · 23 мин
Средний
hivehive-metastorepartitionsbuckets

Hive connector: HMS и Glue, ORC и Parquet, партиции и бакеты

В прошлом модуле мы детально разобрали Iceberg — современный формат таблиц. Этот модуль закрывает остальные форматы lakehouse, и начать логично с самого старого — Hive. Hive — это не просто «ещё один коннектор»: понимание его модели объясняет, почему Iceberg и Delta устроены именно так, какие проблемы они исправляют, и почему в продакшене 2026 года всё ещё полно Hive-таблиц. Этот урок разбирает анатомию Hive-таблицы, роль Metastore, форматы файлов и механику партиций и бакетов в Trino.

Что такое Hive-таблица: метаданные отдельно от данных

Hive появился в эпоху Hadoop как способ дать SQL поверх файлов в HDFS. Его модель проще модели Iceberg, и важно её ухватить, потому что Trino-коннектор hive работает именно с ней.

Hive-таблица состоит из двух разделённых частей. Первая — данные: файлы в директории object storage или HDFS. Просто файлы в папке, без слоя метаданных рядом с ними. Вторая — метаданные в Hive Metastore: отдельный сервис, который хранит описание таблицы — её схему, формат файлов, расположение (LOCATION) и список партиций.

Ключевое отличие от Iceberg вытекает прямо отсюда. У Iceberg метаданные — это дерево файлов рядом с данными, и в нём перечислен каждый файл данных поимённо со статистикой. У Hive метаданные в Metastore описывают таблицу до уровня директории партиции, но не до уровня отдельного файла.

Apache Iceberg: иерархия метаданных vs Hive Metastore Metastore знает: «партиция dt=2026-05-19 лежит в такой-то директории». Какие конкретно файлы внутри этой директории — Metastore не знает. Чтобы это выяснить, Trino делает list директории во время запроса.

Hive-таблица: метаданные в Metastore, данные в директориях
Hive MetastoreОтдельный сервис: хранит схему таблицы, формат файлов, LOCATION и список партиций с их директориями — но не список отдельных файлов
указывает на директорию
Директория партицииПапка в object storage или HDFS, например dt=2026-05-19; Trino делает list этой папки во время запроса, чтобы узнать файлы
list во время запроса
Файлы данныхORC или Parquet файлы внутри директории партиции; именно их Trino читает, узнав о них через list

Это «list во время запроса» — корень многих особенностей Hive, к которым мы вернёмся. А пока запомните формулу: Hive = Metastore (схема и партиции) + директории с файлами. Никакого слоя метаданных уровня файла, никаких снапшотов.

Metastore: HMS или AWS Glue

Коннектору hive обязательно нужен Metastore — иначе негде взять схему таблицы. Trino поддерживает два варианта.

Hive Metastore Service (HMS) — классический. Это отдельный Java-сервис с реляционной БД (PostgreSQL/MySQL) под капотом, общающийся по Thrift. В конфигурации каталога Trino — hive.metastore=thrift и hive.metastore.uri=thrift://.... HMS проверен годами, его понимают все движки экосистемы Hadoop. Минус — это лишний сервис, который надо разворачивать и обслуживать.

AWS Glue Data Catalog — managed-аналог от AWS. Тот же реестр схем и партиций, но как сервис AWS, который не нужно поднимать самому. В конфигурации — hive.metastore=glue. Естественный выбор, если инфраструктура в AWS.

# etc/catalog/hive.properties — вариант с HMS
connector.name=hive
hive.metastore=thrift
hive.metastore.uri=thrift://hms:9083
fs.native-s3.enabled=true
s3.endpoint=http://minio:9000
s3.path-style-access=true

Заметьте: это тот же HMS, что используется как один из вариантов каталога для Iceberg в прошлом модуле. Один сервис Metastore может обслуживать и Hive-таблицы, и Iceberg-таблицы — но это разные форматы таблиц, и записи в Metastore у них устроены по-разному.

WARNING

В актуальных релизах Trino из коннекторов удалён legacy-слой доступа к object storage (старые Hadoop-based реализации). Используется только нативная файловая система Trino — fs.native-s3.enabled для S3 и S3-совместимых хранилищ, и аналогичные свойства для Azure и GCS. Старые конфигурации с Hadoop-плагинами в свежем Trino не работают — это одно из ломающих изменений последних версий.

Форматы файлов: ORC и Parquet

Hive исторически поддерживает много форматов файлов — ORC, Parquet, Avro, RCFile, SequenceFile, JSON, CSV, текстовые. Но для аналитики в Trino реально важны два колоночных: ORC и Parquet. Текстовые и CSV-форматы строчные, без сжатия и статистики — для больших таблиц они непригодны, годятся лишь для приёма сырых данных.

ORC и Parquet — оба колоночные, оба сжимают данные по столбцам и хранят внутреннюю статистику. Parquet — фактический отраслевой стандарт, его использует почти вся экосистема (Spark, Iceberg, Delta по умолчанию). ORC исторически глубоко связан с Hive и нужен для одной конкретной вещи — ACID-таблиц Hive, о которых речь в следующем уроке.

Формат файлов Hive-таблицы задаётся при создании:

CREATE TABLE hive.logs.events (
  event_id BIGINT,
  user_id  BIGINT,
  amount   DECIMAL(12,2),
  dt       DATE
)
WITH (
  format = 'PARQUET',
  partitioned_by = ARRAY['dt']
);

Важная деталь о статистике. Колоночные форматы хранят статистику внутри файлов — min/max по row group. Trino использует её для пропуска row group при чтении уже открытого файла. Но это статистика уровня файла, и чтобы Trino её увидел, файл нужно открыть. Статистика уровня таблицы для cost-based optimizer (общее число строк, NDV) у Hive живёт в Metastore и обновляется отдельно — командой ANALYZE. Hive исторически не предоставляет некоторые виды статистики так полно, как Iceberg, — это одна из причин, по которой CBO на Hive-таблицах работает менее точно.

Партиции в Hive: директория = значение колонки

Партиция в Hive — это физическая директория, имя которой кодирует значение партиционирующей колонки. Таблица из примера выше, партиционированная по dt, разложится в object storage так:

s3://lake/logs/events/
  dt=2026-05-18/   00001.parquet  00002.parquet
  dt=2026-05-19/   00001.parquet  00002.parquet
  dt=2026-05-20/   00001.parquet

Партиционирующая колонка dt — это отдельная колонка в схеме, и её значение не хранится в самих файлах: оно закодировано в имени директории. Это и есть та модель, против которой проектировали hidden partitioning Iceberg (вспомните прошлый модуль). Запрос обязан фильтровать именно по партиционирующей колонке:

-- Фильтр по dt: Metastore знает директории партиций,
-- Trino делает list ТОЛЬКО нужных директорий — это partition pruning
SELECT count(*) FROM hive.logs.events
WHERE dt = DATE '2026-05-19';
--  _col0
-- -------
--   48210

Здесь работает partition pruning. Metastore хранит список партиций с их директориями. Получив WHERE dt = '2026-05-19', Trino спрашивает Metastore про подходящие партиции и делает list только их директорий — папки dt=2026-05-18 и dt=2026-05-20 вообще не трогаются. Если же фильтра по dt нет, Trino вынужден перечислить все партиции и сделать list всех директорий — на таблице с десятками тысяч партиций одно только планирование станет дорогим.

TIP

Если запрос к большой Hive-таблице неожиданно медленный на этапе планирования — проверьте, есть ли в нём фильтр по партиционирующей колонке. Без него Trino перечисляет все партиции и делает list всех директорий. Это самая частая причина медленных запросов к Hive: не объём данных, а перечисление партиций.

Бакеты: предсказуемое разбиение внутри партиции

Партиции хорошо работают для колонок с умеренным числом значений — дата, регион. Для колонки высокой кардинальности вроде user_id партиции не годятся: миллионы значений дали бы миллионы директорий. Hive решает это бакетингом (bucketing).

Бакетинг разбивает данные по хешу колонки на фиксированное число файлов-бакетов. Таблица с bucketed_by = ARRAY['user_id'] и bucket_count = 32 раскладывает строки по 32 бакетам: бакет строки определяется как хеш user_id по модулю 32. Число бакетов фиксировано и предсказуемо.

CREATE TABLE hive.logs.events_bucketed (
  event_id BIGINT,
  user_id  BIGINT,
  amount   DECIMAL(12,2),
  dt       DATE
)
WITH (
  format = 'PARQUET',
  partitioned_by = ARRAY['dt'],
  bucketed_by = ARRAY['user_id'],
  bucket_count = 32
);

Бакетинг даёт два выигрыша. Первый — bucket pruning: запрос WHERE user_id = 12345 позволяет Trino вычислить хеш и прочитать только один из 32 бакетов вместо всех. Второй — ускорение join: если две таблицы забакетены по ключу join одинаково, соответствующие строки уже лежат в соответствующих бакетах, и join можно сделать без дорогого перераспределения данных по сети.

Партиции и бакеты: два уровня разбиения Hive-таблицы
Партиция по dtФизическая директория на каждое значение даты; partition pruning отбрасывает целые директории по фильтру на dt
внутри партиции — бакеты
Бакеты по hash(user_id)Фиксированное число файлов внутри партиции; bucket pruning читает только нужный бакет, одинаковый бакетинг ускоряет join

Партиции и бакеты дополняют друг друга: партиции отсекают по колонке умеренной кардинальности (дата), бакеты — упорядочивают данные по колонке высокой кардинальности (id) внутри каждой партиции. Это аналог day(ts) плюс bucket(id, N) из мира Iceberg, только в Hive — на уровне физических директорий и файлов, а не метаданных.

Попробуй сам

В песочнице курса (Trino + HMS + MinIO) создайте Hive-таблицу events, партиционированную по dt, в формате Parquet. Загрузите данные за три-четыре разных дня. Упражнение первое: выполните SELECT count(*) с фильтром по одному dt, оберните в EXPLAIN, найдите в выводе число затронутых партиций — должна быть одна. Затем выполните тот же запрос без фильтра по dt и сравните. Упражнение второе: посмотрите, как таблица разложена в MinIO — найдите директории вида dt=... и убедитесь, что значение dt в файлах не хранится, оно только в имени папки. Упражнение третье, концептуальное: создайте таблицу с bucketed_by по user_id и bucket_count = 16, загрузите данные. Письменно объясните: чем partition pruning по dt принципиально отличается от bucket pruning по user_id, и почему для user_id нельзя было просто использовать партиционирование.


Проверка знанийKnowledge check
Как устроена Hive-таблица, чем её модель метаданных принципиально отличается от Iceberg, и в чём разница между партициями и бакетами?
ОтветAnswer
Hive-таблица состоит из двух разделённых частей: данные — это просто файлы в директориях object storage или HDFS, без слоя метаданных рядом с ними, а метаданные хранит отдельный сервис Hive Metastore — схему таблицы, формат файлов, LOCATION и список партиций. Принципиальное отличие от Iceberg: метаданные Hive в Metastore описывают таблицу только до уровня директории партиции, но не до уровня отдельного файла. Metastore знает, в какой директории лежит партиция, но не знает, какие конкретно файлы внутри неё — чтобы это выяснить, Trino делает list директории во время запроса. У Iceberg, наоборот, дерево метаданных перечисляет каждый файл данных поимённо со статистикой, и есть снапшоты. Партиция в Hive — это физическая директория, имя которой кодирует значение партиционирующей колонки; само значение в файлах не хранится, оно только в имени папки, и запрос обязан фильтровать по партиционирующей колонке, чтобы сработал partition pruning — Trino спросит Metastore про нужные партиции и сделает list только их директорий. Партиции подходят для колонок умеренной кардинальности вроде даты. Бакетинг — это разбиение данных внутри партиции по хешу колонки на фиксированное число файлов-бакетов; он нужен для колонок высокой кардинальности вроде user_id, где партиции дали бы миллионы директорий. Бакетинг даёт bucket pruning — чтение только нужного бакета по вычисленному хешу — и ускоряет join одинаково забакетенных таблиц, позволяя обойтись без перераспределения данных по сети. Партиции и бакеты дополняют друг друга как два уровня разбиения.

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

Результат: 0 из 0
Концептуальный
Вопрос 1 из 4. Чем модель метаданных Hive-таблицы принципиально отличается от модели Iceberg?

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

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

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

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