Перейти к содержимому
Последнее обновление

Инкрементальное сопровождение представлений (IVM)

Инкрементальное сопровождение представлений (IVM) поддерживает асинхронное материализованное представление на основе изменений на уровне строк в его базовых таблицах. Обычное асинхронное материализованное представление пересчитывает все свои данные или каждую затронутую партицию; IVM обрабатывает только строки, вставленные, обновлённые и удалённые в базовых таблицах между двумя обновлениями представления, и применяет эти изменения к материализованному представлению.

Экспериментальная функция

IVM доступно начиная с Doris 5.0.0. Это экспериментальная функция, отключённая по умолчанию. Перед использованием включите changelog и Table Stream в конфигурации FE. См. раздел Предварительные требования.

Когда использовать IVM​

Прежде чем выбирать способ обновления, сопоставьте количество изменённых строк за одно обновление, размер затронутых партиций и требования к согласованности.

СценарийРекомендацияПричина
В базовых таблицах часто выполняются вставки, обновления и удаления, а изменённые строки составляют небольшую долю таблицы или партицииИспользуйте IVMОбрабатываются только изменения на уровне строк между двумя обновлениями, поэтому партиции целиком не пересчитываются
Изменения сосредоточены в нескольких партициях, и затраты на их пересчёт приемлемыИспользуйте PARTITIONSБолее простая настройка; пересчитываются затронутые партиции
Создание первоначального базового состояния, небольшой объём данных или восстановление базового состояния IVMИспользуйте COMPLETEПересчитываются все данные материализованного представления
Результаты должны быть транзакционно согласованы с базовыми таблицами в реальном времениНе используйте асинхронное материализованное представлениеIVM по-прежнему обновляется через асинхронные задачи и не обеспечивает согласованность в реальном времени

Отличия от инкрементального обновления на уровне партиций​

Doris поддерживает четыре способа обновления асинхронных материализованных представлений. IVM соответствует INCREMENTAL. Не путайте его с инкрементальным обновлением на уровне партиций, которое обеспечивает PARTITIONS.

Способ обновленияГранулярность вычисленийПодходящий сценарийПоведение при невыполнении условий
INCREMENTALИзменения на уровне строкКоличество изменённых строк значительно меньше объёма всей таблицы или затронутых партицийПо умолчанию завершается ошибкой; при указании FALLBACK возможен переход к резервному способу обновления
PARTITIONSКаждая затронутая партиция материализованного представленияИзменения сосредоточены в нескольких партициях, и затраты на их пересчёт приемлемыПо умолчанию завершается ошибкой; переходит к COMPLETE, если указан FALLBACK
COMPLETEВсе данныеНебольшой объём данных, первоначальное базовое состояние или восстановление базового состояния IVMВсегда пересчитывает всё
AUTOВыбирается Doris автоматическиТребуется, чтобы Doris выбрал доступную стратегиюПо очереди пробует IVM, обновление партиций и полное обновление, используя доступный способ

INCREMENTAL FALLBACK меняет только обработку ошибок во время обновления. При создании материализованного представления SQL его определения по-прежнему должен удовлетворять требованиям IVM; FALLBACK не позволяет неподдерживаемому определению пройти проверку при создании.

Принцип работы​

IVM использует возможности changelog и Table Stream в Doris:

Принцип работы IVM: изменения базовых таблиц попадают в changelog, а затем во внутренние Table Stream; дельта-план объединяет ещё не обработанные изменения с согласованным снимком, обновляет материализованное представление и продвигает позиции потребления в той же транзакции

При создании материализованного представления с поддержкой IVM Doris создаёт внутренний Table Stream для каждой базовой таблицы, участвующей в инкрементальном сопровождении. Имена внутренних Stream начинаются с __doris_ivm_stream_. Создавать, потреблять или удалять эти Stream самостоятельно не требуется.

Во время обновления Doris формирует дельта-план из ещё не обработанных изменений каждой базовой таблицы. Для запросов с соединениями он также читает снимок базовых таблиц, согласованный с позициями потребления, чтобы вычисления по нескольким таблицам использовали единую согласованную границу данных. Данные материализованного представления и позиции потребления фиксируются в одной транзакции; если транзакция завершается ошибкой, ни то ни другое не фиксируется.

Предварительные требования​

Версия и конфигурация FE​

  1. Используйте Doris 5.0.0 или более позднюю версию.

  2. Задайте следующие параметры в fe.conf на каждом FE и перезапустите все FE:

    enable_feature_changelog = true
    enable_table_stream = true

Оба параметра являются статическими, то есть не изменяются динамически. enable_feature_changelog включает changelog и глобальную временную метку фиксации транзакций; enable_table_stream включает DDL для Table Stream и управление внутренними Stream.

Требования к базовым таблицам​

Базовые таблицы, участвующие в инкрементальном сопровождении, должны быть внутренними OLAP-таблицами Doris в Internal Catalog и соответствовать следующим требованиям:

Модель базовой таблицыПоддержкаТребования и поведение
Unique Key Merge-on-WriteДаНеобходимо включить changelog и сохранение образа строки до изменения; обрабатываются вставки, обновления и удаления
Duplicate KeyДаНеобходимо включить changelog; предоставляются только добавленные данные, а удаления через предикаты удаления не записываются
Unique Key Merge-on-ReadНетИспользуйте Merge-on-Write
Aggregate KeyНе поддерживается для инкрементального сопровожденияМожет использоваться только как таблица, указанная в excluded_trigger_tables, которая не запускает инкрементальное обновление
Внешние таблицыНетБазовые таблицы, инкрементально сопровождаемые IVM, должны быть внутренними таблицами Doris

Для нагрузок, требующих обработки обновлений и удалений, используйте таблицу Unique Key Merge-on-Write и задайте при её создании следующие свойства:

PROPERTIES (
"enable_unique_key_merge_on_write" = "true",
"changelog.enable" = "true",
"changelog.need_historical_value" = "true"
);

Changelog можно включить только при создании таблицы, после чего отключить его нельзя. Полный список ограничений по моделям таблиц, типам столбцов, способам записи и DDL см. в разделе Changelog.

Быстрый старт​

В следующем примере создаётся IVM, агрегирующее заказы по статусу. Первое обновление использует COMPLETE для создания полного базового состояния; последующие обновления используют INCREMENTAL FALLBACK для обработки изменений на уровне строк.

