このトピックでは、SQL 開発の主要パラメーターについて説明し、解説と使用例を提供します。
table.exec.sink.keyed-shuffle
プライマリキーを持つテーブルにデータを書き込む際に順序が乱れる問題に対処するには、table.exec.sink.keyed-shuffle パラメーターを使用してハッシュシャッフルを実行します。この操作により、同じプライマリキーを持つレコードが同じオペレーターインスタンスにルーティングされ、順序が乱れる問題を軽減できます。
注意事項
-
ハッシュシャッフル操作は、上流オペレーターが同じプライマリキーの更新レコードの順序を保証できる場合にのみ有効です。そうでない場合、この操作では順序が乱れる問題を解決できません。
-
エキスパートモードでオペレーターの並列度を変更した場合、次の並列度ルールは適用されません。
使用可能な値
-
AUTO (デフォルト):シンクの並列度が 1 ではなく、かつ上流オペレーターの並列度と異なる場合、データがシンクに流れる際に Flink がプライマリキーに対して自動的にハッシュシャッフルを実行します。
-
FORCE:シンクの並列度が 1 ではない場合、データがシンクに流れる際に Flink がプライマリキーに対して強制的にハッシュシャッフルを実行します。
-
NONE:Flink は、シンクと上流オペレーターの並列度に基づくハッシュシャッフルを実行しません。
例
-
パラメーターを AUTO に設定
-
SQL ストリーミングジョブを作成し、次の SQL コードをコピーして、ジョブを [Deploy] します。このコードでは、シンクの並列度を 2 に明示的に設定しています。
CREATE TEMPORARY TABLE s1 ( a INT, b INT, ts TIMESTAMP(3) ) WITH ( 'connector'='datagen', 'rows-per-second'='1', 'fields.ts.kind'='random','fields.ts.max-past'='5s', 'fields.b.kind'='random','fields.b.min'='0','fields.b.max'='10' ); CREATE TEMPORARY TABLE sink ( a INT, b INT, ts TIMESTAMP(3), PRIMARY KEY (a) NOT ENFORCED ) WITH ( 'connector'='print', -- sink.parallelism パラメーターを使用して、シンクの並列度を直接指定できます。 'sink.parallelism'='2' ); INSERT INTO sink SELECT * FROM s1; -- 動的テーブルオプションを使用して、シンクの並列度を指定することもできます。 --INSERT INTO sink /*+ OPTIONS('sink.parallelism' = '2') */ SELECT * FROM s1; -
[ジョブ O&M] ページで、[デプロイ詳細] タブの [リソース設定] セクションで、[並列度] を 1 に設定します。[その他の設定] の [実行パラメーター設定] セクションで、
table.exec.sink.keyed-shuffleパラメーターを設定したり、table.exec.sink.keyed-shuffle: AUTOを明示的に追加したりしないでください (どちらも同じ効果です)。 -
ジョブを[開始]します。[ステータス] タブでは、上流オペレーターからシンクへのデータ接続は HASH です。

-
-
パラメーターを FORCE に設定
-
SQL ストリーミングジョブを作成し、次の SQL コードをコピーして、ジョブを [Deploy] します。このコードでは、シンクの並列度を明示的に指定していません。
CREATE TEMPORARY TABLE s1 ( a INT, b INT, ts TIMESTAMP(3) ) WITH ( 'connector'='datagen', 'rows-per-second'='1', 'fields.ts.kind'='random','fields.ts.max-past'='5s', 'fields.b.kind'='random','fields.b.min'='0','fields.b.max'='10' ); CREATE TEMPORARY TABLE sink ( a INT, b INT, ts TIMESTAMP(3), PRIMARY KEY (a) NOT ENFORCED ) WITH ( 'connector'='print' ); INSERT INTO sink SELECT * FROM s1; -
[ジョブ O&M] ページの [デプロイ詳細] タブの [リソース設定] セクションで [並列度] を 2 に設定し、[ランタイムパラメータ設定] セクションで [その他の設定] に
table.exec.sink.keyed-shuffle: FORCEを追加します。 -
ジョブを [開始] した後、[ステータス] タブに移動します。シンクとアップストリームオペレーターの両方の並列度は 2 で、データ接続は HASH に変更されています。

