Learning Platform
Глоссарий Troubleshooting
Урок 11.02 · 22 мин
Средний
hiveacidsync-partition-metadatatransactions

Hive ACID-таблицы, sync_partition_metadata и ограничения коннектора

В прошлом уроке мы установили модель Hive: данные в директориях, метаданные в Metastore до уровня партиции. Эта простая модель имеет фундаментальное следствие — обычная Hive-таблица не поддерживает row-level изменения. Удалить одну строку нельзя. Hive со временем добавил решение — ACID-таблицы, но их механика и ограничения сильно отличаются от того, к чему привыкли по Iceberg. Этот урок разбирает транзакционные таблицы Hive, проблему рассинхронизации Metastore с файловой системой и процедуру sync_partition_metadata — а заодно объясняет, почему для новых проектов выбирают не Hive ACID, а Iceberg или Delta.

Почему обычная Hive-таблица не умеет UPDATE и DELETE

Вспомним модель. Hive-таблица — это файлы в директориях. Metastore знает директории партиций, но не знает отдельных файлов. Теперь представьте DELETE FROM events WHERE user_id = 500. Строка с этим user_id лежит где-то внутри файла на миллион строк. Чтобы её удалить, надо переписать весь файл без этой строки. А в обычной Hive-модели нет ни механизма пометить строку удалённой, ни механизма атомарно подменить файл — Metastore оперирует директориями, файлы он не отслеживает.

Поэтому обычная Hive-таблица поддерживает по сути только две операции записи: добавить данные (INSERT — кладёт новые файлы в директорию) и перезаписать целиком (INSERT OVERWRITE партиции или таблицы). Точечные UPDATE и DELETE отдельных строк она не умеет. Это прямое следствие того, что метаданные не доходят до уровня файла.

Обычная и ACID Hive-таблица: что умеет каждая
Обычная Hive-таблицаФайлы в директориях, Metastore до уровня партиции; умеет только INSERT новых файлов и INSERT OVERWRITE целиком, без row-level изменений
ACID добавляет транзакционность
Hive ACID-таблицаТранзакционная таблица: HMS отслеживает версии через base- и delta-директории, что даёт row-level UPDATE и DELETE, но требует ORC и работает медленнее

Hive ACID: транзакционные таблицы

Чтобы дать Hive row-level изменения, в него добавили ACID-таблицы (транзакционные таблицы). Идея: вместо того чтобы переписывать большие файлы, фиксировать изменения отдельно и собирать актуальное состояние при чтении.

ACID-таблица Hive хранит данные не как один набор файлов, а как base-директории и delta-директории. Base — снимок данных. Delta — директория с изменениями (вставками и удалениями) одной транзакции. Каждая транзакция получает идентификатор и пишет свою delta-директорию. При чтении движок берёт base и накладывает поверх все delta по порядку транзакций — получается актуальное состояние. Удаление строки — это запись в delta-директорию пометки «такая-то строка удалена», а не переписывание base.

Заметили параллель? Это идейно то же, что delete files в Iceberg v2 из прошлого модуля: не трогать большие файлы, а фиксировать дельту отдельно. Но реализация Hive ACID жёстче. Во-первых, она требует формата ORC — base и delta завязаны на ORC, с Parquet ACID-таблицы Hive не работают. Во-вторых, состояние транзакций отслеживает HMS, и это делает HMS ещё более критичной точкой системы.

Два типа транзакционных таблиц различаются по поддерживаемым операциям. Insert-only транзакционные таблицы умеют только атомарный INSERT. Full ACID таблицы умеют и UPDATE, и DELETE отдельных строк — но именно они требуют ORC.

-- Создать full ACID транзакционную таблицу Hive (требует ORC)
CREATE TABLE hive.ops.accounts (
  account_id BIGINT,
  status     VARCHAR,
  balance    DECIMAL(12,2)
)
WITH (
  format = 'ORC',
  transactional = true
);

INSERT INTO hive.ops.accounts VALUES
  (1, 'active', DECIMAL '1000.00'),
  (2, 'active', DECIMAL '500.00');
-- INSERT: 2 rows

-- Row-level UPDATE работает только на транзакционной ORC-таблице
UPDATE hive.ops.accounts SET status = 'frozen' WHERE account_id = 2;
-- UPDATE: 1 row