Последовательность действий:

  1. Создайте базовую таблицу Unique Key Merge-on-Write с включённым changelog.
  2. Создайте IVM с REFRESH INCREMENTAL FALLBACK.
  3. Выполните обновление COMPLETE для создания полного базового состояния.
  4. Измените данные базовой таблицы и выполните инкрементальное обновление.
  5. Запросите сведения о задаче обновления, чтобы проверить результат и причину перехода к резервному способу обновления.

Шаг 1: создание базовой таблицы и загрузка начальных данных​

Создайте базовую таблицу с поддержкой обновлений и удалений, включив changelog и сохранение образа строки до изменения:

CREATE DATABASE IF NOT EXISTS ivm_demo;
USE ivm_demo;

CREATE TABLE orders (
order_id BIGINT,
order_status VARCHAR(16),
amount DECIMAL(10, 2)
)
UNIQUE KEY(order_id)
DISTRIBUTED BY HASH(order_id) BUCKETS 1
PROPERTIES (
"replication_num" = "1",
"enable_unique_key_merge_on_write" = "true",
"changelog.enable" = "true",
"changelog.need_historical_value" = "true"
);

INSERT INTO orders VALUES
(1, 'created', 100.00),
(2, 'created', 200.00),
(3, 'paid', 300.00);

Шаг 2: создание IVM​

Укажите REFRESH INCREMENTAL в CREATE MATERIALIZED VIEW. В этом примере также указан FALLBACK, чтобы Doris мог перейти к обновлению партиций или полному обновлению, если изменение нельзя безопасно обработать инкрементально. Для запуска используется ON MANUAL, чтобы можно было пошагово наблюдать за каждым обновлением; в рабочей среде представления обычно переводят на запуск по расписанию или при фиксации транзакции. См. раздел Настройка интервала автоматического обновления.

CREATE MATERIALIZED VIEW orders_by_status
BUILD DEFERRED
REFRESH INCREMENTAL FALLBACK ON MANUAL
DISTRIBUTED BY RANDOM BUCKETS 1
PROPERTIES (
"replication_num" = "1"
)
AS
SELECT
order_status,
COUNT(*) AS order_count,
SUM(amount) AS total_amount
FROM orders
GROUP BY order_status;

После создания представления Doris автоматически создаёт соответствующий внутренний Table Stream. Проверить соответствие можно с помощью mv_infos:

SELECT Name, RefreshInfo, IvmBaseTableStreams
FROM mv_infos("database" = "ivm_demo")
WHERE Name = "orders_by_status";

Шаг 3: создание полного базового состояния​

Используйте COMPLETE для первого обновления, чтобы создать базовое состояние для последующего инкрементального сопровождения:

REFRESH MATERIALIZED VIEW orders_by_status COMPLETE;

Задача обновления выполняется асинхронно. После её успешного завершения запросите данные материализованного представления:

SELECT order_status, order_count, total_amount
FROM orders_by_status
ORDER BY order_status;
+--------------+-------------+--------------+
| order_status | order_count | total_amount |
+--------------+-------------+--------------+
| created | 2 | 300.00 |
| paid | 1 | 300.00 |
+--------------+-------------+--------------+

Шаг 4: запись изменений и инкрементальное обновление​

Следующие операторы обновляют заказ 1, удаляют заказ 2 и вставляют заказ 4:

INSERT INTO orders VALUES (1, 'paid', 100.00);
DELETE FROM orders WHERE order_id = 2;
INSERT INTO orders VALUES (4, 'created', 400.00);

REFRESH MATERIALIZED VIEW orders_by_status INCREMENTAL FALLBACK;

После успешного завершения задачи обновления IVM обработало только перечисленные выше изменения на уровне строк. Теперь материализованное представление содержит:

SELECT order_status, order_count, total_amount
FROM orders_by_status
ORDER BY order_status;
+--------------+-------------+--------------+
| order_status | order_count | total_amount |
+--------------+-------------+--------------+
| created | 1 | 400.00 |
| paid | 2 | 400.00 |
+--------------+-------------+--------------+

Шаг 5: проверка способа обновления и причины перехода к резервному способу​

Запросите сведения о последней задаче обновления, чтобы проверить запрошенный способ, фактический охват обновления и причину перехода к резервному способу:

SELECT Status, TaskContext, RefreshMode, IvmFallbackReason, ErrorMsg
FROM tasks("type" = "mv")
WHERE MvDatabaseName = "ivm_demo"
AND MvName = "orders_by_status"
ORDER BY CreateTime DESC, TaskId DESC
LIMIT 1;
  • TaskContext содержит способ обновления, запрошенный этой задачей, например INCREMENTAL.
  • RefreshMode содержит охват партиций, фактически обновлённых задачей: COMPLETE, PARTIAL или NOT_REFRESH.
  • IvmFallbackReason пуст, если перехода от IVM к резервному способу не было; в противном случае содержит устойчивый код причины перехода.
  • ErrorMsg содержит подробности ошибки строгого инкрементального обновления.

Поддерживаемые запросы​

IVM проверяет SQL определения при создании материализованного представления. Если явно указано REFRESH INCREMENTAL, неподдерживаемое определение приводит к ошибке создания. В этом разделе перечислены поддерживаемые реляционные операции и агрегатные функции с исполняемым примером для каждой, а затем приведены неподдерживаемые формы запросов.

Таблицы для примеров​

В примерах этого раздела используются три базовые таблицы: таблица клиентов customers, таблица продаж sales и таблица возвратов refunds. Все три являются таблицами Unique Key Merge-on-Write с включённым changelog и сохранением образа строки до изменения. Выполнить примеры можно в базе данных ivm_demo, созданной в разделе Быстрый старт:

CREATE TABLE customers (
customer_id BIGINT,
name VARCHAR(32),
city VARCHAR(16)
)
UNIQUE KEY(customer_id)
DISTRIBUTED BY HASH(customer_id) BUCKETS 1
PROPERTIES (
"replication_num" = "1",
"enable_unique_key_merge_on_write" = "true",
"changelog.enable" = "true",
"changelog.need_historical_value" = "true"
);

