メインコンテンツまでスキップ
に最終更新

増分ビュー保守(IVM)

増分ビュー保守(IVM)は、ベーステーブルの行単位の変更に基づいて非同期マテリアライズドビューを維持します。通常の非同期マテリアライズドビューは全データまたは影響を受けるすべてのパーティションを再計算しますが、IVM は更新間にベーステーブルで挿入、更新、削除された行だけを計算し、その変更をマテリアライズドビューに適用します。

実験的機能

IVM は Doris 5.0.0 以降で利用できます。実験的機能であり、デフォルトでは無効です。使用する前に、FE の設定で changelog と Table Stream を有効にしてください。前提条件を参照してください。

IVM を使用する場面​

更新方法を選ぶ前に、更新ごとの変更行数、影響を受けるパーティションのサイズ、整合性の要件を比較してください。

シナリオ推奨理由
ベーステーブルで挿入、更新、削除が頻繁に発生し、変更行がテーブルまたはパーティションの一部にとどまるIVM を使用する更新間の行単位の変更だけを計算するため、パーティション全体を再計算せずに済む
変更が少数のパーティションに集中し、それらの再計算に無理がないPARTITIONS を使用する設定が簡単で、影響を受けるパーティションを再計算する
最初のベースラインを構築する、データ量が少ない、または IVM のベースラインを復旧するCOMPLETE を使用するマテリアライズドビューの全データを再計算する
結果にベーステーブルとのリアルタイムなトランザクション整合性が必要非同期マテリアライズドビューを使用しないIVM も非同期タスクで更新するため、リアルタイムの整合性は提供しない

パーティション単位の増分更新との違い​

Doris は非同期マテリアライズドビューに対して複数の更新方法をサポートします。IVM は INCREMENTAL に対応します。PARTITIONS が提供するパーティション単位の増分更新とは区別してください。

更新方法計算の粒度適した場面条件を満たさない場合の動作
INCREMENTAL行単位の変更変更行数がテーブル全体や影響を受けるパーティションよりはるかに少ないデフォルトでは失敗する。FALLBACK を指定するとフォールバックできる
PARTITIONS影響を受けるマテリアライズドビューの各パーティション変更が少数のパーティションに集中し、その再計算に無理がないデフォルトでは失敗する。FALLBACK を指定すると COMPLETE にフォールバックする
COMPLETE全データデータ量が少ない、最初のベースラインを作る、または IVM のベースラインを復旧する常に全データを再計算する
AUTODoris が自動的に選択利用可能な方法を Doris に選ばせたい利用可能な方法として IVM、パーティション更新、完全更新を順に試す

INCREMENTAL FALLBACK が変更するのは更新時の失敗の扱いだけです。マテリアライズドビューの作成時には、定義 SQL が IVM の要件を満たす必要があります。FALLBACK を指定しても、サポート対象外の定義は作成時のチェックを通過できません。

仕組み​

IVM は Doris の changelog と Table Stream の機能を再利用します。

IVM の仕組み:ベーステーブルの変更は changelog を経由して内部 Table Stream に流れる。差分プランは未消費の変更と整合性のあるスナップショットを組み合わせてマテリアライズドビューを更新し、同じトランザクションで消費オフセットを進める

IVM をサポートするマテリアライズドビューを作成すると、Doris は増分保守に参加する各ベーステーブルに内部 Table Stream を作成します。内部 Stream の名前は __doris_ivm_stream_ で始まります。これらの Stream を作成、消費、削除する必要はありません。

更新時に、Doris は各ベーステーブルの未消費の変更から差分プランを生成します。結合クエリでは、消費オフセットに対応するベーステーブルのスナップショットも読み取り、複数テーブルの計算で整合性のあるデータ境界を使用します。マテリアライズドビューのデータと消費オフセットは同じトランザクションでコミットされます。トランザクションが失敗すると、どちらもコミットされません。

前提条件​

バージョンと FE の設定​

  1. Doris 5.0.0 以降を使用します。

  2. すべての FE の fe.conf に次のオプションを設定し、FE を再起動します。

    enable_feature_changelog = true
    enable_table_stream = true

どちらも静的な設定であり、動的には変更できません。enable_feature_changelog は changelog とグローバルコミットタイムスタンプを有効にし、enable_table_stream は Table Stream の DDL と内部 Stream の管理を有効にします。

ベーステーブルの要件​

増分保守に参加するベーステーブルは、Internal Catalog 内の Doris OLAP 内部テーブルであり、次の要件を満たす必要があります。

