[Перевод] Iceberg без Spark для каждой мелочи: UPDATE, DELETE и MERGE INTO из одного SQL-движка в Apache Doris 4.1

Страницы:  1

Ответить
 

Professor Seleznov


Атрибуция: Этот материал является переводом и адаптацией статьи Apache Doris 4.1 on Iceberg V3: Running the Full Lakehouse Lifecycle from One SQL Engine за авторством Mingyu Chen (Rayner), опубликованной по лицензии CC BY 4.0. Адаптация выполнена для русскоязычной аудитории Хабра с дополнительным контекстом по архитектурным компромиссам.
-Сегодня поговорим про Iceberg V3 и Apache Doris 4.1. Но не в формате «у нас теперь есть поддержка новой версии формата, хлопаем в ладоши». Гораздо интереснее другой вопрос: что меняется, когда SQL-движок умеет не только читать Iceberg-таблицу, но и делать маленькие исправления, reconciliation, maintenance и диагностику без отдельного Spark-кластера на каждое телодвижение?
Спойлер: Spark никто не отменял. Flink тоже. Но есть целый класс ежедневной работы, где переключение движка дороже самой операции.
Что вы узнаете из этой статьи
  • UPDATE на Iceberg без Spark job. Doris 4.1 делает UPDATE, DELETE, MERGE INTO на Iceberg-таблицах из того же SQL-клиента, где вы уже нашли проблему. Цикл «query → fix → verify» сжимается с 14 часов до минут.
  • Deletion Vectors вместо Position Delete files. В Iceberg V3 DML больше не копит отдельные parquet delete files при каждом commit. Один Puffin bitmap вместо 320 файлов. Query latency перестаёт деградировать с ростом delete ratio (бенчмарк: 2–3x ускорение).
  • Row Lineage как CDC watermark. _last_updated_sequence_number двигается только при реальном DML, а не при compaction. Downstream больше не переобрабатывает строки после rewrite_data_files. Один integer watermark вместо snapshot diff.
  • Doris не заменяет Spark. Heavy ETL, backfills и streaming ingestion остаются за Spark/Flink. Doris забирает adjacent workflow: small DML, incremental reconciliation, maintenance и диагностику.
Хотите сразу попробовать? Переходите в Часть 8: Quick Start там путь от нуля до V3-таблицы за пять минут.
-
Часть 1: 3 часа ночи, одна строка сломана
Представим конкретную ситуацию.
Data engineer в e-commerce компании сидит в Doris и разбирает Iceberg-таблицу с заказами. Видит, что вчерашний batch из ERP проставил status = 'cancelled' для 200 заказов, которые на самом деле были оплачены — upstream-система отправила кривой snapshot. Запрос подтверждает: вот они, 200 строк с неверным статусом. Исправление - банальный WHERE order_id IN (...) и SET status = 'paid'.
В идеальном мире это выглядит так:
UPDATE orders_iceberg
SET status = 'paid'
WHERE order_id IN (SELECT order_id FROM bad_batch_ids);
Что происходит дальше в реальном мире — красиво описано в оригинальной статье Mingyu Chen. Инженер закрывает SQL-клиент и начинает другой рабочий процесс:
  • Пишет Spark job.
  • Подстраивает scheduler.
  • Открывает PR.
  • Ждёт review.
  • Чаще всего — передаёт задачу платформенной команде, потому что у него нет прямого доступа к write-кластеру.
  • Утром возвращается в Doris и проверяет, что всё отработало.