CREATE TABLE sales (
sale_id BIGINT,
customer_id BIGINT,
channel VARCHAR(16),
amount DECIMAL(10, 2)
)
UNIQUE KEY(sale_id)
DISTRIBUTED BY HASH(sale_id) BUCKETS 1
PROPERTIES (
"replication_num" = "1",
"enable_unique_key_merge_on_write" = "true",
"changelog.enable" = "true",
"changelog.need_historical_value" = "true"
);

CREATE TABLE refunds (
refund_id BIGINT,
sale_id BIGINT,
amount DECIMAL(10, 2)
)
UNIQUE KEY(refund_id)
DISTRIBUTED BY HASH(refund_id) BUCKETS 1
PROPERTIES (
"replication_num" = "1",
"enable_unique_key_merge_on_write" = "true",
"changelog.enable" = "true",
"changelog.need_historical_value" = "true"
);

INSERT INTO customers VALUES
(1, 'Alice', 'Beijing'),
(2, 'Bob', 'Shanghai'),
(3, 'Carol', 'Beijing');

INSERT INTO sales VALUES
(101, 1, 'web', 120.00),
(102, 1, 'app', 80.00),
(103, 2, 'web', 200.00),
(104, NULL, 'store', 50.00);

INSERT INTO refunds VALUES
(9001, 102, 80.00),
(9002, 103, 50.00);

Данные намеренно содержат два особых случая, используемых в примерах соединений и агрегации: у заказа 104 из магазина нет клиента, поэтому его customer_id равен NULL, а у клиентки Carol пока нет заказов.

Во всех примерах используется одна последовательность действий: выполните соответствующий CREATE MATERIALIZED VIEW, затем REFRESH MATERIALIZED VIEW <mv_name> COMPLETE для создания базового состояния, а после успешного завершения задачи обновления выполните SELECT * FROM <mv_name>, чтобы увидеть результат. Приведённые здесь результаты отсортированы по первым столбцам.

Поддерживаемые реляционные операции​

ОперацияОписание
Столбцы и выраженияВыбор части столбцов или вычисление новых столбцов с помощью детерминированных выражений
Фильтры WHEREФильтрация строк базовых таблиц по условию
Подзапросы FROMПодзапросы с псевдонимами в предложении FROM; допускается вложенность, но агрегации запрещены
Строки константSELECT без FROM, всегда возвращающий одну строку констант, например SELECT -1, 'unknown'. Может быть ветвью UNION ALL, одной из сторон соединения или всем определением
Агрегация GROUP BYАгрегация должна быть самой внешней операцией запроса, над которой допускаются только проекции; поддерживаются детерминированные выражения над столбцами группировки и результатами агрегации
GROUPING SETS, ROLLUP, CUBEМожно использовать с GROUPING() и GROUPING_ID(); действуют те же ограничения, что и для GROUP BY
SELECT DISTINCTОбрабатывается как GROUP BY без агрегатных функций; действуют те же ограничения, что и для GROUP BY
INNER JOIN, CROSS JOINПоддерживаются соединения нескольких таблиц и самосоединения
LEFT OUTER JOIN, RIGHT OUTER JOIN, FULL OUTER JOINСторона, сохраняющая все свои строки (левая сторона LEFT JOIN, правая сторона RIGHT JOIN, обе стороны FULL JOIN), не должна быть таблицей Duplicate Key без changelog, указанной в excluded_trigger_tables, поскольку у строк такой таблицы нет фиксированного идентификатора
Вложенные внешние соединенияСторона внешнего соединения, которая может дополняться значениями NULL, сама является соединением. Чем сложнее эта сторона, тем больше план инкрементального обновления
UNION ALLВетви могут читать разные таблицы, одну и ту же таблицу или содержать строки констант
Цепочка IVMДругое IVM используется как базовая таблица. Для IVM, используемого как базовая таблица, необходимо включить changelog и сохранение образа строки до изменения

Приведённые ниже примеры сгруппированы по категориям.

Запросы к одной таблице​

Выберите нужные столбцы или вычислите новые с помощью выражений. Выражения должны быть детерминированными, то есть одинаковые входные данные всегда должны давать одинаковый результат; например, NOW() и RAND() использовать нельзя.

CREATE MATERIALIZED VIEW mv_sales_projection
BUILD DEFERRED REFRESH INCREMENTAL ON MANUAL
DISTRIBUTED BY RANDOM BUCKETS 1
PROPERTIES ("replication_num" = "1")
AS
SELECT sale_id, channel, amount, amount * 0.9 AS discounted_amount
FROM sales;

Результат:

+---------+---------+--------+-------------------+
| sale_id | channel | amount | discounted_amount |
+---------+---------+--------+-------------------+
| 101 | web | 120.00 | 108.000 |
| 102 | app | 80.00 | 72.000 |
| 103 | web | 200.00 | 180.000 |
| 104 | store | 50.00 | 45.000 |
+---------+---------+--------+-------------------+

Агрегация​

Вычисляет результаты агрегации для каждой группы. Входными данными агрегации могут быть одна таблица, соединение, UNION ALL или подзапрос FROM, но сама агрегация должна быть самой внешней операцией запроса. Следующий пример соединяет sales с customers, а затем агрегирует данные по городам; у заказа 104 нет клиента, поэтому он не учитывается.

CREATE MATERIALIZED VIEW mv_city_sales
BUILD DEFERRED REFRESH INCREMENTAL ON MANUAL
DISTRIBUTED BY RANDOM BUCKETS 1
PROPERTIES ("replication_num" = "1")
AS
SELECT c.city, COUNT(*) AS sale_cnt, SUM(s.amount) AS total_amount
FROM sales s
INNER JOIN customers c ON s.customer_id = c.customer_id
GROUP BY c.city;

Результат:

+----------+----------+--------------+
| city | sale_cnt | total_amount |
+----------+----------+--------------+
| Beijing | 2 | 200.00 |
| Shanghai | 1 | 200.00 |
+----------+----------+--------------+

Проекция над агрегацией может переименовывать столбцы, менять их порядок или выполнять вычисления над столбцами группировки и результатами агрегации с помощью детерминированных выражений. Например, SUM(amount) * 100, MAX(amount) + 1 и SUM(amount) / COUNT(*) поддерживают инкрементальное обновление с учётом ограничений используемых агрегатных функций. HAVING и агрегации внутри подзапроса или входных данных соединения не поддерживаются. См. раздел Неподдерживаемые формы запросов.