-
table.exec.mini-batch.size
このパラメーターは、マイクロバッチ処理でバッファリング可能なレコードの最大数を制御します。この数に達すると、計算がトリガーされてデータが出力されます。このパラメーターは、table.exec.mini-batch.enabled および table.exec.mini-batch.allow-latency と一緒に使用される場合にのみ有効になります。MiniBatch 最適化の詳細については、「MiniBatch 集計」および「MiniBatch 通常結合」をご参照ください。
注意事項
ジョブの開始前に、[パラメーター] セクションでこのパラメーターを明示的に設定しない場合、ミニバッチモードではマネージドメモリを使用してデータをバッファリングします。次のいずれかの条件で最終計算とデータ出力がトリガーされます。
-
MiniBatchAssigner オペレーターからウォーターマークメッセージを受信した場合。
-
マネージドメモリがフルになった場合。
-
チェックポイントの開始前。
-
ジョブが停止した場合。
使用可能な値
-
-1 (デフォルト):マネージドメモリを使用してデータをバッファリングすることを示します。
-
その他の負の Long 値:デフォルト設定と同じです。
-
その他の正の Long 値:ヒープメモリを使用してデータをバッファリングすることを示します。バッファリングされたレコード数がこの値 (N) に達すると、システムは出力操作を自動的にトリガーします。
例
-
SQL ストリーミングジョブを作成し、次の SQL コードをコピーして、ジョブを [Deploy] します。
CREATE TEMPORARY TABLE s1 ( a INT, b INT, ts TIMESTAMP(3), PRIMARY KEY (a) NOT ENFORCED, WATERMARK FOR ts AS ts - INTERVAL '1' SECOND ) WITH ( 'connector'='datagen', 'rows-per-second'='1', 'fields.ts.kind'='random', 'fields.ts.max-past'='5s', 'fields.b.kind'='random', 'fields.b.min'='0', 'fields.b.max'='10' ); CREATE TEMPORARY TABLE sink ( a INT, b BIGINT, PRIMARY KEY (a) NOT ENFORCED ) WITH ( 'connector'='print' ); INSERT INTO sink SELECT a, sum(b) FROM s1 GROUP BY a; -
[ジョブ O&M] ページの [デプロイ詳細] タブで、[ランタイムパラメーター設定] セクションの [その他の設定] フィールドに、
table.exec.mini-batch.enabled: trueとtable.exec.mini-batch.allow-latency: 2sパラメーターを設定し、table.exec.mini-batch.sizeはデフォルト値の -1 を使用するため未設定のままにします。 -
ジョブを [開始] します。 [ステータス] タブでは、ジョブトポロジに MiniBatchAssigner、LocalGroupAggregate、および GlobalGroupAggregate オペレーターが含まれます。
table.exec.agg.mini-batch.output-identical-enabled
State TTL が有効な場合、データが消費された後に集計結果が変わらなければ、MinibatchGlobalAgg ノードと MinibatchAgg ノードはデフォルトでダウンストリームに重複データを送信しません。これにより、ダウンストリームのステートフルノードは長期間アップストリームからデータを受信しないため、そのステートが期限切れになる可能性があります。このパラメーターは、State TTL が有効で集計結果が変わらない場合に、ダウンストリームに重複データを送信し続けるかどうかを制御します。このパラメーターを true に設定すると、このような場合に MinibatchGlobalAgg ノードと MinibatchAgg ノードがデータを送信するようになります。ジョブの集計結果が、設定された State TTL よりも頻繁に変更される場合、このパラメーターを手動で設定する必要はありません。コミュニティの問題の詳細については、「FLINK-33936」をご参照ください。
注意事項
-
このパラメーターは VVR 8.0.8 以降でのみ有効です。VVR 8.0.8 より前のバージョンでは、このパラメーターを false に設定した場合と同等の動作になります。
-
値を false から true に変更すると、MinibatchGlobalAgg オペレーターと MinibatchAgg オペレーターから下流に送信されるデータ量が増加し、下流オペレーターの負荷が高まる可能性があります。
使用可能な値
-
false (デフォルト):ステート TTL が有効な場合、MinibatchGlobalAgg オペレーターと MinibatchAgg オペレーターは、集計結果が変化しないときに下流にデータを出力しません。
-
true:ステート TTL が有効な場合、集計結果が変化しなくても、MinibatchGlobalAgg オペレーターと MinibatchAgg オペレーターは更新 (重複) レコードを下流に引き続き出力します。
例
-
SQL ストリーミングジョブを作成し、次の SQL コードをコピーして、ジョブを [Deploy] します。
create temporary table src( a int, b string ) with ( 'connector' = 'datagen', 'rows-per-second' = '10', 'fields.a.min' = '1', 'fields.a.max' = '1', 'fields.b.length' = '3' ); create temporary table snk( a int, max_length_b bigint ) with ( 'connector' = 'blackhole' ); insert into snk select a, max(CHAR_LENGTH(b)) from src group by a; -
[ジョブ O&M] ページの [デプロイ詳細] タブの [ランタイムパラメーター設定] エリアの [その他の設定] セクションで、
table.exec.mini-batch.enabled: trueとtable.exec.mini-batch.allow-latency: 2sパラメーターを設定して、Minibatch Aggregate の最適化を有効にします。 -
ジョブを[開始]します。[ステータス] タブでは、ジョブに MinibatchGlobalAggregate 演算子が含まれています。演算子の「+」記号をクリックすると、集計結果が変更されない場合に GlobalGroupAggregate 演算子がデータをダウンストリームに送信しないことを確認できます。
オペレーターの RecordsIn が 19、RecordsOut が 1 と表示されます。これは、19 件の入力レコードから 1 件の集計出力のみが生成されたことを意味します。
-
ジョブを停止し、[ジョブ O&M] ページの [デプロイ詳細] ページにある [実行パラメータ設定] セクションの [その他の設定] に、パラメーター
table.exec.agg.mini-batch.output-identical-enabled: trueを追加します。 -
ジョブを[開始] します。[ステータス] タブでは、ジョブに MinibatchGlobalAggregate オペレーターが含まれていることを確認できます。オペレーターの「+」記号をクリックすると、集計結果が変更されていない場合でも GlobalGroupAggregate オペレーターがダウンストリームにデータを送信することを確認できます。ジョブを再起動すると、[ステータス] タブに、[GlobalGroupAggregate] オペレーターの [RecordsIn] と [RecordsOut] が両方とも 94 であることが表示されます。これは、
table.exec.agg.mini-batch.output-identical-enabled: trueが有効になっている場合、集計結果が変更されなくてもオペレーターがダウンストリームにデータを送信することを示しています。
table.exec.async-lookup.key-ordered-enabled
ディメンションテーブル結合を使用してデータエンリッチメントを行う場合、非同期モードを有効にするとスループットが向上することがよくあります。ディメンションテーブル結合では、table.exec.async-lookup.output-mode パラメーターと、入力が更新ストリームであるかどうかによって、非同期 I/O 操作の出力順序が決まります。
|
出力モード |
更新ストリーム |
非更新ストリーム |
|
ORDERED |
順序付きモード |
順序付きモード |
|
ALLOW_UNORDERED |
順序付きモード |
順序なしモード |
表に示すとおり、更新ストリームと ALLOW_UNORDERED の組み合わせでは、順序付きモードを使用して正確性を確保しますが、スループットが一部犠牲になります。このシナリオを最適化するために、table.exec.async-lookup.key-ordered-enabled パラメーターが導入されました。このパラメーターは、更新ストリームの正確性のセマンティクスと非同期 I/O のスループット性能のバランスを取ります。ストリーム内で同じ更新キー (changelog のプライマリキーと見なすことができます) を持つメッセージは、オペレーターに入力された順序で処理されます。
-
順序付きモード: このモードはストリームの順序を保持します。結果メッセージは、非同期リクエストがトリガーされた順序 (メッセージがオペレーターに入力された順序) と同じ順序で出力されます。
-
順序不同モード:非同期リクエストが完了するとすぐに結果メッセージが出力されるため、非同期 I/O オペレーターによってストリーム内のメッセージの順序が変更されます。詳細については、「非同期 I/O | Apache Flink」をご参照ください。
ユースケース
-
同じ更新キーを持つメッセージが時間とともに少なくなるストリームにおいて、ディメンションテーブル結合でキーごとの処理順序を保持するためにこの最適化を使用します。
-
プライマリキーを持つ変更データキャプチャ (CDC) ストリームで、ディメンションテーブル結合によるデータエンリッチメントを行い、シンクのプライマリキーがソースのプライマリキーと一致するシンクに書き込みます。ディメンションテーブル結合の結合キーはプライマリキーとは異なり、ディメンション側の結合キーがプライマリキーです。この最適化では、更新キーとして派生した CDC プライマリキーによってシャッフルが行われます。同じシナリオで SHUFFLE_HASH 最適化を有効にする場合と比較して、この方法では、より高い並列度でシンクの前に SinkMaterializer オペレーターが生成されるのを回避します。これにより、そのオペレーターに起因する潜在的なパフォーマンスの問題、特に長時間実行中に蓄積される可能性のある大きなステートを回避できます。SinkUpsertMaterializer の詳細については、「推奨事項」をご参照ください。
-
ディメンションテーブル結合の結合キーがプライマリキーと異なり、ディメンション側の結合キーがプライマリキーであり、その後にランク演算子が続く場合、この最適化は、更新キーとして導出される CDC プライマリキーによってシャッフルを行うことで、同じシナリオで SHUFFLE_HASH 最適化を有効にする場合と比較して UpdateFastRank が RetractRank に低下するのを防ぎます。RetractRank を UpdateFastRank に最適化する方法の詳細については、「TopN 最適化手法」をご参照ください。
注意事項
-
ストリームに更新キーがない場合、行全体がキーとして使用されます。
-
同じ更新キーが短時間に頻繁に更新される場合、同じ更新キーのレコードが厳密な順序で処理されるため、スループットが低下します。
-
元の非同期ディメンションテーブル結合と比較すると、Key-Ordered モードではキー付きステートが導入されます。このモードの有効化/無効化はステート互換性に影響します。
-
この機能は、ディメンションテーブル結合の入力が更新ストリームであり、かつ
table.exec.async-lookup.output-mode='ALLOW_UNORDERED'とtable.exec.async-lookup.key-ordered-enabled='true'を設定した場合にのみ、VVR 8.0.10 以降で有効になります。
使用可能な値
-
false (デフォルト):Key-Ordered モードを無効にします。
-
true:Key-Ordered モードを有効にします。
例
-
次の例では、非同期 Hologres ディメンションテーブル結合を使用します。SQL ストリーミングジョブを作成し、次の SQL コードをコピーして、ジョブを [Deploy] します。
Hologres コネクタの詳細については、「Hologres」をご参照ください。
CREATE TEMPORARY TABLE bid_source( auction BIGINT, bidder BIGINT, price BIGINT, channel VARCHAR, url VARCHAR, dateTime TIMESTAMP(3), extra VARCHAR, proc_time AS proctime(), WATERMARK FOR dateTime AS dateTime - INTERVAL '4' SECOND ) WITH ( 'connector' = 'kafka', -- insert-only ではないストリームコネクター 'topic' = 'user_behavior', 'properties.bootstrap.servers' = 'localhost:9092', 'properties.group.id' = 'testGroup', 'scan.startup.mode' = 'earliest-offset', 'format' = 'canal-json' ); CREATE TEMPORARY TABLE users ( user_id STRING PRIMARY KEY NOT ENFORCED, -- プライマリキーを定義 user_name VARCHAR(255) NOT NULL, age INT NOT NULL ) WITH ( 'connector' = 'hologres', -- 非同期ルックアップをサポートするコネクター 'async' = 'true', 'dbname' = 'holo db name', -- Hologres データベースの名前 'tablename' = 'schema_name.table_name', -- データを受信する Hologres テーブルの名前 'username' = 'access id', -- Alibaba Cloud アカウントの AccessKey ID 'password' = 'access key', -- Alibaba Cloud アカウントの AccessKey Secret 'endpoint' = 'holo vpc endpoint' -- Hologres インスタンスの VPC エンドポイント ); CREATE TEMPORARY TABLE bh ( auction BIGINT, age INT ) WITH ( 'connector' = 'blackhole' ); INSERT INTO bh SELECT bid_source.auction, u.age FROM bid_source JOIN users FOR SYSTEM_TIME AS OF bid_source.proc_time AS u ON bid_source.channel = u.user_id; -
[ジョブ O&M] ページの [デプロイ詳細] タブ、[ランタイムパラメーター設定] エリアの [その他の設定] セクションで、
table.exec.async-lookup.output-mode='ALLOW_UNORDERED'およびtable.exec.async-lookup.key-ordered-enabled='true'パラメーターを設定します。 -
ジョブを [開始] します。 [ステータス] タブでは、ジョブの async 属性が KEY_ORDERED:true であることを確認できます。
table.optimizer.window-join-enabled
このパラメーターは、ウィンドウ結合操作を有効にするかどうかを制御します。有効にすると、Flink は対応する実行計画を ウィンドウ結合として最適化します。小さなウィンドウでは、これによりステートのオーバーヘッドが削減され、パフォーマンスが向上します。通常結合と比較して、ウィンドウ結合では下流オペレーターに更新メッセージを出力しないことも可能であり、時間ウィンドウが小さい結合が必要なユースケースで有用です。
注意事項
使用可能な値
例
-
SQL ストリーミングジョブを作成し、次の SQL コードをコピーして、SET ステートメントを使用して
table.optimizer.window-join-enabledパラメーターをtrueに設定してから、SQL を実行して実行計画を確認します。SET 'table.optimizer.window-join-enabled' = 'true'; CREATE TEMPORARY TABLE LeftTable ( id VARCHAR, row_time TIMESTAMP_LTZ(3), num INT, WATERMARK FOR row_time as row_time - INTERVAL '5' SECONDS ) WITH ( 'connector'='datagen' ); CREATE TEMPORARY TABLE RightTable ( id VARCHAR, row_time TIMESTAMP_LTZ(3), num INT, WATERMARK FOR row_time as row_time - INTERVAL '10' SECONDS ) WITH ( 'connector'='datagen' ); EXPLAIN SELECT L.num as L_Num, L.id as L_Id, R.num as R_Num, R.id as R_Id, COALESCE(L.window_start, R.window_start) as window_start, COALESCE(L.window_end, R.window_end) as window_end FROM ( SELECT * FROM TABLE(TUMBLE(TABLE LeftTable, DESCRIPTOR(row_time), INTERVAL '5' MINUTES)) ) L JOIN ( SELECT * FROM TABLE(TUMBLE(TABLE RightTable, DESCRIPTOR(row_time), INTERVAL '5' MINUTES)) ) R ON L.num = R.num AND L.window_start = R.window_start AND L.window_end = R.window_end;出力の「Optimized Execution Plan」セクションで、実行計画に WindowJoin オペレーターが含まれていることを確認できます。
== Optimized Execution Plan == Calc(select=[num AS L_Num, id AS L_Id, num0 AS R_Num, id0 AS R_Id, CASE(window_start IS NOT NULL, window_start, window_start0) AS window_start, CASE(window_end IS NOT NULL, window_end, window_end0) AS window_end]) +- WindowJoin(leftWindow=[TUMBLE(win_start=[window_start], win_end=[window_end], size=[5 min])], rightWindow=[TUMBLE(win_start=[window_start], win_end=[window_end], size=[5 min])], joinType=[InnerJoin], where=[(num = num0)], select=[id, num, window_start, window_end, id0, num0, window_start0, window_end0]) :- Exchange(distribution=[hash[num]]) : +- Calc(select=[id, num, window_start, window_end]) : +- WindowTableFunction(window=[TUMBLE(time_col=[row_time], size=[5 min])]) : +- WatermarkAssigner(rowtime=[row_time], watermark=[(row_time - 5000:INTERVAL SECOND)]) : +- TableSourceScan(table=[[vvp, default, LeftTable]], fields=[id, row_time, num]) +- Exchange(distribution=[hash[num]]) +- Calc(select=[id, num, window_start, window_end]) +- WindowTableFunction(window=[TUMBLE(time_col=[row_time], size=[5 min])]) +- WatermarkAssigner(rowtime=[row_time], watermark=[(row_time - 10000:INTERVAL SECOND)]) +- TableSourceScan(table=[[vvp, default, RightTable]], fields=[id, row_time, num]) -
SQL コード内の SET ステートメントを修正して
table.optimizer.window-join-enabledパラメーターをfalseに設定するか、SET ステートメントを削除してから、SQL を実行して変更後の実行計画を確認します。-- 'false' に設定するか、この設定句を削除します SET 'table.optimizer.window-join-enabled' = 'false'; CREATE TEMPORARY TABLE LeftTable ( id VARCHAR, row_time TIMESTAMP_LTZ(3), num INT, WATERMARK FOR row_time as row_time - INTERVAL '5' SECONDS ) WITH ( 'connector'='datagen' ); CREATE TEMPORARY TABLE RightTable ( id VARCHAR, row_time TIMESTAMP_LTZ(3), num INT, WATERMARK FOR row_time as row_time - INTERVAL '10' SECONDS ) WITH ( 'connector'='datagen' ); EXPLAIN SELECT L.num as L_Num, L.id as L_Id, R.num as R_Num, R.id as R_Id, COALESCE(L.window_start, R.window_start) as window_start, COALESCE(L.window_end, R.window_end) as window_end FROM ( SELECT * FROM TABLE(TUMBLE(TABLE LeftTable, DESCRIPTOR(row_time), INTERVAL '5' MINUTES)) ) L JOIN ( SELECT * FROM TABLE(TUMBLE(TABLE RightTable, DESCRIPTOR(row_time), INTERVAL '5' MINUTES)) ) R ON L.num = R.num AND L.window_start = R.window_start AND L.window_end = R.window_end;出力の「Optimized Execution Plan」セクションには WindowJoin オペレーターが含まれなくなります。操作は通常結合になります。
== Optimized Execution Plan == Calc(select=[num AS L_Num, id AS L_Id, num0 AS R_Num, id0 AS R_Id, CASE(window_start IS NOT NULL, window_start, window_start0) AS window_start, CASE(window_end IS NOT NULL, window_end, window_end0) AS window_end]) +- Join(joinType=[InnerJoin], where=[((num = num0) AND (window_start = window_start0) AND (window_end = window_end0))], select=[id, num, window_start, window_end, id0, num0, window_start0, window_end0], leftInputSpec=[NoUniqueKey], rightInputSpec=[NoUniqueKey]) :- Exchange(distribution=[hash[num, window_start, window_end]]) : +- Calc(select=[id, num, window_start, window_end]) : +- WindowTableFunction(window=[TUMBLE(time_col=[row_time], size=[5 min])]) : +- WatermarkAssigner(rowtime=[row_time], watermark=[(row_time - 5000:INTERVAL SECOND)]) : +- TableSourceScan(table=[[vvp, default, LeftTable]], fields=[id, row_time, num]) +- Exchange(distribution=[hash[num, window_start, window_end]]) +- Calc(select=[id, num, window_start, window_end]) +- WindowTableFunction(window=[TUMBLE(time_col=[row_time], size=[5 min])]) +- WatermarkAssigner(rowtime=[row_time], watermark=[(row_time - 10000:INTERVAL SECOND)]) +- TableSourceScan(table=[[vvp, default, RightTable]], fields=[id, row_time, num])