Полный цикл — 14 часов. Код по смыслу — один UPDATE.
Есть и менее драматичный, но более частый сценарий. CDC-поток пишет в Iceberg, small files и delete files копятся, запросы постепенно деградируют. Все понимают, что нужен rewrite_data_files.
В командах с self-service инфраструктурой инженер запустит compaction сам. Но во многих средних и крупных организациях write-доступ к Iceberg-таблицам живёт у выделенной platform team — и тогда задача, которая по сути один SQL-вызов, превращается в ticket, maintenance window и ожидание. Именно этот организационный паттерн описывает автор оригинальной статьи, и он встречается чаще, чем хотелось бы.
Iceberg решил огромную часть проблемы: он сделал таблицу открытым transactional-форматом поверх файлов в object storage. Но работа с этой таблицей часто остаётся не такой уж открытой:
  • читаем в одном движке;
  • пишем в другом;
  • compaction и maintenance делаем через третий;
  • CDC и streaming ingestion живут отдельно.
Получается два налога на каждое маленькое действие: switch-engine tax и cross-team tax. Apache Doris 4.1 пытается уменьшить оба.
-
Часть 2: Где Doris находится в Iceberg-экосистеме
Важно сразу снять лишнее ожидание: Doris не пытается заменить Spark во всех задачах.
Apache Doris — это MPP SQL engine, который начинал как data warehouse, а за последние два мажорных релиза сместил центр тяжести в сторону lakehouse workloads. Spark при этом остаётся нормальным инструментом для cross-source backfills, тяжёлого ETL, больших batch-job’ов и широкой batch/streaming-экосистемы. Flink остаётся естественным выбором для streaming ingestion и continuous CDC write paths.
Роль Doris в этом контексте узкая и конкретная: real-time query layer для Iceberg, который постепенно забирает соседний workflow:
  • вы уже пришли в SQL-клиент с запросом;
  • увидели проблему или расхождение;
  • хотите сделать небольшое исправление;
  • хотите запустить incremental reconciliation;
  • хотите выполнить maintenance или диагностику;
  • не хотите ради этого прыгать в другой кластер.
В Doris 4.1 матрица Iceberg-поддержки выглядит довольно широко:
Зона Что поддерживается
Read V1/V2/V3, time travel, branch/tag, views, system tables, Position/Equality/DV delete
Write INSERT, OVERWRITE, CTAS, INSERT INTO BRANCH, DELETE, UPDATE, MERGE INTO
DDL create/drop table, schema change, partition evolution, branch/tag management
Maintain rewrite_data_files, expire_snapshots, rewrite_manifests, rollback, fast_forward
Diagnose data file distribution, dangling delete

pic
Apache Doris 4.1 & Iceberg V3 Lakehouse Lifecycle
Архитектура: один SQL-движок для Read, Write, DDL, Maintenance и Diagnostics на Iceberg V3 таблицах. Источник: оригинальная статья Mingyu Chen.
Но в этой статье нас интересует не вся матрица. Фокус на двух вещах:
  • DML completeness: UPDATE, DELETE, MERGE INTO прямо из SQL-клиента.
  • Iceberg V3 mechanics: Deletion Vectors и Row Lineage, без которых DML быстро начинает копить технический долг.

-
Часть 3: DML возвращается в query layer
Начнём с простого.
Исправить одну строку:
UPDATE iceberg_tbl
SET name = 'Alice-fixed'
WHERE id = 1;
Откатить плохой batch:
DELETE FROM iceberg_tbl
WHERE dt = '2026-04-01'
AND source = 'bad_pipeline';
Сделать upsert по incremental batch:
MERGE INTO iceberg_tbl t
USING (
SELECT 1 AS id, 'Alice_new' AS name, 26 AS age, 'U' AS flag
UNION ALL SELECT 2, 'Bob', 30, 'D'
UNION ALL SELECT 4, 'Dora', 28, 'I'
) s
ON t.id = s.id
WHEN MATCHED AND s.flag = 'D' THEN DELETE
WHEN MATCHED THEN UPDATE SET name = s.name, age = s.age
WHEN NOT MATCHED THEN INSERT (id, name, age) VALUES (s.id, s.name, s.age);
MERGE INTO здесь самая важная операция. Именно на ней держатся типичные CDC/upsert-сценарии:
  • изменения из PostgreSQL или MySQL прилетают через Flink CDC или похожий инструмент;
  • downstream Iceberg-таблица должна принять inserts, updates и deletes вместе;
  • incremental materialized views должны обновлять wide tables по change key.