Соединения​

Сохраняет только строки, для которых найдено соответствие с обеих сторон. У заказа 104 нет клиента, поэтому он не попадает в результат.

CREATE MATERIALIZED VIEW mv_sales_customer
BUILD DEFERRED REFRESH INCREMENTAL ON MANUAL
DISTRIBUTED BY RANDOM BUCKETS 1
PROPERTIES ("replication_num" = "1")
AS
SELECT s.sale_id, c.name, c.city, s.amount
FROM sales s
INNER JOIN customers c ON s.customer_id = c.customer_id;

Результат:

+---------+-------+----------+--------+
| sale_id | name | city | amount |
+---------+-------+----------+--------+
| 101 | Alice | Beijing | 120.00 |
| 102 | Alice | Beijing | 80.00 |
| 103 | Bob | Shanghai | 200.00 |
+---------+-------+----------+--------+

UNION ALL и цепочки IVM​

Объединяет результаты нескольких запросов без удаления дубликатов. Ветви могут читать разные таблицы, одну и ту же таблицу или содержать строки констант. В следующем примере продажи и возвраты объединяются в единый журнал, в котором суммы возвратов записываются отрицательными числами.

CREATE MATERIALIZED VIEW mv_ledger
BUILD DEFERRED REFRESH INCREMENTAL ON MANUAL
DISTRIBUTED BY RANDOM BUCKETS 1
PROPERTIES ("replication_num" = "1")
AS
SELECT sale_id AS doc_id, 'sale' AS doc_type, amount
FROM sales
UNION ALL
SELECT refund_id, 'refund', -amount
FROM refunds;

Результат:

+--------+----------+--------+
| doc_id | doc_type | amount |
+--------+----------+--------+
| 101 | sale | 120.00 |
| 102 | sale | 80.00 |
| 103 | sale | 200.00 |
| 104 | sale | 50.00 |
| 9001 | refund | -80.00 |
| 9002 | refund | -50.00 |
+--------+----------+--------+

Поддерживаемые агрегатные функции​

IVM поддерживает следующие агрегатные функции. Их аргументами могут быть столбцы или детерминированные выражения:

  • COUNT(*), COUNT(expr)
  • SUM
  • AVG
  • MIN
  • MAX
  • BITMAP_UNION
  • BITMAP_UNION_COUNT
  • ARRAY_AGG
  • COLLECT_LIST (только форма с одним аргументом)

Во всех следующих примерах таблица sales группируется по каналу:

COUNT(*) подсчитывает строки; COUNT(expr) подсчитывает только строки, в которых expr не равен NULL. Значение customer_id заказа 104 из магазина равно NULL, поэтому member_sale_cnt для канала магазина равно 0.

CREATE MATERIALIZED VIEW mv_agg_count
BUILD DEFERRED REFRESH INCREMENTAL ON MANUAL
DISTRIBUTED BY RANDOM BUCKETS 1
PROPERTIES ("replication_num" = "1")
AS
SELECT channel,
COUNT(*) AS sale_cnt,
COUNT(customer_id) AS member_sale_cnt
FROM sales
GROUP BY channel;

Результат:

+---------+----------+-----------------+
| channel | sale_cnt | member_sale_cnt |
+---------+----------+-----------------+
| app | 1 | 1 |
| store | 1 | 0 |
| web | 2 | 2 |
+---------+----------+-----------------+

Агрегатные функции с DISTINCT, например COUNT(DISTINCT customer_id), не поддерживаются. Для точного подсчёта уникальных значений в столбце неотрицательных целых чисел используйте BITMAP_UNION_COUNT(TO_BITMAP(customer_id)), но при этом удаления и обновления требуют полного пересчёта Bitmap, как описано ниже.

При удалении строк некоторые агрегатные функции не могут безопасно вычислить новый результат только по текущей дельте. Обновление в таблице Unique Key удаляет старую строку, а затем записывает новую, поэтому обновления также могут приводить к следующим ситуациям:

  • Если удаляемое значение может быть текущим MIN или MAX, необходимо заново прочитать все данные.
  • Если удаление затрагивает результат агрегации Bitmap, необходимо пересчитать весь Bitmap.

Строгое обновление INCREMENTAL в этих случаях завершается ошибкой; INCREMENTAL FALLBACK переходит к COMPLETE.

Неподдерживаемые формы запросов​

Следующие формы не поддерживаются, и при явном указании REFRESH INCREMENTAL создание завершается ошибкой:

  • UNION (то есть UNION DISTINCT), INTERSECT и EXCEPT.
  • ORDER BY, LIMIT и оконные функции.
  • HAVING и агрегации внутри подзапроса FROM или входных данных соединения. Агрегация может быть только самой внешней операцией запроса; чтобы отфильтровать результаты агрегации, применяйте фильтр при запросе к материализованному представлению.
  • Подзапросы в WHERE или в списке SELECT, например IN (SELECT ...), EXISTS (SELECT ...) и скалярные подзапросы. Подзапросы IN и скалярные подзапросы могут завершаться ошибкой Multiple columns returned by subquery are not yet supported; причина также в том, что IVM не поддерживает эти подзапросы.
  • Предложения WITH (CTE). Перепишите их в виде подзапросов FROM.
  • LATERAL VIEW.
  • Использование обычного асинхронного материализованного представления как базовой таблицы. Для сопровождения цепочки представлений материализованное представление нижнего уровня также должно быть IVM.
  • Любые другие операции, не перечисленные в таблице выше.

Обновление и переход к резервным способам​

Выбор стратегии обновления при создании​

ОпределениеПоведение при созданииПоведение последующих обновлений по умолчанию
REFRESH INCREMENTALСтрого проверяет соответствие поддерживаемым возможностям IVM; при отсутствии поддержки создание завершается ошибкойПробует только IVM; при ошибке IVM задача завершается ошибкой
REFRESH INCREMENTAL FALLBACKКак в строгом режиме; проверка IVM по-прежнему должна быть пройденаСначала пробует IVM, затем переходит к резервному способу в зависимости от причины
REFRESH AUTOПроверяет, поддерживает ли определение IVM; если нет, создаёт обычное асинхронное материализованное представлениеДля представлений с поддержкой IVM по очереди пробует IVM, обновление партиций и полное обновление
REFRESH PARTITIONS [FALLBACK]Требует, чтобы в материализованном представлении было определено PARTITION BYПересчитывает изменённые партиции; при наличии FALLBACK может перейти к полному обновлению
REFRESH COMPLETEНе создаёт метаданные IVMВсегда выполняет полное обновление

