Flink SQLとは?動的テーブルと継続クエリの仕組み・状態肥大の回避と採用判断を解説
Kafkaやデータベースの変更ログを、Javaのジョブを書かずにSELECT文だけで結合・集計し、別のテーブルへ書き戻す。それを担うのがApache FlinkのSQLレイヤ、Flink SQLです。この記事では、ストリームを表として扱う動的テーブルの考え方、終わらない継続クエリがappend・retract・upsertのどれで結果を出すか、状態が無制限に伸びるクエリの条件とその打ち切り方、そしてFlink 2.3系で加わったマテリアライズドテーブルとchangelog変換までを、実装の手触りが残る粒度で整理しました。DataStream APIへ戻すべき処理の線引きと、採用を見送るべき条件も具体的に示します。
まとめ|Flink SQLを採用する条件と、DataStream APIへ戻す分かれ目
Flink SQLが効くのは、複数のデータソースを横断して結合・集計する処理を、Javaのビルドパイプラインを持たないデータ担当者にも書かせたい場面です。KafkaとPostgreSQLの変更ログとオブジェクトストレージ上の履歴を、同じSELECT文の語彙で突き合わせられます。バッチとストリームが同一ランタイムで動くため、日次集計と即時集計をひとつの基盤に寄せられる点も効きます。
逆に、DataStream APIへ戻すべき処理は3つあります。ひとつ目はイベントごとに複雑な分岐状態を持つ処理で、SQLの述語では表現しきれません。ふたつ目は状態の保持期間やアクセスパターンを自分で制御したい処理。みっつ目は外部システムとの非同期入出力をタイミングまで含めて作り込む処理です。この3つに当てはまるロジックをSQLで無理に表現すると、実行計画の書き換え結果が読めなくなり、性能の当たり外れを制御できなくなります。
採用判断で先に確かめるのは、クエリの状態が有限に収まるかどうかです。非ウィンドウのGROUP BYや高カーディナリティ列での通常結合は、公式ドキュメントが明示するとおり保持データが伸び続けます。ウィンドウ化かstate TTLで打ち切れないクエリは、そのまま本番へ出さない。この一点を設計レビューの必須項目に置くだけで、稼働後の障害はかなり減らせます。
Flink SQLの位置づけと動的テーブル・継続クエリという実行モデル
Flink SQLは独立した製品ではなく、Apache Flinkが提供するAPI階層の最上位です。同じクラスタ、同じチェックポイント機構、同じイベント時刻処理の上に乗ります。Apache Flinkのエンジン構成とユースケースを押さえておくと、SQLで書いた1文がどこで並列化され、どこで状態を持つかを追いやすくなります。
DataStream APIの上に重なるTable APIとSQLの階層構造
FlinkのAPIは階層化されています。最下層にProcessFunctionがあり、その上にDataStream API、さらに上にTable APIとSQLが乗る構造です。SQL文はパーサとプランナを通り、最終的には下位のオペレータ列へ落とし込まれてから実行されます。
この構造から導ける実務上の含意はひとつ。SQLはDataStream APIの表現力を超えません。逆に、SQLで書けた処理は下位APIで書いた場合と同じランタイム特性を引き継ぎます。チェックポイントの挙動もバックプレッシャーの伝わり方も共通です。Table APIとSQLは記述形式が違うだけで、生成されるプランは同じ経路をたどります。
ストリームを表として扱う動的テーブルと2方向の変換が成り立つ条件
動的テーブルは、新しいデータの到着に合わせて中身が変わり続けるテーブルです。公式ドキュメントは、ストリームのレコードをINSERT操作として解釈し、変更ログの流れからテーブルを構築すると説明しています。静的なテーブルとの違いは、確定した1枚のスナップショットではなく、時間とともに更新される状態を指し示す点にあります。
変換は双方向です。ストリームから動的テーブルを作り、動的テーブルに対する継続クエリの結果を再びストリームへ戻せます。ここで公式ドキュメントが置く前提が効いてきます。継続クエリの結果は、入力テーブルのスナップショットに対して同じクエリをバッチ実行した結果と意味的に等価である、という宣言です。SQLの意味論がバッチと揃っているからこそ、同じ集計定義を日次バッチと即時処理で共有できます。
append・retract・upsertで結果を出し分ける継続クエリの3モード
継続クエリは終了しません。入力が変わるたびに結果を更新し続けます。ここでクエリは2種類に割れます。窓付き集計のように過去の行を書き換えない追記専用のクエリと、窓のないGROUP BYのように既出の行を再計算して出し直す更新クエリです。
結果を外へ流すときの符号化は3通りに分かれます。追記のみのモードはINSERTをそのまま新しい行として出す方式です。retractモードは更新を「古い行の取り消し」と「新しい行の追加」の2メッセージで表現します。upsertモードは一意キーの存在を前提に、更新を1メッセージにまとめる方式です。公式ドキュメントはupsertがretractより効率的だと明記しています。どのモードになるかはクエリの形とシンク側の宣言で決まるため、シンクのDDLに主キーを書いたかどうかで書き込み量が倍近く変わる場面もあります。
CREATE TABLEとカタログでソース・シンクを宣言する記述の型
Flink SQLのジョブは、テーブルの宣言と、そのテーブルに対する問い合わせの2層で構成されます。テーブル宣言はDDLで書き、カタログに登録する仕組みです。ここでの記述精度が、後段のクエリで選べる結合の種類と状態の量をほぼ決めてしまいます。
コネクタ指定とスキーマ定義でソースとシンクを結ぶDDLの書き方
ソースもシンクもCREATE TABLEで宣言します。列名と型を並べ、WITH句にコネクタ種別と接続情報、データ形式を書く。この形はKafkaでもJDBCでもファイルシステムでも共通で、切り替えはWITH句の書き換えで済みます。
ここで手を抜きやすいのがスキーマの型定義です。上流のスキーマが変わったとき、DDL側の列定義が追随していないと、ジョブは起動するのに値がnullで埋まるという読みにくい壊れ方をします。Avroやプロトコルバッファを使うなら、テーブル定義を人手で写経するのではなく、レジストリ側の定義を正として運用するのが安全です。
WATERMARK句の記述位置がイベント時刻処理の精度を決める理由
イベント時刻で処理するかどうかは、クエリではなくテーブル定義で決まります。CREATE TABLEの中に時刻列を置き、WATERMARK句でその列からどれだけ遅れを許容するかを宣言する。この1行がないテーブルに対しては、窓付き集計もインターバル結合も書けません。
遅延の許容幅は、処理の待ち時間と取りこぼしのトレードオフです。幅を広げれば遅れて届いたイベントを拾えますが、窓の確定が遅れて結果の出力までの時間が伸びます。実測せずに秒単位で決めると、上流のリトライやパーティション偏りで想定外の取りこぼしが出ます。上流の到着遅延を分位点で測ってから幅を決める。この順序を崩さないでください。
ウィンドウTVFとJOIN種別を処理要件から選び分ける判断基準
集計の窓はTUMBLEやHOPなどのテーブル値関数で指定します。結合は要件によって性質の異なる複数の種類から選ぶ構成です。ここを取り違えると、動きはするが状態が際限なく膨らむジョブができあがります。
| 結合の種類 | 状態の持ち方 | 向く場面 |
|---|---|---|
| 通常結合 | 両側を保持し続ける | 小さい参照表との突合 |
| インターバル結合 | 時間範囲ぶんだけ保持 | 受注と決済の突合 |
| テンポラル結合 | 版付き表の履歴を保持 | 時点の単価や為替の適用 |
| ルックアップ結合 | 外部DBを都度参照 | 更新頻度の低いマスタ |
| ウィンドウ結合 | 同一の窓の範囲で保持 | 窓単位のイベント突合 |
迷ったらまずインターバル結合を検討してください。突合の対象が「注文から30分以内の決済」のように時間で区切れるなら、状態の上限が時間幅で決まります。マスタ参照はルックアップ結合に寄せると、Flink側に状態を持たずに済みます。窓そのものの選び方と確定タイミングの制御はストリームウィンドウ処理の解説を参照してください。
状態肥大とバックプレッシャーを招くクエリ条件と回避のための設計指針
Flink SQLの本番障害は、文法エラーではなく状態の設計から起きます。公式ドキュメントは継続クエリの制約として、状態サイズの増大と更新計算のコストという2つを名指しで挙げています。この章で扱うのは、その2つを実装の判断に落とす方法です。
非ウィンドウGROUP BYと高カーディナリティ結合で状態が伸び続ける条件
更新を伴うクエリは、出力済みの行を後から書き換えるために、それらを保持し続けなければなりません。公式ドキュメントは、ユーザー名のように値の種類が多い列でグルーピングした場合、状態が制御不能に膨らみシステム障害に至りうると警告しています。
危険な形はほぼ決まっています。窓のないGROUP BYで高カーディナリティ列を使う。両側とも無限ストリームのテーブルを時間条件なしで通常結合する。DISTINCTを無限ストリーム全体に掛ける。この3つが揃うクエリは、検証環境では動いて本番の数日後に落ちます。テストデータの期間が短いと再現しないため、レビューで形から弾くのが確実です。
state TTLとウィンドウ化のどちらで打ち切るかを決める判断軸
打ち切り方は2つあり、選択基準は明確です。集計の対象期間が業務要件として決まっているなら、ウィンドウ化を選びます。1時間ごとの売上、5分ごとの異常件数といった定義は、窓の定義そのものです。窓が閉じれば状態は解放されるので、上限が要件から決まります。
一方、集計期間を切れないが古い値は捨ててよい場合にstate TTLを使います。「同一ユーザーの直近アクセスとの照合」のように、期間の定義が業務側にない処理が該当します。TTLの値は、遅延到着の許容幅より確実に長く取ってください。TTLが短すぎると、遅れて届いたイベントの相手方が消えていて、静かに突合漏れが発生します。この漏れは例外を出さないので、件数の日次照合を組まないと気づけません。
ランキングとupsertシンクで更新伝播が詰まる典型的な失敗パターン
もうひとつの制約が更新計算のコストです。公式ドキュメントはランキングを例に挙げ、1件の新しい入力が既存の順位を無効にし、結果セットの広い範囲へ更新が連鎖すると説明しています。上位100件のランキングでも、下位の入れ替わりが激しければ出力メッセージは入力を大きく上回ります。
この連鎖はシンク側で詰まりとして表面化します。書き込み先のスループットが更新流量に追いつかず、バックプレッシャーが上流へ伝わり、最終的にはチェックポイントの所要時間が延びてタイムアウトする。症状はチェックポイント失敗として出るので、原因がクエリの形にあると気づきにくい流れです。ランキングを出すなら順位の粒度を粗くするか、出力を一定間隔でまとめる設計を先に入れておきます。基盤全体の処理配分や監視の組み方まで含めて設計を詰めたい場合は、データ分析基盤構築・MLOps構築支援で実装から運用設計まで引き受けています。
Flink 2.3系で加わったマテリアライズドテーブルとchangelog変換
公式のダウンロードページによると、2026年8月時点の最新安定版は2.3.0で、リリース日は2026年6月25日と記載されています。2.2.1が2026年5月15日、2.1.3が2026年6月14日と併記されており、2.1系と2.2系も保守が続いている状態です。2.3系ではSQL側の更新が目立ちます。
FROM_CHANGELOGとTO_CHANGELOGが埋めた変換の往復経路
2.3.0のリリース告知は、FROM_CHANGELOGとTO_CHANGELOGという2つのProcess Table Functionの追加を挙げています。前者は操作種別を表す列を持つ追記専用ストリームを動的テーブルへ変換し、後者は動的テーブルを追記専用の形へ戻します。
実務で効くのは後者です。告知は用途として保管、監査、追記専用シンクへの書き出しを挙げています。更新を含む結果を、更新をサポートしないストレージへ落としたいという要求は現場でよく出ます。従来はDataStream APIへ降りるか手作りの変換を挟んでいた部分が、SQLの語彙で書けるようになりました。
マテリアライズドテーブルのDDL拡張とSTART_MODEによる再処理制御
マテリアライズドテーブルまわりも広がりました。CREATE MATERIALIZED TABLEで列定義を明示でき、ウォーターマークと主キーも指定できます。ALTER MATERIALIZED TABLEには追加、変更、削除の各操作が入りました。
運用の観点でいちばん効くのはSTART_MODE句です。告知はこれを、マテリアライズドテーブルのクエリが変わったときの再処理をきめ細かく制御するための仕組みだと説明しています。定義変更のたびに全期間を作り直すか、変更点以降だけを流し直すかを宣言で選べるという意味で、長期運用中のテーブル定義に手を入れる際の停止時間に直結します。
ON CONFLICT句でupsertキー不一致の扱いを明示する運用設計
upsert書き込みの内部処理にも変更が入りました。告知はON CONFLICT句の追加を挙げ、選べる方針として何もしない、エラーにする、重複を排除するの3つを示しています。upsertキーと主キーが食い違ったときの挙動を、暗黙の処理ではなく宣言で明示させる変更です。
この明示化は移行時に効きます。旧来の暗黙処理に依存していたジョブは、宣言を書き足すまで挙動が変わる可能性があるため、2.3系へ上げるときは書き込み結果の件数比較を先に取っておいてください。あわせて、告知はProcess Table Functionに遅延データの取り扱いと表引数へのORDER BY指定が入ったこと、UDF登録のUSING句にARTIFACTキーワードが追加されたことにも触れています。
DataStream APIと他のストリーミングSQL基盤に対する採用判断
ここからは判断の話です。Flink SQLは万能の入口ではなく、書ける範囲と運用コストがはっきりした道具として扱うのが実務的です。
SQLで書く処理とDataStream APIへ落とす処理を分ける境界線
境界線の引き方はひとつで足ります。「状態の持ち方を自分で決める必要があるか」で分けてください。決める必要がなければSQLで書き、必要ならDataStream APIへ落とす。窓付き集計、結合、フィルタ、射影はSQL側。イベントの順序に応じて内部状態を遷移させる処理、タイマーで能動的に発火する処理、外部APIへ非同期に問い合わせて結果を待ち合わせる処理は下位API側です。
ひとつのジョブでSQLと下位APIを混ぜることもできますが、混在は運用の見通しを悪くします。ジョブ単位でどちらかに寄せ、境界はKafkaのトピックで切る。この分け方なら、SQL側の担当とアプリ側の担当を分離できます。ストリーム処理の仕組みとバッチ処理との使い分けを先に共有しておくと、この分担の議論が速く進みます。
Kafka単独ならksqlDB、複数ソース横断ならFlink SQLという線引き
SQLでストリーム処理を書ける基盤はFlinkだけではありません。線引きの基準はデータソースの広がりです。入口も出口もKafkaで完結し、処理が射影・フィルタ・結合・時間窓集計に収まるなら、ksqlDBのストリームとテーブルの仕組みと採用判断のほうが構成部品は少なく済みます。
ただし、Kafka以外のシステムを一級のテーブルとして扱いたくなった時点で、Flink SQLへ寄せる判断が妥当になります。JDBCソース、オブジェクトストレージ上のテーブル形式、外部APIのルックアップ参照を同じSQLの語彙で結合できるのは、コネクタの幅を持つFlink側の強みです。既存のKafka資産に薄く載せるならksqlDB、これから数年運用する基盤の中核に据えるならFlink SQL。この順序で見ると判断がぶれません。
Flink SQLを見送るべきデータ規模と運用体制の具体的な条件
見送るべき条件を言い切ります。日次のイベント量が数百万件に届かず、遅延要件が「翌朝までに反映されていればよい」なら、Flink SQLは過剰です。データウェアハウスへの15分間隔のバッチ取り込みで要件は満たせて、クラスタ運用の負担だけが残ります。
体制面の条件も明確です。JobManagerとTaskManagerの障害対応、チェックポイントとセーブポイントの管理、状態を持つジョブのバージョン更新を担当できる人が1人もいない組織では、稼働後3か月で誰も触れないジョブができあがります。マネージドサービスを使えばクラスタ運用は軽くなりますが、状態設計とジョブ更新の判断は外注先か社内のどちらかが必ず持つ必要があります。ここが空欄のまま導入を決めない。これが最後の分岐点です。
よくある質問
Flink SQLの検討時に出やすい質問を、実装と運用の観点でまとめました。
Flink SQLとTable APIはどう違いますか?
実行される中身は同じで、記述形式だけが違います。Table APIはJavaやPythonのメソッドチェーンでリレーショナル演算を組み立てる書き方、Flink SQLは文字列としてSQL文を渡す書き方です。どちらも同じプランナを通り、同じオペレータ列へ落とし込まれます。SQL文字列は外部ファイルやカタログで管理しやすく、Table APIはアプリケーションのコードから型を保ったまま組み立てやすいという運用差で選ぶのが実際的です。
Flink SQLはバッチ処理にも使えますか?
利用可能です。Flinkはバッチを有界ストリームとして扱う設計なので、同じSQL文を実行モードの指定だけで切り替えられます。公式ドキュメントの定義でも、継続クエリの結果は入力のスナップショットに対するバッチ実行と意味的に等価です。日次集計の定義と即時集計の定義を1本のSQLで共有し、実行モードだけ変えるという運用が取れます。ただしコネクタ側の対応やソート処理の挙動には差があるため、両モードでの結果一致は必ず実データで確認してください。
state TTLはどのくらいの値に設定すべきですか?
業務上の突合が成立する時間幅に、上流の遅延到着の許容幅を足した値を下限にします。注文と決済の突合で決済が最大30分遅れる運用なら、TTLを30分ちょうどに置くのは危険です。上流のリトライやパーティション偏りで到着が遅れた分だけ突合漏れが出ます。到着遅延の分位点を実測し、99パーセンタイルに余裕を足した値から始めて、状態サイズのメトリクスを見ながら詰めていく手順を推奨します。
Flink SQLのジョブはどうやって本番へデプロイしますか?
SQL文をファイルとしてバージョン管理し、ジョブ投入を自動化する形が基本です。クラスタの起動形態は、ジョブ専用のクラスタを立てるアプリケーションモードと、共有クラスタへ複数ジョブを投げるセッションモードがあります。状態を持つジョブの更新はセーブポイントの取得と復元を伴うため、SQL文の変更が状態の互換性を壊さないかを事前に確認する工程を、デプロイ手順へ組み込んでおいてください。
Flinkの版はどれを選べばよいですか?
公式ダウンロードページでは2026年8月時点の最新安定版として2.3.0(2026年6月25日リリース)が示され、2.2.1と2.1.3も併記されています。新規構築なら2.3系を起点にし、マネージドサービスを使う場合は提供側が対応する版に合わせるのが実務的です。既存ジョブの引き上げでは、2.3系で入ったupsert書き込みの明示化のように挙動が変わりうる変更があるため、書き込み結果の件数比較を取ってから切り替えてください。
関連記事
- Materialize(ストリーミングDB)とは?仕組み・料金と採用判断を実装視点で解説:SQLで増分計算を回す別系統の基盤として、Flink SQLと比較検討する際に読む記事です。
- RisingWaveとは?仕組み・料金とMaterializeとの使い分けを実装視点で解説:ストリーミングDB側の選択肢を整理したい場合の比較材料になります。
- スキーマレジストリとは?互換性モードとスキーマ進化の実装・採用判断を解説:Flink SQLのテーブル定義と上流スキーマを食い違わせないための運用側の前提です。