Без MERGE INTO вы либо пересобираете данные целиком, либо уносите логику в Spark/Flink и вручную обслуживаете отдельный pipeline.
В Doris 4.1 MERGE INTO поддерживает partitioned targets, subqueries как source и expressions в UPDATE-части, например age = age * 2 + 1.
Но caveat сразу: target table должен быть format-version >= 2, а сам MERGE INTO в оригинальной статье помечен как experimental. Это не «сразу катим в прод в пятницу вечером», а «делаем POC на своём workload».
-
Часть 4: Почему DML без V3 быстро копит долги
Может показаться, что раз Doris теперь умеет UPDATE, DELETE, MERGE INTO на Iceberg — задача решена.
Не совсем. Наличие DML — это ещё не хороший DML.
В Iceberg V2 каждая такая операция создаёт Position Delete file. Это отдельный parquet-файл, в котором записано: «вот эти позиции из data file нужно считать удалёнными». Чтобы вернуть корректный результат, reader вынужден выполнять anti-join: прочитать data files, прочитать все связанные delete files, отфильтровать помеченные строки.
Один delete file — ничего страшного. Десять — терпимо. Сотни и тысячи маленьких DML commits в CDC-таблице — уже неприятно.
Проблема растёт линейно:
  • каждый DML commit добавляет новые delete files;
  • каждый последующий query вынужден открывать и объединять все релевантные delete files;
  • периодически нужен rewrite_data_files, чтобы вернуть таблицу в нормальное состояние;
  • а значит, снова maintenance job, scheduler и платформа — тот самый Jira-ticket из начала статьи.
Это первый долг: performance debt. DML вроде есть, но каждый commit медленно ухудшает чтение.
Есть второй долг: observability debt. После того как commit произошёл, downstream-системы больше не знают, когда конкретная строка была последний раз изменена. Они полагаются на snapshot diff. Но compaction и rewrite_data_files тоже создают новые snapshots, хотя данные логически не менялись.
В результате downstream pipeline видит физический rewrite и интерпретирует его как новые данные — и начинает переобрабатывать строки, которых никто не трогал.
Если коротко:
DML без V3 быстро превращается в два долга: performance debt и observability debt.
Iceberg V3 отвечает на них двумя механизмами:
  • Deletion Vectors — чтобы DML не раздувал delete files.
  • Row Lineage — чтобы row-level changes были наблюдаемыми.

-
Часть 5: Deletion Vectors — меньше delete files, меньше anti-join
Deletion Vector — это bitmap, который хранит, какие строки в data file считаются удалёнными. В Iceberg V3 он хранится в Puffin file format.
Контраст с V2 такой:
  • V2: каждый DML commit создаёт отдельный Position Delete parquet.
  • V3: все пометки на удаление для одного data file схлопываются в один Puffin Deletion Vector.
  • V2 reader: читает data file, потом anti-join со всеми связанными delete files.
  • V3 reader: читает data file + DV и применяет bitmap за один проход. Anti-join исчезает.