Настройка интервала автоматического обновления​

IVM использует способы запуска асинхронных материализованных представлений и не имеет собственного синтаксиса запуска. Укажите способ запуска в предложении ON после REFRESH INCREMENTAL [FALLBACK]:

Способ запускаСинтаксисОписание
ВручнуюON MANUALПо умолчанию. Обновление выполняется только при выполнении REFRESH MATERIALIZED VIEW
По расписаниюON SCHEDULE EVERY <interval> <unit> [STARTS '<start_time>']Автоматически выполняет инкрементальное обновление с фиксированным интервалом
При фиксации транзакцииON COMMITАвтоматически выполняет инкрементальное обновление после фиксации транзакции загрузки в базовой таблице

Правила задания интервала расписания:

  • <interval> должен быть положительным целым числом, а <unit> может принимать значения MINUTE, HOUR, DAY или WEEK.
  • Минимальный интервал обновления: EVERY 1 MINUTE. Указание SECOND приводит к ошибке interval time unit can not be second. Параметр FE enable_job_schedule_second_for_test разрешает интервалы с точностью до секунд, но предназначен только для тестирования и не должен включаться в рабочей среде.
  • STARTS задаёт время первого запуска по расписанию в формате 'yyyy-MM-dd HH:mm:ss', которое должно быть позже текущего времени. Если оно не задано, первое обновление выполняется через один интервал после создания материализованного представления. Последующие запуски назначаются на время первого запуска плюс целое число интервалов независимо от времени завершения предыдущей задачи.

В следующем примере инкрементальное обновление выполняется каждые 5 минут с разрешённым переходом к резервному способу, если изменение нельзя обработать инкрементально:

CREATE MATERIALIZED VIEW orders_by_status
BUILD IMMEDIATE
REFRESH INCREMENTAL FALLBACK ON SCHEDULE EVERY 5 MINUTE
DISTRIBUTED BY RANDOM BUCKETS 1
PROPERTIES (
"replication_num" = "1"
)
AS
SELECT
order_status,
COUNT(*) AS order_count,
SUM(amount) AS total_amount
FROM orders
GROUP BY order_status;

Задачи по расписанию и при фиксации транзакции выполняются со стратегией обновления, заданной при создании материализованного представления: INCREMENTAL пробует только IVM, INCREMENTAL FALLBACK сначала пробует IVM, а затем переходит к резервному способу в зависимости от причины, а AUTO по очереди пробует IVM, обновление партиций и полное обновление. Автоматически запускаемые задачи также имеют следующие особенности:

  • Первое автоматическое обновление самостоятельно создаёт базовое состояние. Если материализованное представление ещё ни разу не было успешно обновлено (например, оно создано с BUILD DEFERRED и пока не обновлялось), первая задача по расписанию или при фиксации транзакции автоматически выполняет COMPLETE для создания полного базового состояния; выполнять COMPLETE вручную не требуется. Последующие задачи обновляют данные инкрементально.
  • Задачи обновления одного материализованного представления выполняются последовательно, а лишние события запуска пропускаются. Сохраняется не более одной выполняемой и одной ожидающей автоматически запущенной задачи. Если интервал короче длительности одного обновления или базовые таблицы фиксируют транзакции очень часто при использовании ON COMMIT, новые события запуска пропускаются и метрика FE async_materialized_view_task_skip_num увеличивается. Ожидающая задача обрабатывает все накопленные изменения за один запуск, поэтому изменения не теряются, но фактическая задержка обновления превышает настроенный интервал. Выбирайте интервал, за который одно инкрементальное обновление успевает завершиться при обычной нагрузке.
  • ON COMMIT срабатывает только для базовых таблиц, участвующих в инкрементальном сопровождении. Фиксации транзакций в таблицах, перечисленных в excluded_trigger_tables, не запускают обновление. См. раздел excluded_trigger_tables.

При изменении способа запуска указывайте только предложение ON и не повторяйте INCREMENTAL. Способ обновления IVM нельзя изменить с помощью ALTER, поэтому ALTER MATERIALIZED VIEW ... REFRESH INCREMENTAL ... отклоняется; менять только способ запуска разрешено, при этом Doris пересоздаёт задание планировщика с новым способом запуска:

ALTER MATERIALIZED VIEW orders_by_status REFRESH ON SCHEDULE EVERY 1 MINUTE;
ALTER MATERIALIZED VIEW orders_by_status REFRESH ON COMMIT;
ALTER MATERIALIZED VIEW orders_by_status REFRESH ON MANUAL;

Ручное переопределение способа обновления​

После создания IVM при необходимости можно выполнять любую из следующих команд:

REFRESH MATERIALIZED VIEW <mv_name> INCREMENTAL;
REFRESH MATERIALIZED VIEW <mv_name> INCREMENTAL FALLBACK;
REFRESH MATERIALIZED VIEW <mv_name> PARTITIONS;
REFRESH MATERIALIZED VIEW <mv_name> PARTITIONS FALLBACK;
REFRESH MATERIALIZED VIEW <mv_name> COMPLETE;

IVM не допускает устаревший синтаксис PARTITION (<partition_name>) или PARTITIONS (<partition_name>, ...) для явного указания обновляемых партиций. Используйте ключевое слово PARTITIONS, чтобы Doris определил партиции, требующие обновления, или COMPLETE для обновления всех данных.

Порядок перехода к резервным способам​

Если запрос разрешает переход к резервным способам, по умолчанию Doris пробует их в следующем порядке:

IVM -> PARTITIONS -> COMPLETE

Некоторые ошибки означают, что существующее базовое состояние IVM больше нельзя безопасно использовать. В этих случаях Doris пропускает обновление партиций и сразу выполняет COMPLETE. Типичные причины:

