---
title: "ストリームウィンドウ処理とは？イベントタイムの扱いと4方式の使い分け・遅延データの締め切り設計を解説"
url: "https://www.issoh.co.jp/tech/details/16430/"
published: 2026-08-11
updated: 2026-10-01
categories: ["データ分析・BI"]
publisher: "株式会社一創"
---

# ストリームウィンドウ処理とは？イベントタイムの扱いと4方式の使い分け・遅延データの締め切り設計を解説

「1分ごとの売上」「直近5分の異常値」。終わりのないイベント列に対してこの手の集計が成り立つのは、流れを区切るウィンドウという仕掛けがあるからです。そして窓の境界を「いつ起きたか」で判定するのか「いつ処理したか」で判定するのかを決めるのが、イベントタイム（イベント時間）という考え方になります。本記事では、ストリームウィンドウ処理の定義、イベントタイムの意味と発生時刻の取り出し方、タンブリング・スライディング・セッション・グローバルという4つの区切り方、遅れて届いたデータをどこで締め切るかという許容遅延の設計、ステートを膨らませない差分集計の書き方、SQLのウィンドウ関数とAPI実装の境界までを実装者の目線で整理しました。窓長やスライド幅を業務要件から逆算する手順と、ウィンドウ化を見送るべき条件も示します。

## まとめ｜ウィンドウ4方式の選び分けと許容遅延を先に決める設計順序

ストリームウィンドウ処理とは、始まりと終わりが定義できないイベント列を時間や件数で区切り、その区切りごとに集計・結合を確定させる仕組みを指します。バッチ集計と違い「全件そろった」瞬間が来ないため、どこまでを1回分とみなすかを設計者が決めなければなりません。窓の切り方が、そのまま集計値の意味を決めます。

区切りの基準となる時刻はイベントタイムで取るのが原則です。イベントタイムはデータが発生した時点の時刻で、レコード自身が持っています。処理した時刻で窓を切ると、再処理や障害復旧のたびに集計値が変わる。金額や件数として業務が参照する数字なら、発生時刻をレコードから取り出し、ウォーターマークで進行を管理する構成を選びます。

方式の選び分けは分析要件から一意に決まります。定点観測の集計値がほしいなら重複なく区切るタンブリング、しきい値監視のように直近区間を連続評価したいなら窓を重ねるスライディング、ユーザーの一連の操作をひとまとまりにしたいなら無操作時間で切るセッション。件数や独自条件で確定させたい要件だけがグローバルウィンドウの領分です。

設計の順序を間違えないでください。先に決めるのは窓の長さではなく、遅れて届くデータをどこで締め切るかです。Apache Flinkの許容遅延（allowed lateness）は既定値が0で、ウォーターマーク通過後に届いたイベントは何も設定しなければ捨てられます。締め切りを決めてから窓長・スライド幅・ステート量を逆算する順序が、稼働後の手戻りを一番減らします。

## ストリームウィンドウ処理の定義と無限のデータを有限区間へ区切る仕組み

