営業本部長が経営会議で「在庫をリアルタイムで見たい」と言いました。データ基盤の担当は2人です。1週間かけてKafkaとFlinkの構成案を描き、マネージドサービスの見積もりも添えて持っていくと、DX推進の担当者から「この金額で、何がどう良くなるのか説明してほしい」と返されます。本部長に聞き直すと、欲しかったのは毎朝9時の発注会議で前日の夕方までの在庫が見えることでした。いまの集計は翌日の昼です。要るのは「リアルタイム」より、朝に間に合うことでした。

このずれは、「リアルタイム」という言葉が人によって1秒にも15分にも翌朝にもなることから起きます。先に「何分遅れたら、誰がどう困るか」を数字に置き換えます。そうすれば、選ぶ構成はほぼ決まります。

早見表:遅れてよい時間と、運用できる人数で決める

状況選ぶ構成これを選ぶと後で困ること
翌朝までに前日分が見えればよい日次バッチ(dbt を夜間に実行)日中の急な変化は翌日まで見えない
15分〜1時間遅れで足りるバッチの頻度を上げる(dbt の incremental を15〜30分ごと、Snowflake の Dynamic Tables など)実行回数に比例してDWHの計算費が増える
1分前後で、SQLで書ける集計を見たいMaterialize(業務DBの変更を直接取り込める)メモリに載る量が構成の上限になる。商用中心で、移行先は少ない
数秒以内に、複数のシステムが同じ出来事を受け取るKafka(またはマネージドのKafka)を通り道に置く保持期間・パーティション数・スキーマの決めごとを誰かが持ち続ける
数秒以内に、時間窓・複数イベントの突き合わせ・不正判定をするKafka+Flink状態の設計とジョブ更新の手順に専門性が要る。担当が1人だと休めない

表は上から順に検討します。上の段で足りるなら、下には進みません。下に行くほど遅れは短くなる一方で、夜間や休日に誰かが見張る仕組みに変わっていくからです。

「リアルタイムで」を、3つの質問で数字に置き換える

依頼者に技術の話をしても、要件は固まりません。聞くのは業務の場面です。

「その数字を見て、誰が、どんな判断をしますか」「その判断が10分遅れると、何が起きますか」「1時間遅れなら、どうですか」。

答えはたいてい、次のどれかに収まります。

返ってきた答え遅れの許容向く構成
「朝の会議で使う」「週次の報告に載せる」数時間〜翌朝日次バッチ
「午前と午後で発注を見直す」「店舗に昼までに連絡する」15分〜1時間高頻度バッチ
「画面を開いたときに直近の数字が出てほしい」1分前後高頻度バッチか Materialize
「不正な決済を、承認する前に止めたい」「設備の異常で、ラインを止める判断をする」数秒以内Kafka+Flink など

冒頭の在庫の例は1行目でした。夜間バッチの開始を早め、発注会議の前に終わるよう順番を並べ替える。それだけで片づきました。秒単位が要るのは4行目のように、遅れがそのまま損失になる場面です。そこまでの要件が出てきたら、次の節に進みます。

15分で足りるなら、まずバッチの頻度を上げる

新しい基盤は、まだ入れません。先に、いまある仕組みで遅れを縮めます。dbt の incremental モデル(前回から増えた行だけを処理する作り方)を15〜30分ごとに動かせば、15分〜1時間の要件には届きます。dbtの層の分け方はdbtの3層構造、テストの組み方はdbt testsの実践に書いています。

Snowflake を使っているなら、Dynamic Tables という選択肢もあります。「このテーブルは最大で何分遅れてよいか」を目標遅延(TARGET_LAG)として宣言すると、Snowflake が更新の頻度を決めてくれます。公式ドキュメントでは、目標遅延に指定できる最短は60秒です(2026年10月17日参照)。

気をつけるのは費用です。1日1回から15分ごとに変えると、実行回数は96倍になります。差分だけを処理する作りにしておかないと、請求額が先に問題になります。

秒単位が要るときの3つの道具は、担う役割が違う

Kafka・Flink・Materialize は、よく同じ棚に並べられます。しかし受け持つ仕事は別物です。

道具受け持つ仕事向く場面選ばない方がよい条件
Apache Kafka起きた出来事を記録し、複数の読み手に配る通り道注文や更新の出来事を、DWH・検索・通知など複数の宛先が使う宛先がDWH 1つだけで、1日1回の取り込みで足りる
Apache Flink流れてくる出来事に、時間窓の集計や突き合わせをかける処理エンジン不正判定、異常検知、複数のイベントをまたぐ判定SQLの集計で済む。担当が1人しかいない
MaterializeSQLで定義したビューを、元データの変更に合わせて更新し続けるデータベースSQLが書けるチームが、直近の数字をBIや画面に出したい扱うデータがメモリに載らない量になる。OSSで自社運用したい