IvmFallbackReasonЗначениеВосстановление
STREAM_UNSUPPORTEDВнутренний Stream отсутствует или больше не может использоватьсяПолное обновление, которое также пересоздаёт внутренние Stream и повторно согласует позиции потребления
MIN_MAX_BOUNDARY_HITУдаление может изменить текущий MIN или MAXПолностью пересчитать результат агрегации
BITMAP_AGG_DELETEУдаление затрагивает агрегацию BitmapПолностью пересчитать Bitmap
PLAN_SIGNATURE_MISMATCHТекущий план запроса не соответствует сохранённой структуре IVMПолное обновление и создание нового базового состояния
INCOMPLETE_REFRESH_SNAPSHOTДля цепочки резервных способов ещё нет снимка полного обновленияПолное обновление для создания базового состояния

Строгое обновление INCREMENTAL никогда не переходит к резервным способам. Если задача завершается ошибкой, материализованное представление сохраняет данные, которые были в нём до обновления, а позиции потребления внутренних Stream не продвигаются. После исправления ошибки можно снова выполнить инкрементальное обновление или выполнить COMPLETE для перестроения базового состояния.

Первое обновление и перестроение базового состояния​

Инкрементальное обновление может обрабатывать только изменения, сделанные после создания базового состояния. Базовое состояние представляет собой результат последнего полного вычисления материализованного представления вместе с согласованными с ним позициями потребления внутренних Stream. Doris отслеживает действительность базового состояния для каждой партиции материализованного представления: партиции с недействительным базовым состоянием перестраиваются полностью, а остальные продолжают обновляться инкрементально.

Первое обновление

Пока материализованное представление не было успешно обновлено, ни у одной его партиции нет базового состояния. Независимо от запрошенного способа первое обновление полностью вычисляет все данные:

Запрос на обновлениеПоведение первого обновления
COMPLETEПолностью обновляет все данные
INCREMENTALСначала перестраивает все партиции, затем обрабатывает дельту
INCREMENTAL FALLBACK, AUTOСразу выполняет COMPLETE
Запуск по расписанию или при фиксации транзакцииВыполняет COMPLETE, см. раздел Настройка интервала автоматического обновления

Значение RefreshMode у всех этих задач равно COMPLETE, а IvmFallbackReason пусто. Поэтому после создания IVM отдельно выполнять COMPLETE не требуется; в разделе Быстрый старт команда COMPLETE выполняется явно только для наглядного выделения этапа создания базового состояния.

Аннулирование базового состояния отдельных партиций

Следующие операции с базовыми таблицами изменяют только метаданные и не записывают изменения в changelog, поэтому инкрементальное обновление не может определить по записям изменений, какие строки были удалены или заменены. Партиции материализованного представления, читающие эти партиции базовых таблиц, необходимо перестроить:

  • TRUNCATE TABLE, включая очистку только отдельных партиций
  • ALTER TABLE ... DROP PARTITION
  • RECOVER PARTITION
  • ALTER TABLE ... REPLACE PARTITION со строгим сопоставлением диапазонов, используемым по умолчанию

Если материализованное представление партиционировано по столбцу партиционирования базовой таблицы, Doris помечает только затронутые партиции материализованного представления. Следующее обновление, включая строгое INCREMENTAL, сначала перестраивает эти партиции, а затем инкрементально обновляет остальные. Столбец IvmRebuiltPartitions в tasks("type"="mv") показывает, сколько дополнительных партиций было перестроено при обновлении. При удалении партиции базовой таблицы соответствующая партиция материализованного представления удаляется во время синхронизации партиций и не требует перестроения.

Если материализованное представление не партиционировано или Doris не может определить затронутые партиции материализованного представления, базовое состояние всего материализованного представления становится недействительным, как описано ниже. Эти операции с базовыми таблицами, перечисленными в excluded_trigger_tables, не аннулируют базовое состояние.

Аннулирование базового состояния всего материализованного представления

Следующие ситуации аннулируют базовое состояние всего материализованного представления и переводят его в состояние SCHEMA_CHANGE, которое можно проверить в столбцах State и SchemaChangeDetail функции mv_infos:

  • В базовой таблице выполняется DROP COLUMN, MODIFY COLUMN, RENAME COLUMN, RENAME (переименование таблицы) или REPLACE WITH TABLE. ADD COLUMN не аннулирует базовое состояние. Таблицы с changelog не допускают MODIFY COLUMN и RENAME COLUMN, см. раздел Ограничения DDL для changelog.
  • В базовой таблице выполняется REPLACE PARTITION без строгого сопоставления диапазонов либо операцию с партициями из предыдущего списка невозможно сопоставить с конкретными партициями материализованного представления.
  • Изменение свойства расширяет область данных, сопровождаемых материализованным представлением: удаление базовой таблицы из excluded_trigger_tables, расширение или снятие ограничения ivm_partition_window_limit либо расширение диапазона partition_sync_limit.

Пока материализованное представление находится в состоянии SCHEMA_CHANGE, следующее обновление INCREMENTAL, INCREMENTAL FALLBACK, AUTO, COMPLETE или PARTITIONS FALLBACK перестраивает всё материализованное представление, и после успешного завершения состояние возвращается к NORMAL. Обновление PARTITIONS без FALLBACK завершается ошибкой с предложением использовать COMPLETE, AUTO или PARTITIONS FALLBACK.

Если после изменения базовой таблицы SQL определения больше не поддаётся анализу, например из-за удаления столбца, используемого материализованным представлением, каждое обновление завершается ошибкой и материализованное представление необходимо удалить и создать заново.

Внутренний Stream недоступен

Если внутренний Stream отсутствует или больше не может использоваться, INCREMENTAL FALLBACK и AUTO сразу выполняют COMPLETE со значением IvmFallbackReason STREAM_UNSUPPORTED; обновление COMPLETE пересоздаёт внутренние Stream и повторно согласует позиции потребления. Строгое обновление INCREMENTAL завершается ошибкой, и необходимо один раз выполнить COMPLETE или AUTO.

Предварительный просмотр и анализ дельта-плана​

Просмотр плана обновления с помощью EXPLAIN​

Следующая команда только формирует план. Она не изменяет данные материализованного представления, позиции внутренних Stream или метаданные IVM:

EXPLAIN REFRESH MATERIALIZED VIEW <mv_name> INCREMENTAL;

По умолчанию план включает только Stream, в которых сейчас есть ещё не обработанные данные. Для просмотра полной структуры дельта-плана используйте:

EXPLAIN REFRESH MATERIALIZED VIEW <mv_name> INCREMENTAL WITH ALL STREAMS;