pic
Iceberg V2 Position Deletes vs V3 Deletion Vector
V2 копит Position Delete parquet-файлы при каждом DML commit. V3 схлопывает все пометки в один Puffin Deletion Vector. Источник: оригинальная статья Mingyu Chen.
Мини-пример V2:
CREATE TABLE orders_v2 (
id INT,
status STRING,
amount DECIMAL(10,2)
) PROPERTIES ('format-version' = '2');
INSERT INTO orders_v2 VALUES
(1, 'pending', 100),
(2, 'pending', 200),
(3, 'pending', 300);
UPDATE orders_v2 SET status = 'shipped' WHERE id = 1;
UPDATE orders_v2 SET status = 'shipped' WHERE id = 2;
DELETE FROM orders_v2 WHERE id = 3;
После трёх DML commits в V2 появляются три Position Delete files.
Та же логика на V3:
CREATE TABLE orders_v3 (
id INT,
status STRING,
amount DECIMAL(10,2)
) PROPERTIES ('format-version' = '3');
INSERT INTO orders_v3 VALUES
(1, 'pending', 100),
(2, 'pending', 200),
(3, 'pending', 300);
UPDATE orders_v3 SET status = 'shipped' WHERE id = 1;
UPDATE orders_v3 SET status = 'shipped' WHERE id = 2;
DELETE FROM orders_v3 WHERE id = 3;
Теперь вместо набора parquet delete files рядом с data file лежит Puffin Deletion Vector. SQL на поверхности тот же, физическая механика другая.
Проверить это можно через system table:
SELECT content, file_path, record_count
FROM orders_v3$files;
В нормальном V3-сценарии вы ожидаете увидеть data file и .puffin DV, а не растущий список ...delete.parquet.
Что дают числа
В оригинальной статье приведён Doris-side benchmark на нескольких сценариях. Самое важное:
Сценарий Iceberg V2: Position Deletes Iceberg V3: Deletion Vector
16 data files, 20% deleted 336 files to open: 16 data + 320 delete 17 files: 16 data + 1 Puffin
100M rows, 99% deleted 98 MiB delete storage 3.8 MiB DV storage
Delete metadata reduction ~96%

Latency на 16 data files и 1M rows:
Delete % Doris V2 Doris V3 Speedup
5% 0.31s 0.15s 2.1x
10% 0.35s 0.16s 2.2x
20% 0.43s 0.17s 2.5x
30% 0.46s 0.14s 3.3x
40% 0.39s 0.17s 2.3x

Для large file с 99% deleted:
Table version Doris Q1 Doris Q2
V2, Position Delete 3.42s 3.28s
V3, Deletion Vector 1.03s 0.86s
Speedup ~3x ~3x

pic
Doris on Iceberg V3 Deletion Vector performance
Doris V3 vs V2: latency остаётся плоской при росте delete ratio, в то время как V2 деградирует линейно. Источник: оригинальная статья Mingyu Chen.
Смысл не в конкретном множителе «всегда 3x». Паттерн другой: под V2 anti-join cost растёт вместе с числом delete files, и query latency деградирует по мере роста delete ratio. Под V3 bitmap применяется за один проход, и latency остаётся практически плоской независимо от того, сколько строк помечено на удаление.
Что именно Doris реализует для Deletion Vectors
Важный нюанс из оригинала: чтобы Deletion Vectors приносили пользу, движок должен уметь и читать, и писать DV. Read-only движок может потреблять DV, записанные кем-то другим, но сам их не создаёт. Write-only движок создаёт DV, которые никто не прочитает.
Doris 4.1 поддерживает обе стороны:
  • Read: Puffin-format Deletion Vectors читаются без дополнительной настройки. V3-таблицы queryable сразу.
  • Write: DELETE, UPDATE, MERGE INTO на V3-таблице автоматически создают Puffin DV вместо Position Delete files. Пользователю достаточно указать format-version = 3 при создании таблицы — SQL-синтаксис не меняется.
Caveats по Deletion Vectors
Три практических ограничения:
  • DELETE, UPDATE, MERGE INTO требуют format-version >= 2.
  • Deletion Vectors появляются только при format-version = 3.
  • Concurrent writes используют Iceberg optimistic concurrency control, так что конфликты будут всплывать как transaction exceptions. Для high-conflict workloads нужны retry или сериализация.