-- Row-level DELETE — тоже только на транзакционной ORC-таблице
DELETE FROM hive.ops.accounts WHERE status = 'frozen';
-- DELETE: 1 row
WARNING

Row-level UPDATE и DELETE в Hive-коннекторе Trino работают только для транзакционных таблиц формата ORC. На обычной (не транзакционной) Hive-таблице или на транзакционной таблице в Parquet эти команды не выполнятся. Если нужны row-level изменения в Parquet — это сигнал, что таблице место в формате Iceberg или Delta, а не Hive.

Проблема рассинхронизации: Metastore против файловой системы

Теперь — характерная боль Hive, которой по своей природе нет у Iceberg. Она прямо вытекает из модели «метаданные отдельно от данных».

Список партиций живёт в Metastore. Данные живут в директориях object storage. Это два независимых хранилища, и ничто не гарантирует их согласованность. Если кто-то добавит файлы в object storage в обход Trino и Hive — например, Spark-джоб напишет данные прямо в новую директорию dt=2026-05-21, или файлы зальёт сторонний пайплайн, — то на диске партиция есть, а в Metastore записи о ней нет.

Последствие: Trino не увидит эти данные. Запрос SELECT ... WHERE dt = '2026-05-21' спросит Metastore про партицию, Metastore ответит «такой партиции нет», и Trino вернёт ноль строк — хотя файлы физически лежат в S3. Данные есть, но для Trino их не существует. Бывает и обратное: партиция удалена из object storage, а запись в Metastore осталась — тогда запрос обратится к несуществующей директории.

Рассинхронизация: партиция на диске есть, в Metastore нет
Запись в обход TrinoSpark-джоб или сторонний пайплайн заливает файлы прямо в новую директорию партиции в object storage, не трогая Metastore
Metastore об этом не знает
Metastore без партицииВ Metastore нет записи о новой партиции, поэтому запрос её не видит и возвращает ноль строк, хотя данные физически есть
sync_partition_metadata чинит
СинхронизацияПроцедура сверяет директории в object storage со списком партиций в Metastore и приводит их в соответствие

sync_partition_metadata: синхронизация партиций

Чинит рассинхронизацию процедура system.sync_partition_metadata. Она сверяет фактические директории партиций в object storage со списком партиций в Metastore и приводит Metastore в соответствие.

-- Синхронизировать партиции таблицы events с файловой системой
CALL hive.system.sync_partition_metadata(
  schema_name => 'logs',
  table_name  => 'events',
  mode        => 'FULL'
);

Параметр mode задаёт направление синхронизации:

РежимЧто делает
ADDДобавляет в Metastore партиции, чьи директории есть в object storage, но записи нет
DROPУдаляет из Metastore партиции, чьих директорий в object storage больше нет
FULLИ добавляет недостающие, и удаляет лишние — полная синхронизация в обе стороны

Типичное применение: пайплайн залил в Hive-таблицу новые данные в обход Trino, и шагом после загрузки вызывается sync_partition_metadata в режиме ADD — чтобы свежие партиции стали видны. Это рабочий приём, но обратите внимание, чего он стоит: его нужно не забыть вызвать. Забыли — данные молча невидимы. У Iceberg такой проблемы нет в принципе: там нет двух независимых хранилищ метаданных, любая запись через любой движок атомарно обновляет единый слой метаданных, и синхронизировать нечего.

TIP

Родственная процедура — system.flush_metadata_cache. Hive-коннектор кэширует ответы Metastore, чтобы не дёргать его на каждый запрос. Если метаданные изменили внешним инструментом, кэш может отдавать устаревшее. flush_metadata_cache сбрасывает кэш. Разница в сути: sync_partition_metadata чинит сам Metastore, flush_metadata_cache — лишь кэш Trino поверх Metastore.

Ограничения Hive-коннектора и почему выбирают Iceberg

Соберём ограничения Hive в одну картину — это и есть мотивация, по которой в 2026 году новые таблицы делают в Iceberg или Delta, а не в Hive.