WITH ALL STREAMS также добавляет в план Stream, данные которых уже полностью обработаны, что полезно для изучения полной структуры инкрементального вычисления соединения нескольких таблиц. Сохранённое состояние при этом не изменяется.

Также можно посмотреть план полного обновления:

EXPLAIN REFRESH MATERIALIZED VIEW <mv_name> COMPLETE;

Просмотр записываемых строк с помощью DRY RUN​

WITH DRY RUN выполняет дельта-запрос следующего инкрементального обновления и возвращает результат клиенту, не записывая данные в материализованное представление и не продвигая позиции Stream:

REFRESH MATERIALIZED VIEW <mv_name> INCREMENTAL WITH DRY RUN;
REFRESH MATERIALIZED VIEW <mv_name> INCREMENTAL WITH DRY RUN LIMIT 100;
REFRESH MATERIALIZED VIEW <mv_name> INCREMENTAL WITH DRY RUN LIMIT 100 OFFSET 200;

Результат содержит строки, которые обновление фактически записало бы, поэтому, помимо бизнес-столбцов, он включает внутренние столбцы, такие как IVM, Sequence и Delete Sign. Пока данные базовых таблиц не меняются, повторные выполнения возвращают одинаковый результат.

DRY RUN поддерживает только обновление INCREMENTAL для IVM. Его нельзя сочетать с COMPLETE, EXPLAIN или обновлением партиций.

Внутренние Table Stream​

IVM автоматически управляет полным жизненным циклом своих внутренних Table Stream:

  • При создании IVM для каждой базовой таблицы, участвующей в инкрементальном сопровождении, создаётся внутренний Stream.
  • При изменении excluded_trigger_tables внутренние Stream добавляются или удаляются в соответствии с новым набором базовых таблиц.
  • При удалении IVM внутренние Stream, принадлежащие этому материализованному представлению, удаляются.
  • Во время обновления позиции потребления записываются и продвигаются отдельно для каждой партиции базовой таблицы.

Посмотреть соответствие между базовыми таблицами и внутренними Stream можно непосредственно с помощью mv_infos:

SELECT Name, IvmBaseTableStreams
FROM mv_infos("database" = "<database_name>")
WHERE Name = "<mv_name>";

Также можно проверить состояние Stream и объём необработанных данных по партициям в information_schema.table_streams и information_schema.table_stream_consumption.

Не управляйте внутренними Stream вручную

Имена внутренних Stream начинаются с __doris_ivm_stream_, и эти Stream зарезервированы для IVM. Не потребляйте их через INSERT INTO ... SELECT и не удаляйте и не пересоздавайте их вручную. Doris отклоняет обычное потребление внутренних Stream через INSERT INTO.

Семантику позиций потребления, снимков и конкурентного доступа Table Stream см. в разделах Основы Table Stream и Расширенные возможности Table Stream.

Свойства IVM​

Сначала выбирайте свойства с учётом идентификации строк, окна партиций и связей запуска обновления, затем оцените их влияние по разделам ниже.

СвойствоПо умолчаниюМожно изменитьДействие
ivm_use_full_keysfalseНетДобавляет ключи идентификации исходных строк в Unique Key материализованного представления, снижая риск коллизий хеша
ivm_partition_window_limitБез ограниченийДаСопровождает только последние N партиций указанных базовых таблиц, упорядоченных по значению партиции
excluded_trigger_tablesНетДаБазовые таблицы, для которых не создаётся внутренний Stream и которые сами по себе не запускают инкрементальное обновление

ivm_use_full_keys​

ivm_use_full_keys определяет, включает ли Unique Key материализованного представления также ключи идентификации исходных строк:

ПараметрОписание
ТипЛогический
По умолчаниюfalse
Можно изменитьНет; задаётся только при создании IVM
ДействиеСнижает риск коллизий при хешировании составного идентификатора строки
ЗатратыУвеличивает ширину ключа и затраты на хранение материализованного представления
PROPERTIES (
"ivm_use_full_keys" = "true"
)

Включайте это свойство, если запрос содержит соединения нескольких таблиц, UNION ALL или цепочки IVM и для вашей нагрузки особенно важно избежать коллизий хеша идентификаторов строк. Перед включением убедитесь, что типы и суммарная длина исходных ключей соответствуют ограничениям Unique Key в Doris.

ivm_partition_window_limit​

ivm_partition_window_limit ограничивает каждое обновление IVM последними N партициями указанных базовых таблиц, упорядоченными по значению партиции. Формат: <table_name>:<partition_count>, несколько таблиц разделяются запятыми:

PROPERTIES (
"ivm_partition_window_limit" = "orders:7,users:1"
)

Это свойство ограничивает только партиции, читаемые при инкрементальном обновлении IVM. Оно не задаёт политику хранения партиций материализованного представления. COMPLETE по-прежнему читает базовые таблицы целиком и строит полный результат. Изменения в старых партициях, исключённых окном, никогда не попадают в последующие результаты IVM, поэтому эта настройка приводит к потере части изменений и подходит только для нагрузок, которым явно нужны лишь последние партиции.

Это свойство можно изменить с помощью ALTER MATERIALIZED VIEW ... SET. После расширения окна или снятия ограничения ранее проигнорированные изменения невозможно восстановить из существующих позиций потребления, поэтому базовое состояние материализованного представления становится недействительным и следующее обновление перестраивает всё материализованное представление. См. раздел Первое обновление и перестроение базового состояния.

excluded_trigger_tables​

Для базовых таблиц, перечисленных в excluded_trigger_tables, не создаётся внутренний Stream, и сами по себе они не запускают инкрементальное обновление. Когда обновление запускается изменениями в других базовых таблицах, Doris всё равно читает текущие данные этих таблиц во время вычислений.

Исключайте только редко меняющиеся таблицы измерений или таблицы, изменения которых могут ждать обновления, запущенного другой базовой таблицей. Если исключить часто меняющуюся таблицу, после её самостоятельного изменения результаты материализованного представления будут оставаться устаревшими.

Свойства ресурсов и партиций​

  • workload_group: Workload Group, используемая задачами обновления для ограничения ресурсов CPU и памяти.
  • partition_sync_limit, partition_sync_time_unit: управляют диапазоном партиций, которые материализованное представление хранит и синхронизирует. Это отличается от окна инкрементального чтения, задаваемого ivm_partition_window_limit.
  • refresh_partition_num: определяет, сколько партиций обрабатывает один INSERT при обновлении партиций и переходе к резервному способу. Не управляет количеством строк в дельте IVM.