-
Часть 6: Row Lineage — watermark для CDC без snapshot diff
Deletion Vectors отвечают на performance debt. Теперь про observability debt.
В Iceberg V1/V2 change tracking в основном живёт на уровне snapshot. Это нормальная модель для многих задач, но у неё есть неприятный эффект: физический rewrite может выглядеть как логическое изменение.
Например:
  • downstream-система подписана на изменения Iceberg-таблицы;
  • она сравнивает snapshots;
  • между checkpoint’ами прошёл rewrite_data_files;
  • физические файлы новые, snapshot новый;
  • downstream не знает, изменились строки логически или просто переехали между файлами.
И начинает переобрабатывать лишнее.
Iceberg V3 добавляет Row Lineage: две скрытые системные колонки для каждой строки.
pic
Iceberg V3 Row Lineage hidden columns
Row Lineage:_row_idдаёт стабильную идентичность строки,_last_updated_sequence_number— watermark для CDC. Источник: оригинальная статья Mingyu Chen.
Колонка Что означает
_row_id стабильный numeric identifier строки
_last_updated_sequence_number sequence number последнего логического изменения строки

Обе колонки поддерживаются системой. Пользователь не записывает их руками.
Самая важная часть: _last_updated_sequence_number меняется при настоящем UPDATE или MERGE INTO, но не меняется при compaction и physical rewrite. Значит, его можно использовать как CDC watermark.
Пример:
CREATE TABLE users_v3 (
id INT,
name STRING,
email STRING
) PROPERTIES ('format-version' = '3');
SET show_hidden_columns = true;
INSERT INTO users_v3 VALUES
(1, 'Alice', 'alice@x.com'),
(2, 'Bob', 'bob@x.com'),
(3, 'Carol', 'carol@x.com');
SELECT id, name, email, _row_id, _last_updated_sequence_number
FROM users_v3;
После первого insert все три строки получают стабильные _row_id, а _last_updated_sequence_number равен 1.
Дальше меняем Bob:
UPDATE users_v3
SET email = 'bob@newmail.com'
WHERE id = 2;
У Bob _row_id остаётся тем же, но _last_updated_sequence_number становится 2. Alice и Carol остаются с SN = 1.
Добавляем Dora:
INSERT INTO users_v3
VALUES (4, 'Dora', 'dora@x.com');
Dora получает новый _row_id и SN = 3.
Теперь downstream, который синхронизирован по watermark 1, может запросить только реальные изменения:
SELECT id, name, email, _last_updated_sequence_number
FROM users_v3
WHERE _last_updated_sequence_number > 1;
Вернутся Bob и Dora. Alice и Carol не попадут в pipeline. Если между checkpoint’ами случится compaction, они всё равно не попадут, потому что physical rewrite не двигает _last_updated_sequence_number.
Это и есть главный выигрыш: downstream хранит один integer watermark, а не пытается угадывать смысл snapshot diff.
Как меняется CDC-pipeline
Контраст в архитектуре:
V2 (snapshot diff): Upstream (PostgreSQL и т.д.) → Flink CDC → Iceberg V2 Sink → Downstream делает snapshot diff, но не может отличить реальное изменение от compaction/rewrite → false positives → переобработка.
V3 (Row Lineage): Upstream → Flink CDC → Iceberg V3 Sink → Downstream делает WHERE _last_updated_sequence_number > :watermark → только реальные изменения → без false positives.
Разница не только в точности, но и в простоте: вместо инфраструктуры для snapshot diffing — один SQL-запрос с одним integer watermark.
Что Doris реализует для Row Lineage
В Doris скрытые колонки можно читать явно или включить их отображение:
SET show_hidden_columns = true;
UPDATE и MERGE INTO на V3-таблицах автоматически обновляют _last_updated_sequence_number. Пользователь не добавляет отдельный audit trigger, не пишет отдельную таблицу lineage, не синхронизирует внешний log.
Но caveat снова важный: Row Lineage в оригинале помечен как experimental. Проверять на своём workload обязательно.
-
Часть 7: Audit trail через _row_id
Ещё один полезный сценарий — аудит.
Финансовый аудитор спрашивает: «Order 102 менялся три раза. Какие суммы были на каждом шаге?»
В V2 обычно приходится идти во внешнюю audit log, потом пытаться совместить её с текущим состоянием таблицы, потом учитывать compaction, file rewrite и прочие радости жизни. Это возможно, но хрупко: audit system и table state живут в разных местах.
В V3 появляется более удобная опора: _row_id остаётся lifetime identity строки.
Условно:
Snapshot SN Amount Event
SN=1 80.00 Order created
SN=2 90.00 Customer complaint, partial refund
SN=3 100.00 Final settlement