ベーステーブルのモデルサポート要件と動作
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. changelog を有効にした Unique Key Merge-on-Write ベーステーブルを作成します。
  2. REFRESH INCREMENTAL FALLBACK を指定して IVM を作成します。
  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 を作成する​

CREATE MATERIALIZED VIEW で REFRESH INCREMENTAL を指定します。この例では 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 を使用します。いずれも changelog と更新前イメージを有効にした Unique Key Merge-on-Write テーブルです。クイックスタートで作成した 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 句内の別名付きサブクエリ。入れ子にできるが、集約は含められない
定数行FROM のない SELECT で、常に定数からなる単一行を返すもの。例:SELECT -1, 'unknown'。UNION ALL の分岐、結合の片側、定義全体のいずれにもできる
GROUP BY 集約集約は、上位に射影のみを持つクエリの最も外側の演算である必要がある。グループ化列や集約結果に対する決定的な式はサポートされる
GROUPING SETS、ROLLUP、CUBEGROUPING() および 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 の両側)には、excluded_trigger_tables に列挙された changelog のない Duplicate Key テーブルを使用できない。このようなテーブルの行には固定の行識別子がないため
入れ子の外部結合外部結合で 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 でない行だけを数えます。店舗の注文 104 の customer_id は 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 |
+---------+----------+-----------------+

COUNT(DISTINCT customer_id) のような DISTINCT 付きの集約関数はサポートされません。非負整数列の正確な重複排除カウントには、代わりに 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 INCREMENTALIVM のサポート範囲を厳密にチェックし、対象外なら作成に失敗するIVM だけを試し、IVM が失敗するとタスクも失敗する
REFRESH INCREMENTAL FALLBACK厳密モードと同じで、IVM のチェックを通過する必要があるまず IVM を試し、失敗理由に応じてフォールバックする
REFRESH AUTO定義が IVM をサポートするか確認し、サポートしなければ通常の非同期マテリアライズドビューを作成するIVM 対応ビューでは、IVM、パーティション更新、完全更新を順に試す
REFRESH PARTITIONS [FALLBACK]マテリアライズドビューで PARTITION BY を定義する必要がある変更されたパーティションを再計算する。FALLBACK があれば完全更新にフォールバックできる
REFRESH COMPLETEIVM メタデータを作成しない常に完全更新する

自動更新間隔の設定​

IVM は非同期マテリアライズドビューのトリガー方式を再利用し、独自のトリガー構文を持ちません。REFRESH INCREMENTAL [FALLBACK] の後の ON 句でトリガーを指定します。

トリガー構文説明
手動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、AUTOCOMPLETE を直接実行する
スケジュールまたはコミット時トリガーCOMPLETE を実行する。自動更新間隔の設定を参照

これらのタスクの RefreshMode はいずれも COMPLETE で、IvmFallbackReason は空です。したがって、IVM の作成後に COMPLETE を別途実行する必要はありません。クイックスタートでは、ベースライン構築の手順を明確にするためだけに COMPLETE を明示的に実行しています。

一部のパーティションのベースラインが無効になる場合

次のベーステーブル操作はメタデータだけを変更し、changelog に変更レコードを書き込みません。そのため、増分更新ではどの行が削除または置換されたかを変更から判別できません。これらのベーステーブルパーティションを読み取るマテリアライズドビューのパーティションを再構築する必要があります。

  • 一部のパーティションだけを切り詰める場合を含む TRUNCATE TABLE
  • ALTER TABLE ... DROP PARTITION
  • RECOVER PARTITION
  • デフォルトの厳密な範囲一致を使用する ALTER TABLE ... REPLACE PARTITION

マテリアライズドビューがベーステーブルのパーティション列でパーティション化されている場合、Doris は影響を受けるマテリアライズドビューのパーティションだけにマークを付けます。次の更新では、厳密な INCREMENTAL を指定した場合も、まずそれらのパーティションを再構築し、その後にほかのパーティションを増分更新します。tasks("type"="mv") の IvmRebuiltPartitions 列には、更新時に追加で再構築したパーティション数が記録されます。ベーステーブルのパーティションを削除した場合、対応するマテリアライズドビューのパーティションはパーティション同期時に削除されるため、再構築は不要です。

マテリアライズドビューがパーティション化されていない場合、または Doris が影響を受けるマテリアライズドビューのパーティションを特定できない場合は、後述のようにビュー全体のベースラインが無効になります。excluded_trigger_tables に列挙されたベーステーブルでこれらの操作を行っても、ベースラインは無効になりません。

マテリアライズドビュー全体のベースラインが無効になる場合