Все свойства материализованных представлений описаны в разделе CREATE ASYNC MATERIALIZED VIEW.

Ограничения и примечания​

  • IVM можно включить только при создании материализованного представления. Нельзя превратить обычное материализованное представление в IVM с помощью ALTER MATERIALIZED VIEW или задать для IVM другой способ обновления по умолчанию. Для переключения пересоздайте материализованное представление.
  • Включение changelog увеличивает затраты на запись и хранение. Таблицам Unique Key MoW также требуется читать и хранить значения до каждого обновления. Включайте его только для базовых таблиц, которым действительно нужно IVM, и перед использованием в рабочей среде оцените пропускную способность загрузки на реалистичных нагрузках.
  • IVM по-прежнему обновляется через асинхронные задачи и не обеспечивает согласованность с транзакциями базовых таблиц в реальном времени. Задержка обновления зависит от способа запуска, времени ожидания в очереди и времени выполнения дельта-плана. Минимальный интервал расписания составляет 1 минуту, см. раздел Настройка интервала автоматического обновления.
  • Сложные вложенные внешние соединения увеличивают дельта-план, особенно при сложных поддеревьях на стороне, порождающей NULL. Если планирование или обновление становится слишком затратным, упростите соединения или сначала материализуйте сложное поддерево в виде IVM нижнего уровня.
  • MIN, MAX и агрегации Bitmap требуют полного пересчёта в некоторых сценариях удаления. В рабочей среде используйте INCREMENTAL FALLBACK или AUTO и отслеживайте IvmFallbackReason.
  • Ограничения changelog по моделям таблиц, типам столбцов, изменениям схемы и удалениям непосредственно применяются к IVM. См. раздел Поддерживаемые возможности и ограничения changelog.

Частые вопросы​

Если создание завершается ошибкой, обновление переходит к резервному способу или данные не обновляются, используйте следующую таблицу для поиска причины.

ПроблемаДиагностика и решение
CREATE MATERIALIZED VIEW ... REFRESH INCREMENTAL завершается ошибкойУбедитесь, что на FE включены enable_feature_changelog и enable_table_stream, базовые таблицы соответствуют требованиям к модели таблицы и changelog, а SQL определения входит в область поддержки IVM
У строгой задачи инкрементального обновления RefreshMode имеет значение COMPLETE или IvmRebuiltPartitions больше 0Это автоматическое перестроение при первом обновлении или после аннулирования базового состояния, а не ошибка. Условия описаны в разделе Первое обновление и перестроение базового состояния
Обновление PARTITIONS без FALLBACK завершается ошибкой The refresh baseline of MV ... was invalidatedБазовое состояние всего материализованного представления недействительно. Выполните обновление с COMPLETE, AUTO или PARTITIONS FALLBACK
ON SCHEDULE EVERY 30 SECOND завершается ошибкой interval time unit can not be secondМинимальный интервал расписания: EVERY 1 MINUTE, а единицей может быть только MINUTE, HOUR, DAY или WEEK. Для меньшей задержки используйте ON COMMIT, см. раздел Настройка интервала автоматического обновления
Фактическая задержка обновлений по расписанию превышает заданный интервалЗадачи одного материализованного представления выполняются последовательно, а лишние события запуска пропускаются, если одно обновление длится дольше интервала. Проверьте длительность задач в tasks("type"="mv") и метрику FE async_materialized_view_task_skip_num, затем увеличьте интервал, упростите определение или выделите обновлению больше ресурсов через workload_group
Задача обновления переходит к COMPLETEЗапросите IvmFallbackReason в tasks("type"="mv") и примите меры в соответствии с причинами из раздела Порядок перехода к резервным способам
Представление не обновляется после самостоятельного изменения исключённой базовой таблицыТаблицы в excluded_trigger_tables сами по себе не запускают обновления. Дождитесь обновления, запущенного другими базовыми таблицами, или выполните обновление COMPLETE. После удаления таблицы из excluded_trigger_tables следующее обновление перестроит всё материализованное представление
Можно ли вручную потреблять внутренние Stream или сбрасывать их состояниеНет. Внутренние Stream зарезервированы для IVM; Doris управляет их жизненным циклом и позициями потребления

Рекомендации​

  1. Сначала сравните количество изменённых строк с размером партиции. Предпочитайте IVM, когда изменения составляют небольшую долю партиции; если они охватывают большую часть партиции, PARTITIONS может быть проще.
  2. В рабочей среде разрешайте безопасный переход к резервным способам. Используйте INCREMENTAL FALLBACK или AUTO, чтобы удаления или проблемы с базовым состоянием не приводили к длительным сбоям обновления.
  3. Выбирайте способ запуска и интервал с учётом длительности обновления. Минимальный интервал расписания составляет 1 минуту, и интервал должен превышать длительность одного инкрементального обновления при обычной нагрузке; используйте ON COMMIT, если нужна меньшая задержка и базовые таблицы фиксируют транзакции не слишком часто.
  4. Сначала проверьте базовое состояние. Первое обновление полностью вычисляет все данные; проверьте его результат, прежде чем полагаться на инкрементальные обновления для его сопровождения.
  5. Используйте Unique Key MoW для нагрузок с обновлениями и удалениями. Таблицы Duplicate Key подходят только для данных, в которые выполняется лишь добавление.
  6. Изолируйте ресурсы задач обновления. Используйте workload_group, чтобы сложные дельта-планы не конкурировали с пользовательскими запросами.
  7. Отслеживайте задачи и необработанные данные Stream. Совместно наблюдайте за tasks("type"="mv"), mv_infos и information_schema.table_stream_consumption.
  8. Регулярно анализируйте причины перехода к резервным способам. Редкие переходы защищают корректность; постоянные переходы указывают на необходимость проверить структуру запроса, непрерывность changelog или базовое состояние.

Очистка окружения примера​

DROP MATERIALIZED VIEW orders_by_status;
DROP TABLE orders;
DROP DATABASE ivm_demo;

При удалении IVM Doris также удаляет его внутренние Table Stream. Отдельная команда DROP STREAM не требуется.

См. также​

На этой странице