Сама Row Lineage не заменяет time travel и не хранит полную историю «магически». Важная деталь именно в связке:
  • _row_id даёт стабильный ключ строки;
  • snapshots/time travel дают возможность смотреть прошлые состояния;
  • _last_updated_sequence_number показывает логическую хронологию изменений;
  • compaction не ломает эту идентичность.
Примерный запрос для восстановления состояния:
SELECT order_id, amount
FROM orders_v3 FOR VERSION AS OF
WHERE _row_id = 1;
Да, вам всё ещё нужны snapshots. Но _row_id и _last_updated_sequence_number — это логические свойства строки, а не физические. Если между шагами прошёл ALTER TABLE orders_v3 EXECUTE rewrite_data_files(), физические файлы будут новые, а _row_id останется прежним и SN не изменится. Под V2 тот же compaction создал бы новый snapshot, и без внешней audit log вы бы не знали, менялись строки логически или нет.
-
Часть 8: Quick Start
Если хочется быстро потрогать механику руками, минимальный путь такой.
Prerequisites
Нужны:
  • Apache Doris 4.1.0 или новее (скачать или docker pull apache/doris:4.1.0).
  • Iceberg catalog с поддержкой V3.
  • Object store, доступный Doris BE: S3, MinIO, OSS или HDFS.
По catalog’ам в оригинале рекомендация такая:
  • REST Catalog — наиболее надёжный путь для V3. Например, Apache Polaris, Lakekeeper или Tabular open-source REST catalog image.
  • Hive Metastore-backed catalogs подходят для V1/V2 reads, но отстают по V3.
  • AWS Glue и Aliyun DLF на момент оригинальной статьи указаны как read-only.
1. Подключить catalog
CREATE CATALOG iceberg_v3 PROPERTIES (
'type' = 'iceberg',
'iceberg.catalog.type' = 'rest',
'uri' = 'http://your-rest-catalog:8181',
'warehouse' = 's3://your-bucket/warehouse',
's3.endpoint' = 'https://s3.us-west-2.amazonaws.com',
's3.access_key' = '',
's3.secret_key' = '',
's3.region' = 'us-west-2'
);
SWITCH iceberg_v3;
CREATE DATABASE IF NOT EXISTS demo;
USE demo;
2. Создать V3-таблицу и сделать DML
CREATE TABLE orders (
id INT,
status STRING,
amount DECIMAL(10,2)
) PROPERTIES ('format-version' = '3');
INSERT INTO orders VALUES
(1, 'pending', 100),
(2, 'pending', 200),
(3, 'pending', 300);
UPDATE orders SET status = 'shipped' WHERE id = 1;
DELETE FROM orders WHERE id = 3;
3. Проверить V3-поведение
Проверка Deletion Vector:
SELECT content, file_path, record_count
FROM orders$files;
Ожидаем .puffin DV рядом с data file, а не россыпь parquet delete files.
Проверка Row Lineage (скрытые колонки по умолчанию не видны):
SET show_hidden_columns = true;
SELECT id, status, _row_id, _last_updated_sequence_number
FROM orders;
4. Попробовать MERGE INTO
MERGE INTO orders t
USING (SELECT 1 AS id, 'delivered' AS status, 110 AS amount) s
ON t.id = s.id
WHEN MATCHED THEN UPDATE SET status = s.status, amount = s.amount
WHEN NOT MATCHED THEN INSERT (id, status, amount)
VALUES (s.id, s.status, s.amount);
Напоминание: MERGE INTO experimental. Это хороший кандидат для POC, но не для blind rollout в production.
Если orders$files показывает один Puffin file рядом с data file, а выжившие строки несут корректные значения _row_id / _last_updated_sequence_number — ваш стек V3-ready end to end.
Полный справочник по конфигурации: Doris Iceberg catalog docs.
-
Часть 9: Common gotchas
Короткий список того, обо что легко удариться:
  • format-version = 3 нужно задать при создании таблицы или мигрировать существующую V2-таблицу через ALTER TABLE ... SET PROPERTIES ('format-version' = '3').
  • Hidden columns (_row_id, _last_updated_sequence_number) работают только на V3. На V1/V2 таблицах запрос к ним даст ошибку.
  • Если MERGE INTO не парсится, проверьте Doris 4.1.0+ и target table V2/V3.
  • DML требует format-version >= 2; Deletion Vectors появляются только на V3.
  • Concurrent writers могут ловить optimistic concurrency conflicts. Для high-conflict workloads нужны retry, backoff или сериализация.
  • REST Catalog behavior зависит от конкретной реализации. Для production лучше свериться с support matrix в Doris docs.
  • AWS Glue и Aliyun DLF в оригинале указаны как read-only на момент статьи.