次の状況ではマテリアライズドビュー全体のベースラインが無効になり、ビューは SCHEMA_CHANGE 状態になります。この状態は mv_infos の State 列と SchemaChangeDetail 列で確認できます。

  • ベーステーブルで DROP COLUMN、MODIFY COLUMN、RENAME COLUMN、RENAME(テーブル名の変更)、REPLACE WITH TABLE を実行した場合。ADD COLUMN ではベースラインは無効になりません。changelog を有効にしたテーブルでは MODIFY COLUMN と RENAME COLUMN は許可されません。changelog の DDL 制限を参照してください。
  • ベーステーブルで厳密な範囲一致を使用せずに REPLACE PARTITION を実行した場合、または前述のパーティション操作を特定のマテリアライズドビューパーティションに対応付けられない場合。
  • プロパティの変更によってマテリアライズドビューの保守対象が広がった場合。たとえば、ベーステーブルを excluded_trigger_tables から除外する、ivm_partition_window_limit を拡大または削除する、partition_sync_limit の範囲を広げる場合。

マテリアライズドビューが SCHEMA_CHANGE 状態の間は、次の INCREMENTAL、INCREMENTAL FALLBACK、AUTO、COMPLETE、または PARTITIONS FALLBACK 更新でビュー全体を再構築し、成功すると状態が NORMAL に戻ります。FALLBACK のない PARTITIONS 更新は失敗し、代わりに COMPLETE、AUTO、または PARTITIONS FALLBACK を使用するよう求められます。

ベーステーブルの変更後に定義 SQL を解析できなくなった場合、たとえばマテリアライズドビューで使用する列が削除された場合は、すべての更新が失敗します。その場合はマテリアライズドビューを削除して作成し直す必要があります。

内部 Stream が使用できない場合

内部 Stream が存在しないか使用できなくなった場合、INCREMENTAL FALLBACK と AUTO は IvmFallbackReason を STREAM_UNSUPPORTED として COMPLETE を直接実行します。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 がサポートするのは IVM の INCREMENTAL 更新だけです。COMPLETE、EXPLAIN、パーティション更新とは組み合わせられません。

内部 Table Stream​

IVM は内部 Table Stream のライフサイクル全体を自動管理します。

  • IVM の作成時に、増分保守に参加する各ベーステーブルに内部 Stream が作成されます。
  • excluded_trigger_tables を変更すると、新しいベーステーブルの集合に応じて内部 Stream が追加または削除されます。
  • IVM を削除すると、そのマテリアライズドビューが所有する内部 Stream も削除されます。
  • 更新時には、消費オフセットがベーステーブルのパーティションごとに記録され、進められます。

mv_infos を使用すると、ベーステーブルと内部 Stream の対応関係を直接確認できます。

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

information_schema.table_streams と information_schema.table_stream_consumption では、Stream の状態とパーティションごとの未処理量も確認できます。

内部 Stream を手動で管理しないでください

内部 Stream の名前は __doris_ivm_stream_ で始まり、IVM 専用です。INSERT INTO ... SELECT で消費したり、手動で削除または再作成したりしないでください。Doris は通常の INSERT INTO による内部 Stream の消費を拒否します。

Table Stream のオフセット、スナップショット、同時実行のセマンティクスについては、Table Stream BasicsとTable Stream Advancedを参照してください。

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 にソース行の識別キーも含めるかどうかを制御します。

項目説明
型Boolean
デフォルトfalse
変更可能いいえ。IVM の作成時にのみ設定できる
効果複合行識別子をハッシュ化する際の衝突リスクを下げる
コストマテリアライズドビューのキー幅とストレージ使用量が増える
PROPERTIES (
"ivm_use_full_keys" = "true"
)

クエリに複数テーブルの結合、UNION ALL、または連鎖した IVM が含まれ、ワークロードで行識別子のハッシュ衝突の回避が重要な場合は、このプロパティを有効にします。有効にする前に、ソースキーの型と合計長が Doris の Unique Key の制限を満たすことを確認してください。

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 を有効にできるのはマテリアライズドビューの作成時だけです。ALTER MATERIALIZED VIEW で通常のマテリアライズドビューを IVM に変えたり、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 より大きい最初の更新または無効になったベースラインの自動再構築であり、エラーではない。発生条件については最初の更新とベースラインの再構築を参照
FALLBACK のない PARTITIONS 更新が 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 にフォールバックするtasks("type"="mv") の IvmFallbackReason をクエリし、フォールバックの順序に記載された理由に応じて対処する
除外したベーステーブルだけが変更されてもビューが更新されない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 を実行する必要はありません。

関連項目​