Kafka:多くの宛先に同じ出来事を配る

Kafka は書いた出来事を、読まれた後も残します。ブローカー設定 log.retention.hours の既定値は168時間(7日)で、読み手が止まっても、復旧後に続きから読み直せます。配信の保証は既定で「少なくとも1回」です。つまり、同じ出来事が2回届くことがあります。受け取る側は、2回処理しても結果が変わらない作りにしておきます。

後から直しにくい決めごとは2つです。1つはパーティション(トピックを並列に読むための分割単位)の数で、公式ドキュメントには減らす操作はできないと書かれています。増やすと、同じキーの出来事が別のパーティションに振り分けられることがあり、順序の前提が崩れます。もう1つは運用方式です。Kafka 4.0 から ZooKeeper は取り除かれ、KRaft という方式でしか動きません。古い構成のまま運用している場合は、移行が先に必要です。

Flink:状態を持つ処理を、障害の後も続ける

「直近5分に同じカードで3回以上決済された」。こうした判定には、過去の出来事を覚えておく仕組みが要ります。Flink はこの記憶(状態)を持ったまま処理を続け、定期的にチェックポイントとして保存します。障害が起きても、最後のチェックポイントから再開できます。

運用で効いてくるのは、チェックポイントとセーブポイントの区別です。前者は障害復旧のために Flink が自動で作って自動で消すもの、後者は人が作って人が消すバックアップです。ジョブのコードを変える前や Flink のバージョンを上げる前には、後者を取っておきます。手順を決めないまま本番に入ると、最初の更新で状態を失います。

SQL でも書けます。1分ごとに店舗別の売上を集計する例です。

CREATE TABLE orders (
    order_id BIGINT,
    store_id INT,
    amount DECIMAL(10, 2),
    ordered_at TIMESTAMP(3),
    WATERMARK FOR ordered_at AS ordered_at - INTERVAL '10' SECOND
) WITH (
    'connector' = 'kafka',
    'topic' = 'orders',
    'properties.bootstrap.servers' = 'broker:9092',
    'format' = 'json'
);

SELECT window_start, window_end, store_id, SUM(amount) AS revenue
FROM TUMBLE(TABLE orders, DESCRIPTOR(ordered_at), INTERVAL '1' MINUTES)
GROUP BY window_start, window_end, store_id;

WATERMARK の行は「10秒までの遅れは待つ」という意味です。古い記事にある GROUP BY TUMBLE(...) の書き方は、公式ドキュメントで非推奨とされています。

Materialize:SQLのビューを常に最新に保つ

Materialize では、CREATE MATERIALIZED VIEW で定義した集計が、元データが変わるたびに差分で更新されます。PostgreSQL と通信の方式が互換なので、PostgreSQL に接続できるBIツールやクライアントから普通のテーブルのように読めます。取り込み元には Kafka のほか、PostgreSQL・MySQL・SQL Server の変更データ、Webhook などが公式ドキュメントに並んでいます。業務DBから直接取り込めるので、Kafka を置かずに始められます。小さなチームには助かる点です。

自社のサーバーで動かす版もあります。有償の Enterprise Edition と、無償の Community Edition です。無償版にはメモリ24GiB・ディスク48GiBまでという上限があります(2026年10月17日参照)。使いどころと注意点はMaterializeの解説にまとめています。

Spark を使っているチームは、Structured Streaming も候補になる

Spark でバッチを書いているチームなら、新しいエンジンを覚えずに済む Structured Streaming が候補に入ります。既定は小さなバッチを連続で回す方式で、公式ドキュメントでは遅れは最短100ミリ秒程度です。Flink との分かれ目は遅れの長さより、状態をどこまで細かく扱いたいかにあります。

自前で運用するか、マネージドにするかは人件費で比べる

ストリーミングの基盤は、止まると困る場面で使います。だから夜間や休日の障害にも誰かが備えます。自前で運用すると、費用の大半はサーバー代より人の時間です。

年収800万円のエンジニアが、Kafka と Flink の面倒を稼働の半分で見る場合を試算します。会社が負担する総コストは給与の約3倍なので、年2,400万円です。年1,900時間で割ると、時給はおよそ12,600円になります。

項目計算金額
1人の総コスト800万円×3年2,400万円
運用に割く分稼働の半分年1,200万円
障害対応の待機を2人で回す同じ条件の2人が稼働の半分ずつ年2,400万円

マネージドサービス(Confluent Cloud、Amazon MSK、Amazon Managed Service for Apache Flink、Confluent Cloud for Apache Flink など)の見積もりは、この人件費と並べて比べます。数字は説明のための例なので、自社の年収に置き換えて計算してください。担当が1人だと、その人が休んだ日に止まります。人数が足りないなら、マネージドを選ぶか、1つ上の段の構成に戻すかのどちらかです。