ウィンドウは、無限のデータに対して集計という演算を定義するための必須の道具です。ストリーム処理そのものの全体像は[ストリーム処理の仕組みとバッチ処理との使い分け](https://www.issoh.co.jp/tech/details/15301/)で整理しているため、本記事は窓の内部と、窓の境界を決める時刻の扱いに絞ります。

### 終わらないデータ列を集計可能にするウィンドウという区切りの役割

ストリーム処理が扱うのは、アクセスログ・センサー値・データベースの変更履歴のように終端が定義できないイベント列です。ここで合計や平均を計算しようとすると、すぐ矛盾に突き当たります。合計はいつ確定するのか。区間を決めない限り、答えは永遠に出ません。

そこで時間や件数でイベント列を有限の区間へ切り出し、その区間内の集合に従来どおりの集計関数を当てる。この切り出しがウィンドウで、切り出す規則を決める部品をFlinkではウィンドウアサイナと呼びます。窓は割り当て・発火・破棄の3段階で動き、終端に許容遅延を足した時刻を過ぎるとステートごと解放されます。1件のイベントが複数の窓に所属することもあり、窓が重なる方式なら同一イベントが数十の窓に同時に数え上げられる。所属関係は排他ではありません。

### イベント時間と処理時間のどちらで窓を切るかで変わる集計結果の再現性

窓の境界を判定する時刻には2種類あります。イベント時間はデータに埋め込まれた発生時刻、処理時間はエンジンが処理した壁時計の時刻です。処理時間ウィンドウは実装が単純で遅延も小さい一方、同じ入力を再処理すると結果が変わります。障害復旧やバックフィルで過去分を流し直したとき、全イベントが「いま」到着したものとして扱われ、窓の中身が丸ごと崩れるためです。

再現性が要るなら選択肢はイベント時間しかありません。監視ダッシュボードの表示遅延を見る用途なら処理時間で足りる一方、業務側が金額や件数として参照する数字は、再計算しても同じ値になる必要があります。

## イベントタイムとは何か｜発生時刻で窓を切る仕組みと処理時間・取り込み時刻の違い

イベントタイムは、ストリーム処理で「そのイベントが実際に起きた時刻」を指す用語です。[Flink 2.3系の時間概念のドキュメント](https://nightlies.apache.org/flink/flink-docs-release-2.3/docs/concepts/time/)は、イベント時間を各イベントが発生元のデバイスで起きた時刻と定義し、その進み方はデータに依存して壁時計には依存しないと説明しています。処理時間は演算子を実行しているマシンのシステム時計です。[Apache Beamのプログラミングガイド](https://beam.apache.org/documentation/programming-guide/)も同じ2分法を採っており、基盤が違っても定義は揃っています。

3つ目の時刻として語られるのが取り込み時刻（ingestion time）で、イベントがストリーム基盤に入った瞬間の時刻です。Flink 2.3系の時間概念ページはイベント時間と処理時間の2つだけを挙げ、取り込み時刻を独立した時間特性としては扱っていません（2026年10月時点）。実務では、Kafkaのブローカーが書き込み時刻を付ける設定が事実上の取り込み時刻にあたります。発生時刻よりは安定し、処理時間よりは再現性がある。ただし発生から書き込みまでの遅れは集計から消えるため、業務上の「いつ起きたか」を答える用途には使えません。

イベントタイムで窓を切る代償は、到着順と発生順のずれを自分で扱うことです。ネットワークの遅延、端末のオフライン、上流の再送によって、発生時刻の古いイベントがあとから届く。そこで「この時刻より前のイベントはもう来ないとみなす」という印、すなわちウォーターマークで時計を進め、窓を閉じる判断をエンジンに任せます。イベントタイムとウォーターマークは常に対で設計するものです。

### イベントタイムはどこから来るか：タイムスタンプを持つ3つの層と選び方

発生時刻の候補は、パイプラインのどこで時刻が刻まれたかで3つの層に分かれます。どれを採るかで、集計値が業務の実態をどこまで反映するかが変わります。

| 時刻を刻む層    | 代表例              | 強み       | 弱み       |
| --------- | ---------------- | -------- | -------- |
| 業務データの項目  | 注文日時・計測時刻        | 業務の意味と一致 | 端末の時計ずれ  |
| メッセージのヘッダ | KafkaのCreateTime | 抽出が不要    | 送信側の実装次第 |
| 基盤の書き込み時刻 | LogAppendTime    | 時計が単一    | 発生時刻と別物  |

第一候補は業務データそのものに含まれる発生時刻で、注文日時やセンサーの計測時刻がこれにあたります。集計結果を業務側へ説明するとき、もっとも話が通じる時刻です。第二はメッセージ基盤のヘッダに載る時刻で、[Kafka 4.3系のトピック設定](https://kafka.apache.org/43/configuration/topic-configs/)では `message.timestamp.type` の既定値が CreateTime、つまりプロデューサーが付けた時刻です。値を LogAppendTime に変えるとブローカーの書き込み時刻に置き換わります。Kafka側の構成要素は[Apache Kafkaの仕組みとKRaft構成の解説](https://www.issoh.co.jp/tech/details/7563/)で扱っています。

データベースの変更を流す構成では、ソースDBでのコミット時刻が発生時刻の候補になります。変更ログからイベントを作る方式の違いは[CDCの3方式と実装設定の解説](https://www.issoh.co.jp/tech/details/15871/)を参照してください。どの層を選んでも、設計書には「窓の境界はこの項目の時刻で判定する」と項目名まで書いておく。ここが曖昧なまま実装に入ると、プロデューサー側の改修でヘッダの時刻が変わった瞬間に集計値が静かにずれます。

### Javaのコード例で示すタイムスタンプ割り当てと順序乱れ許容幅の指定

DataStream APIでは、ソースを作るときに `WatermarkStrategy` を渡し、レコードからイベントタイムを取り出す関数と、どれだけの順序乱れを許すかを同時に宣言します。[Flink 2.3系のウォーターマーク生成のドキュメント](https://nightlies.apache.org/flink/flink-docs-release-2.3/docs/dev/datastream/event-time/generating%5Fwatermarks/)に載っている形を、注文イベントに当てはめると次のようになります。

```
import java.time.Duration;
import org.apache.flink.api.common.eventtime.WatermarkStrategy;
import org.apache.flink.streaming.api.datastream.DataStream;

// 注文イベントの発生時刻（エポックミリ秒）をイベントタイムとして使う
WatermarkStrategy<Order> strategy = WatermarkStrategy
    .<Order>forBoundedOutOfOrderness(Duration.ofSeconds(20))   // 20秒までの順序乱れを許す
    .withTimestampAssigner((order, recordTs) -> order.getOrderedAtMillis())
    .withIdleness(Duration.ofMinutes(1));                        // 1分無入力のパーティションは待たない

DataStream<Order> orders = env.fromSource(kafkaSource, strategy, "orders-source");
```

押さえる点は3つです。`forBoundedOutOfOrderness` に渡す幅は、ウォーターマークが観測済みの最大イベントタイムからどれだけ遅れて進むかを決めます。幅を広げれば遅れて届いたイベントを窓に入れやすくなる一方、すべての窓の確定がその分だけ遅れる。`withTimestampAssigner` の第2引数はKafkaのヘッダ時刻で、業務項目ではなくヘッダの時刻を採るなら関数を省略しても構いません。`withIdleness` は、データが途絶えたパーティションを進行判定から外す設定で、深夜に結果が止まる症状への直接の対策になります。

この幅と、後述する許容遅延は役割が違います。順序乱れ許容幅は「窓を最初に閉じるまで待つ時間」、許容遅延は「閉じたあとに更新を受け付ける時間」です。前者を大きく取ると全件の出力が遅れ、後者を大きく取るとステートの保持が延びる。前者は到着遅延の中央値付近、後者は高い分位点に合わせる配分が扱いやすい出発点です。

### Flink SQLでイベントタイム列を宣言するWATERMARK句とDDLの書き方

SQLで書く場合は、テーブル定義でイベントタイムを宣言します。[Flink 2.3系の時間属性のドキュメント](https://nightlies.apache.org/flink/flink-docs-release-2.3/docs/concepts/sql-table-concepts/time%5Fattributes/)によれば、イベント時間属性は `WATERMARK FOR` 句で定義し、文字列由来なら TIMESTAMP(3)、エポック値なら TIMESTAMP\_LTZ を使うよう勧めています。処理時間属性は `PROCTIME()` の計算列で、戻り値は TIMESTAMP\_LTZ 型です。

```
CREATE TABLE orders (
  order_id   STRING,
  amount     DECIMAL(12, 0),
  ordered_at BIGINT,                                  -- 発生時刻（エポックミリ秒）
  event_time AS TO_TIMESTAMP_LTZ(ordered_at, 3),      -- イベントタイム列
  WATERMARK FOR event_time AS event_time - INTERVAL '20' SECOND
) WITH (
  'connector' = 'kafka',
  'topic' = 'orders',
  'properties.bootstrap.servers' = 'localhost:9092',
  'scan.startup.mode' = 'earliest-offset',
  'format' = 'json'
);

-- イベントタイムで1分ごとの売上を確定させる
SELECT window_start, window_end, SUM(amount) AS sales
FROM TABLE(
  TUMBLE(TABLE orders, DESCRIPTOR(event_time), INTERVAL '1' MINUTES))
GROUP BY window_start, window_end;
```

WATERMARK句の「- INTERVAL ’20’ SECOND」が、Javaの例の20秒と同じ順序乱れ許容幅です。DESCRIPTORに処理時間列を渡せば処理時間ウィンドウになり、同じクエリの意味がまるごと変わります。レビューでは、窓関数に渡している列がどちらの時間属性かを最初に確認してください。

### 発生時刻を信用できないときに起きる未来日付と時計ずれへの防御策

イベントタイムの弱点は、時刻を刻む側の時計をそのまま信用する点にあります。スマートフォンや工場の端末は時計がずれていることがあり、数時間先の未来日付を持つイベントが1件届くだけで、ウォーターマークがその時刻まで一気に進む。以後に届く正常なイベントがすべて遅延扱いになり、窓から捨てられます。

Kafka 4.3系では、CreateTimeのときに限りブローカーが時刻の幅を検査します。`message.timestamp.after.max.ms` の既定値は3,600,000ミリ秒（1時間）で、ブローカー時刻より1時間を超えて未来のメッセージは受け付けません。過去側の `message.timestamp.before.max.ms` は既定で事実上無制限です。トピックごとに締めるなら次のように設定します。

```
bin/kafka-configs.sh --bootstrap-server localhost:9092 \
  --alter --entity-type topics --entity-name orders \
  --add-config message.timestamp.after.max.ms=300000
```

ブローカー側の検査はヘッダ時刻にしか効かないため、業務項目の時刻を使う場合は取り込み側の関数で範囲外の値を弾く処理も要ります。到着時刻との差が閾値を超えたイベントは窓へ入れずに隔離用のストリームへ流し、件数を監視する。発生時刻を記録として持ち続ける設計は[イベントソーシングの状態再生とイベントストア設計](https://www.issoh.co.jp/tech/details/16144/)とも共通する論点で、時刻を後から書き換えない前提でパイプラインを組むと再処理の結果がぶれません。

## タンブリング・スライディング・セッション・グローバルの4方式と選定基準

Flinkが標準で備えるウィンドウアサイナは4種類です。名前は基盤ごとに揺れますが、区切り方の型はこの4つに収まります。それぞれが向く要件と、実装時に踏みやすい地雷を押さえます。仕様の原典は[Flink 2.3系のウィンドウのドキュメント](https://nightlies.apache.org/flink/flink-docs-release-2.3/docs/dev/datastream/operators/windows/)です。

### 重複なく区切るタンブリングウィンドウが向く定点集計の要件と境界

タンブリングウィンドウは固定長で隙間も重複もなく並ぶ、もっとも単純な区切りです。1分ごとの注文件数、1時間ごとのエラー数といった定点観測がそのまま書けます。1件のイベントがちょうど1つの窓に属するため、集計値の合計が元データの総量と一致し、バッチ集計との照合に使えます。

境界の位置には注意が要ります。Flinkの時間窓は開始時刻を含み終了時刻を含まない半開区間で、オフセットを指定しなければエポック基準で整列します。1時間の窓なら 1:00:00.000 から 1:59:59.999 が1区間。日本時間の日次境界に合わせるにはアサイナへオフセットを渡す必要があり、忘れると「日次集計が9時始まりになっていた」という報告が上がってきます。

### 窓が重なるスライディングウィンドウで計算量が増える仕組みと歯止め

スライディングウィンドウは、固定長の窓を一定間隔でずらして並べる方式です。「直近5分の平均を10秒ごとに評価する」といったしきい値監視や不正検知で使います。Kafka Streamsや Azure Stream Analytics では同じ形をホッピングウィンドウと呼ぶため、設計書の用語は揃えておいてください。

コストの構造は単純です。窓長をスライド幅で割った値が、1件のイベントが同時に所属する窓の数になります。5分の窓を10秒ずらしなら30個。イベント1件あたり30箇所のステートが更新され、出力レコード数も30倍です。監視要件で「なるべく細かく」と言われたら、この倍率を提示して要件側と詰めてください。

### 無操作時間で区切るセッションウィンドウのマージ動作と可変長の扱い

セッションウィンドウは、イベントが一定時間途切れたところで区切る可変長の窓です。無操作30分でユーザーの一連の行動を1セッションとみなす、といった行動分析が代表例になります。固定長の2方式と違い、窓の長さも個数も入力次第で変わる点が特徴です。

内部動作はマージです。イベントが届くたびにギャップ分の仮の窓が作られ、既存の窓と時間的に接すれば両者が1つに統合されます。ここから落とし穴が2つ生まれる。ひとつは確定の遅さで、活動が続く限り窓が閉じないため活発なキーのステートは延々と残ります。もうひとつは集計関数の制約で、マージ可能なステートの形でないと統合ができません。連続アクセスがありうるデータでは、セッション長の上限を業務ルールとして設けておくのが安全です。イベントタイムのセッション窓では、遅れて届いた1件が離れていた2つのセッションをつなぎ、確定済みの結果が後から1つに統合されることもあります。

### 件数や独自条件で確定させるグローバルウィンドウとカウント窓の位置

グローバルウィンドウは、キーごとに窓をただ1つだけ持ち、時間では区切らない特殊なアサイナです。既定のトリガが NeverTrigger のため、そのままでは永遠に発火しません。組み込みの CountTrigger を与えれば「100件たまるごとに集計」というカウントウィンドウになります。ただし件数基準は、トラフィックが薄い時間帯に窓が閉じず結果が何時間も出てこない弱点を持つ。受注確定のような業務イベントそのものを区切りの合図にしたい要件に絞って使ってください。

## ウォーターマークと許容遅延で遅延データの締め切りを決める設計手順

窓の種類を決めたら、次は「いつ閉じるか」です。イベントは発生順には届かないため、ここの設計が甘いと集計値が静かにずれます。エンジン側の実装は[Apache Flinkの特徴とユースケースの解説](https://www.issoh.co.jp/tech/details/9735/)で扱った実行モデルの上に乗ります。

### ウォーターマーク到達でウィンドウが確定するまでの発火条件と停止要因

イベント時間の窓は、ウォーターマークが窓の終端を追い越した時点で発火します。ウォーターマークは「この時刻より前のイベントはもう来ないとみなす」という印で、ストリーム上を流れていく。イベント時間アサイナの既定トリガである EventTimeTrigger が、この到達を検知して窓関数を呼び出します。

見落とされがちなのが、ウォーターマークが進む条件そのものです。ウォーターマークは入力イベントのタイムスタンプから導出されるため、データが1件も流れないパーティションがあると全体の進行が止まり、窓が閉じません。深夜帯にトラフィックが途切れる系では、アイドル判定を設定しないと「朝になって一気に結果が出る」挙動になります。印を作る側の設定は[ウォーターマークの生成戦略と進行停止の切り分けを解説した記事](https://www.issoh.co.jp/tech/details/16440/)で扱っています。

### 許容遅延の既定値0が遅れて届いたデータを捨てる動作とその回避策

Flinkのウィンドウのドキュメントが明記するとおり、許容遅延の既定値は0です。ウォーターマーク通過後に到着したイベントは、既定では窓に入らず破棄されます。`allowedLateness` に正の値を与えると、窓の終端に許容遅延を足した時刻までは遅れて届いたイベントも受け入れ、そのたびに窓が再発火して更新後の結果を出します。

設定値は勘で決めないでください。上流の到着遅延を実測し、分位点で判断します。99パーセンタイルが40秒なら許容遅延は1分前後が出発点になり、そこから取りこぼし率とステート保持時間のバランスを見て詰めていく。「安全のため1時間」といった雑な設定は、そのままメモリ不足の原因になります。なおKafka Streamsは grace を書かずに窓を作れないAPI（TimeWindows.ofSizeAndGrace など）へ変わり、既定値に頼らせず設計者へ判断させる方向に寄せています。

### サイドアウトプットで捨てられたデータを回収する運用と突合の手順

許容遅延を超えて届いたデータを黙って捨てると、集計値のずれに気づけません。Flinkには `sideOutputLateData` で遅延データを別ストリームへ流し、`getSideOutput` で受け取る経路が用意されています。ここをオブジェクトストレージへ落とし、遅延データの件数と金額を日次で集計して本系の集計値に対する比率を監視する形が扱いやすい構成です。比率が閾値を超えたら、許容遅延の設定か上流の遅延を疑います。この回収経路がない基盤は、「数字が合わない」と指摘された時点で切り分け手段を持ちません。

## ウィンドウ集計のステート肥大を抑える差分集計と実装パターンの選択

ウィンドウ処理の障害要因は、ほぼステートに集約されます。窓が閉じるまで途中結果を保持する以上、保持の仕方が性能とメモリを直接決める領域です。

### 全件を溜める窓関数と差分集計で保持する状態量が桁違いになる理由

Flinkの窓関数には2系統あります。`ReduceFunction` や `AggregateFunction` は到着のたびに畳み込みを進めるため、窓が保持するのは集約途中の値だけで、1窓あたり数十バイトに収まります。対して `ProcessWindowFunction` は全要素を内部バッファへ保持し、発火時にまとめて処理する方式です。中央値や上位N件のように全要素を見ないと計算できない処理には要りますが、保持量は要素数に比例して伸びます。

判断は単純です。合計・件数・最大値・平均のように畳み込める集計は、必ず差分集計側で書く。窓のメタ情報が結果に要るだけなら両者は併用でき、畳み込んだ値を `ProcessWindowFunction` が受け取る形にすれば、全件バッファを避けたまま開始時刻や終了時刻へアクセスできます。これを知らずに書かれたジョブは、トラフィック増加とともに必ず詰まります。

### トリガとエビクタで中間結果の出力頻度と保持件数を変える判断軸

既定の発火タイミングを変えたい場面はあります。1時間窓の途中経過を10分ごとに見たい、といった要件です。組み込みトリガには ProcessingTimeTrigger、CountTrigger、発火後にステートを消す PurgingTrigger が用意され、その組み合わせで発火頻度を制御します。ただし途中経過を出すなら、下流は同じ窓に対する複数回の更新を受け取る前提で作らなければなりません。要素を間引くエビクタ（CountEvictor・DeltaEvictor・TimeEvictor）はJava限定で、挟むと全要素のバッファが前提になり差分集計の利点が消えます。窓の設計自体を見直すほうが安く済む場面がほとんどです。

### 窓長をスライド幅で割った倍率からステート量を見積もる計算手順

見積もりの骨格は、同時に開いている窓の数×キー数×1窓あたりの状態サイズです。窓の数はタンブリングなら1、スライディングなら窓長÷スライド幅、セッションならアクティブなキー数と一致します。イベントタイムで組む場合は、順序乱れ許容幅と許容遅延の分だけ古い窓が閉じずに残るため、その時間に含まれる窓の数も足し込みます。

具体例を置きます。キー（商品ID）が50万、窓長5分、スライド10秒、差分集計で1窓あたり64バイトなら、30窓×50万×64バイトでおよそ1GB弱。ここに許容遅延分の保持とチェックポイントのコピーが加わります。RocksDBのようなディスク併用のステートバックエンドなら数GB規模までは現実的な一方、全件バッファを持てば同じ条件で2桁跳ね上がる。設計前にこの掛け算を1回やるだけで、方式選定の議論が感覚論から抜けます。見積もりを超えて流入したときに送出量そのものを抑える手段は[バックプレッシャーによる流量制御の解説](https://www.issoh.co.jp/tech/details/16432/)で整理しています。

## SQLのウィンドウ関数とAPI実装のどちらで書くかを分ける境界線

窓の実装はJavaやScalaのAPIだけではありません。SQLで書ける範囲が広がり、データ担当者が直接ウィンドウ集計を定義する構成も現実的になりました。どちらで書くかを整理します。

### TUMBLE・HOP・CUMULATE・SESSIONの4関数と出力される3列

Flink SQLでは、テーブルを引数に取るウィンドウTVFという形式で窓を宣言します。4つの関数が用意され、いずれも結果に window\_start・window\_end・window\_time の3列が追加されます。window\_time は window\_end から1ミリ秒引いた値で、後続の集計や結合で時刻属性として使われる列です。

| 関数       | 区切り方     | 主な引数              | 向く要件    |
| -------- | -------- | ----------------- | ------- |
| TUMBLE   | 固定長・重複なし | 時刻列, size, offset | 定点集計    |
| HOP      | 固定長・重複あり | 時刻列, slide, size  | しきい値監視  |
| CUMULATE | 始点固定で伸長  | 時刻列, step, size   | 累計の途中経過 |
| SESSION  | 無操作時間で分割 | 時刻列, gap          | 行動分析    |

SESSION はストリーム実行専用でバッチモードには対応せず、結合やTopNとの組み合わせも限定的な扱いです。オフセット引数は TUMBLE・HOP・CUMULATE で任意指定できます。なお旧来のグループ化ウィンドウ関数は非推奨で、新規の記述はTVF側へ寄せる流れです。SQLレイヤ全体の実行モデルは[Flink SQLの動的テーブルと継続クエリの解説](https://www.issoh.co.jp/tech/details/16428/)で扱っています。

### 日次の途中経過を1関数で出せるCUMULATEが効く集計要件

4関数のうち、API側に直接の対応物がないのが CUMULATE です。始点を固定したまま終端をstep刻みで伸ばす窓で、「当日0時からの累計売上を10分ごとに更新する」という要件がそのまま1文で書けます。同じことをDataStream APIで書くなら、グローバルウィンドウに独自トリガを付けるか状態を持つ関数を自前で書くことになる。日次・月次の進捗ダッシュボードは要望として頻出するため、この形が中心ならSQL側で書く判断が有利です。逆に、窓の発火条件をイベントの中身で動的に変える処理はSQLの語彙に収まりません。宣言的に書ける定型の窓はSQL、条件分岐を含む制御はAPI。境界はここに引きます。

## ウィンドウ処理を採用する条件とウィンドウ化を見送る場面の線引き

窓は無料ではありません。ステート・許容遅延・再発火という運用負担を抱え込む選択なので、要件が本当に窓を求めているかを先に確かめます。

### ウィンドウ集計が要る要件と状態TTLだけで足りる要件の見分け方

窓が要るのは「区間ごとの値」に業務上の意味がある場合だけです。1分ごとの取引件数、直近5分の異常スコア、セッション単位の回遊数。これらは区間そのものが分析単位になっています。一方で「現在の在庫数」のように最新値だけを知りたい要件に窓は不要で、キー単位の状態をTTLで打ち切るほうが単純に済みます。

判断を誤りやすいのが「直近1時間のユニークユーザー数」のような要件です。区間の値に見えて実態は近似で足りるケースが多く、その場合はスライディング窓を並べるよりHyperLogLogのような近似構造を状態に持つほうが桁違いに安く上がります。要件の言葉をそのまま窓へ翻訳しないでください。こうした基盤の設計から構築までを外部と進める選択肢もあり、当社の[データ分析基盤構築・MLOps構築支援](https://www.issoh.co.jp/service/ai/data-platform/)でも立ち上げを支援しています。

### イベントタイムと処理時間のどちらで組むかを決める3つの確認質問

時刻の選択は、次の3つの質問で決まります。1つでも「はい」があればイベントタイムで組み、すべて「いいえ」のときだけ処理時間を検討してください。

1. 集計結果を金額・件数として業務や会計の資料が参照するか
2. 障害復旧やバックフィルで、過去分を流し直す運用が想定されるか
3. 端末のオフラインや上流の再送で、発生から到着まで数秒以上の遅れが出るか

処理時間が正解になるのは、処理基盤自体の健康状態を見る監視、たとえばスループットや処理遅延のダッシュボードです。この用途では「いま何件さばいているか」こそが知りたい値で、発生時刻で並べ直す意味がありません。逆に業務指標を処理時間で出していると、障害で10分止まった直後の窓に10分分のイベントが流れ込み、売上が急増したように見えます。イベントタイムの導入コストはタイムスタンプ抽出とウォーターマーク設定の数行で済むため、迷ったらイベントタイムに倒すのが安全側の判断です。

### 窓長・スライド幅・許容遅延を業務要件から逆算して決める設計手順

パラメータは次の順で決めます。技術側の都合から入ると、必ずどこかで矛盾します。

1. 業務が判断に使う時間粒度を決める（例：5分以内に異常を検知したい）
2. その粒度から窓長を置く（検知の感度に合わせて5分または10分）
3. 結果の更新頻度からスライド幅を決める（30秒更新なら倍率は10〜20）
4. 上流の到着遅延を実測し、分位点から許容遅延を置く
5. 倍率×キー数で見積もったステート量が予算に収まるか確認する

5番目で予算を超えたら、戻るのは3番目です。スライド幅を粗くすれば倍率が下がり、ステートも出力レコード数も比例して減ります。窓長を削ると分析の意味が変わるため、削る順序は後回しにしてください。この逆算を先にやっておけば、稼働後に仕様変更を業務側へ相談する事態を避けられます。

### 実装で頻出する3つの失敗パターンと稼働前に潰しておく確認項目

現場で繰り返し見る失敗は3つです。第一に、処理時間ウィンドウで組んでしまい再処理のたびに数字が変わるパターン。バックフィルの瞬間に過去の集計値が上書きされ、レポートの整合が崩れます。第二に、許容遅延を既定の0のまま出して遅延データを黙って捨てているパターンで、バッチ集計との差分が数パーセント出てから発覚します。第三が、スライディング窓の倍率を意識しないまま「細かいほうが良い」と設定してしまう例。5分窓を1秒スライドにすれば倍率は300で、ステートも出力も300倍です。

稼働前の確認項目は5つに絞れます。窓の時刻属性がイベント時間か、イベントタイムをどの項目から取るかが設計書に明記されているか、許容遅延と遅延データの回収経路が設定されているか、見積もったステート量が予算内か、ウォーターマークが止まりうるアイドル状態に対処しているか。この5点を設計レビューの定型項目に置くだけで、稼働後の障害はかなり減ります。

## よくある質問

ウィンドウ処理とイベントタイムの実装で迷いやすい論点をまとめます。用語の混同と設定値の決め方に絞って答えます。

### イベントタイム（ストリーム処理）とは何ですか？処理時間とどう違いますか？

イベントタイムは、イベントが発生した時刻をレコード自身から取り出して使う時刻の扱い方です。処理時間はエンジンがそのレコードを処理した瞬間の壁時計を使います。イベントタイムで窓を切れば、同じ入力を何度流し直しても同じ集計結果になる。その代わり到着順のずれを吸収するためにウォーターマークと許容遅延の設計が必要になります。処理時間は設定が要らず遅延も小さい反面、再処理で結果が変わるため業務指標には向きません。

### ウィンドウ処理とSQLのウィンドウ関数（OVER句）は同じものですか？

別物です。SQLのOVER句は行ごとの移動平均や順位を計算する分析関数で、対象データはすでに全件そろっています。ストリームのウィンドウ処理は終端のないイベント列を区切って有限の集合を作り出す仕組みで、そもそも全件がそろう瞬間がありません。Flink SQLでウィンドウTVFという専用の構文が用意されているのも、この差があるためです。

### タンブリングとホッピングとスライディングはどう違いますか？

タンブリングは固定長で重複せず並ぶ窓、ホッピングは固定長の窓を一定間隔でずらして重ねる窓を指します。混乱の元は「スライディング」で、Flinkではホッピングと同義、Kafka Streamsではレコード間の時間差で定義される別方式です。設計書では「固定長・重複あり・スライド幅30秒」のように性質で書くと誤解が起きません。窓長やスライド幅の決め方は、業務が判断に使う時間粒度から逆算する本文の手順を参照してください。

### 遅れて届いたデータはどこまで待てばよいですか？

上流の到着遅延を実測してから決めます。発生時刻と到着時刻の差を数日分集め、99パーセンタイルに少し余裕を足した値を出発点にする手順が扱いやすい形です。待ち時間を伸ばすほど取りこぼしは減る一方、窓のステートが長く残り結果の遅延も伸びます。取りこぼした分はサイドアウトプットへ回収し、件数の比率を見ながら調整してください。

### マネージドサービスでもウィンドウ処理は同じように書けますか？

おおむね同じ考え方が通ります。Amazon Managed Service for Apache FlinkはFlinkのAPIとSQLをそのまま使え、Google Cloud DataflowはApache Beamのウィンドウモデル、Azure Stream AnalyticsはSQL方言で同種の窓を提供しています。差が出るのは遅延データの扱いと上限値、それにイベントタイムとして使う列の指定方法です。窓の種類・時刻の取得元・許容遅延・出力頻度を要件として書き出しておけば、基盤を替えても設計の翻訳で済みます。

## 関連記事

- [Apache Beamとは｜バッチとストリームを同じコードで書く仕組みとランナー選定の判断基準](https://www.issoh.co.jp/tech/details/16105/)：ウィンドウとトリガを抽象化したモデルを持つ実行基盤で、複数ランナーへ移植する場合の前提になります。
- [ksqlDBとは？ストリームとテーブルの仕組み・Kafka Streamsとの違いと採用判断を解説](https://www.issoh.co.jp/tech/details/16426/)：Kafka単独構成でウィンドウ集計をSQLで書く場合の選択肢として比較できます。
- [イベント駆動アーキテクチャとは？仕組み・実装パターンと採用判断を実装者目線で解説](https://www.issoh.co.jp/tech/details/13226/)：窓で集計する対象となるイベントが、どう発行され流れてくるかという上流側の設計です。

---

出典: [ストリームウィンドウ処理とは？イベントタイムの扱いと4方式の使い分け・遅延データの締め切り設計を解説](<https://www.issoh.co.jp/tech/details/16430/>)（株式会社一創）