Нет настоящих снапшотов и time travel — модель Hive не хранит историю состояний. Нет атомарной фиксации на уровне таблицы как у Iceberg-снапшота: INSERT OVERWRITE не атомарен так же надёжно. Row-level UPDATE и DELETE — только для транзакционных таблиц и только в ORC. Метаданные не доходят до уровня файла, поэтому каждый запрос делает list директорий, а pruning возможен только на уровне директорий партиций, не отдельных файлов по статистике. И главная эксплуатационная боль — рассинхронизация Metastore с файловой системой, которую приходится чинить руками через sync_partition_metadata.

ВозможностьHiveIceberg
Снапшоты и time travelНетЕсть
Атомарность записи уровня таблицыСлабаяЕсть (снапшот + каталог)
Row-level UPDATE/DELETEТолько ORC, транзакционные таблицыЕсть (v2, любой формат)
Метаданные уровня файлаНет (только до партиции)Есть (манифесты)
Hidden partitioning, partition evolutionНетЕсть
Рассинхронизация с файловой системойДа, нужен sync_partition_metadataНет в принципе

Тогда зачем вообще нужен Hive-коннектор? По одной практической причине: Hive-таблиц в мире огромное количество — это десятилетие данных в продакшене. Trino должен уметь их читать. Поэтому hive — рабочий коннектор для существующих таблиц и для миграции. Но для новых таблиц выбор — Iceberg или Delta.

Hive Metastore vs Iceberg: сравнение моделей метаданных

Попробуй сам

В песочнице создайте обычную Hive-таблицу events, партиционированную по dt. Упражнение первое: попробуйте выполнить DELETE FROM events WHERE ... — зафиксируйте, что произойдёт, и объясните почему. Затем создайте таблицу accounts с transactional = true и format = 'ORC', выполните UPDATE и DELETE отдельных строк — теперь это сработает. Упражнение второе на рассинхронизацию: в обычной Hive-таблице вручную создайте в MinIO новую директорию-партицию dt=2026-05-25 и положите в неё Parquet-файл (или сымитируйте загрузкой в обход Trino). Выполните SELECT ... WHERE dt = '2026-05-25' — сколько строк вернулось? Теперь вызовите sync_partition_metadata в режиме ADD и повторите SELECT. Письменно объясните: почему до синхронизации данные были невидимы, какого класса проблема за этим стоит, и почему у Iceberg-таблицы этой проблемы не возникает в принципе.


Проверка знанийKnowledge check
Почему обычная Hive-таблица не поддерживает row-level UPDATE и DELETE, как это решают ACID-таблицы Hive, и какая эксплуатационная проблема возникает из-за разделения метаданных и данных?
ОтветAnswer
Обычная Hive-таблица не поддерживает row-level UPDATE и DELETE, потому что её метаданные не доходят до уровня файла: Metastore знает директории партиций, но не отслеживает отдельные файлы, и нет механизма ни пометить строку удалённой, ни атомарно подменить файл. Чтобы удалить одну строку из файла на миллион строк, пришлось бы переписать весь файл, а такого механизма в обычной модели нет. Поэтому обычная Hive-таблица умеет только INSERT новых файлов и INSERT OVERWRITE целиком. ACID-таблицы Hive решают это, храня данные как base-директории и delta-директории: base — снимок, delta — изменения одной транзакции, и при чтении движок накладывает delta поверх base по порядку транзакций. Удаление строки становится записью пометки в delta, а не переписыванием base — идейно это как delete files в Iceberg v2. Но реализация Hive ACID жёстче: full ACID таблицы с UPDATE и DELETE требуют формата ORC, с Parquet не работают, а состояние транзакций отслеживает HMS, что делает его ещё более критичным. Эксплуатационная проблема из-за разделения метаданных и данных — рассинхронизация: список партиций живёт в Metastore, а данные в директориях object storage, это два независимых хранилища без гарантии согласованности. Если файлы добавить в обход Trino и Hive, на диске партиция есть, а в Metastore записи нет, и запрос вернёт ноль строк, хотя данные физически лежат в storage. Чинит это процедура sync_partition_metadata, сверяющая директории со списком партиций в Metastore в режимах ADD, DROP или FULL. Минус в том, что её нужно не забыть вызвать. У Iceberg такой проблемы нет: там нет двух независимых хранилищ метаданных, любая запись атомарно обновляет единый слой метаданных.

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

Результат: 0 из 0
Концептуальный
Вопрос 1 из 4. Почему обычная (не транзакционная) Hive-таблица не поддерживает row-level DELETE отдельных строк?

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

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

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

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