運用で詰まるのは、どれも最初に決めなかったこと

詰まる場面原因最初に決めておくこと
読み手の処理が追いつかず、未処理が積み上がる読み手の遅れ(コンシューマーラグ)を見ていない遅れの上限と、超えたときに誰へ通知するか
項目を1つ変えたら、下流のダッシュボードが壊れた出来事の形(スキーマ)の変え方に決まりがない項目の追加は可、削除と型変更は事前に告知、という決まり
ジョブを直したら、集計がゼロからやり直しになったセーブポイントを取らずに更新した更新手順書に「セーブポイントを取る」を入れる
保存しているデータの費用が増え続ける保持期間を既定のまま全トピックに使っているトピックごとに、読み直しが必要な日数
2回届いた出来事で、売上が二重に計上された「少なくとも1回」の前提で受け手を作っていない出来事に一意のIDを持たせ、受け手で重複を除く

業務DBの変更を流す部分(変更データキャプチャ、CDC)の作り方はCDCの実装とPostgreSQLの論理レプリケーション、取り込みツールの選び方はELとETLの選び方に書いています。処理した結果をレイクに置くときのテーブル形式はテーブルフォーマットの比較を参照してください。

承認する側が確かめる4つのこと

DX推進や経営企画の立場で構成案を受け取ったら、技術の中身は後回しで構いません。次の4点を聞きます。

確かめること答えが曖昧なときに起きること
この数字が何分遅れると、誰がどう困るのか使われない速さに費用を払う
1つ上の段の構成(バッチの頻度を上げる等)で足りない理由作る必要のない基盤を抱える
夜間・休日に止まったとき、誰が何時間以内に対応するのか担当者1人に負荷が集中し、辞めたときに止まる
マネージドと自前で、人件費込みの年額はいくらかサーバー代だけで比べて、安い方を選び違える

1行目に具体的な答えが返ってくれば、残りの3つは技術側が詰められます。返ってこないなら、構成の議論はいったん止めます。依頼者との打ち合わせを1回入れるほうが早く進みます。

よくある質問(FAQ)

dbt を5分ごとに動かせば、ストリーミングは要りませんか?

足ります。5分遅れで判断が間に合う業務なら、それで十分です。ただしDWHの計算費は実行回数に比例して増え、1日1回から5分ごとにすると実行回数は288倍です。差分だけを処理する incremental モデルにしたうえで、15〜30分ごとで足りないかを先に試します。秒単位の判断が要る場合は、Kafka と Flink や Materialize の構成を検討します。

中小規模でも Kafka は必要ですか?

宛先がDWH 1つだけなら、要らないことがほとんどです。業務DBからDWHへの取り込みは取り込みツールで足り、Materialize なら業務DBの変更を直接取り込めます。Kafka が効くのは、同じ出来事を検索・通知・DWHなど複数のシステムがそれぞれ使う場面です。保持期間やパーティション数を決めて持ち続ける担当が要る点も含めて判断します。

Flink と Spark Structured Streaming はどう選べばよいですか?

すでに Spark でバッチを運用しているチームなら、Structured Streaming から始めると覚えることが少なく済みます。既定はマイクロバッチで、遅れは最短100ミリ秒程度です。時間窓や複数イベントの突き合わせのように状態を細かく扱う処理が中心なら、Flink が向きます。どちらを選んでも、ジョブ更新時の状態の引き継ぎ手順は最初に決めておきます。

Materialize と Snowflake の Dynamic Tables は何が違いますか?

Dynamic Tables は Snowflake の中のデータを、宣言した目標遅延に合わせて更新する仕組みで、目標遅延の最短は60秒です。Materialize は別のデータベースで、Kafka や業務DBの変更を受け取ってビューを差分で更新し続けます。データがすでに Snowflake にあり、1分程度の遅れで足りるなら Dynamic Tables、Snowflake の外の出来事を直接取り込んで直近の数字を出したいなら Materialize が候補です。

ストリーミング基盤を、将来に備えて先に作っておくべきですか?

勧めません。使う業務が決まらないうちから、保持期間・スキーマ・障害対応の面倒を見る費用がかかり続けます。まずバッチの頻度を上げて要件に応え、遅れが損失に直結する業務が具体的に出てきた時点で、その業務の分だけを最小の構成で作ります。

出典

「リアルタイムで」と言われてKafkaの構成案を描く前に、その数字が何分遅れると誰が困るのかを依頼者と一緒に詰めるところから、壁打ちにどうぞ。初回相談(30分・無料)で一緒に整理できます。