-
Часть 10: Где граница Doris, Spark и Flink
Самая опасная версия этой статьи звучала бы так: «Теперь Doris заменяет Spark для Iceberg».
Это плохая версия. Не надо так.
Более честная матрица:
Инструмент Где уместен
Doris real-time queries, small DML, incremental reconciliation, row-level provenance, day-to-day maintenance, диагностика Iceberg tables из SQL
Spark heavy backfills, cross-source ETL, long-running batch jobs
Flink streaming ingestion, continuous CDC write paths

Doris 4.1 не заменяет Spark. Он сокращает число случаев, когда Spark приходится запускать ради мелкой операции вокруг проблемы, которую вы уже нашли.
Если запрос уже привёл вас в SQL-клиент — маленькое исправление не должно превращаться в distributed-computing ритуал с отдельным кластером.
-
Итог
Одна фраза хорошо описывает направление Doris на Iceberg:
Если запрос уже привёл вас в SQL-клиент, маленькое исправление, incremental reconciliation или day-to-day maintenance не должны заставлять уходить в другой движок.
Что меняется в Apache Doris 4.1:
  • UPDATE, DELETE, MERGE INTO закрывают loop «query → fix → verify» внутри одного SQL-клиента.
  • Deletion Vectors в Iceberg V3 не дают DML накапливать performance debt через растущий набор Position Delete files.
  • Row Lineage даёт _row_id и _last_updated_sequence_number, чтобы CDC и audit не ломались от compaction и physical rewrite.
  • Maintenance и диагностика становятся ближе к человеку, который уже смотрит на данные.
Что не меняется:
  • Spark остаётся для тяжёлых batch/backfill/ETL задач.
  • Flink остаётся для streaming ingestion.
  • MERGE INTO и Row Lineage в оригинале помечены как experimental.
  • Production adoption требует POC на своём catalog, storage, concurrency pattern и workload.
Если коротко: Iceberg V3 делает DML архитектурно более здоровым, а Doris 4.1 пытается сделать этот DML доступным из того же SQL-движка, где вы уже нашли проблему.
Для data engineer’а это не звучит как революция. Это звучит как минус один ticket, минус один job, минус один context switch.
А иногда именно это и есть настоящая продуктивность.
-
Ссылки -Оригинал по лицензии CC BY 4.0 © 2026 Mingyu Chen (Rayner).-Источник
 
Loading...
Error