# ストリーム結合
## 定義
ストリーム結合(stream join)とは、バッチ処理のジョイン(→ [[@2026__OReilly__Designing Data-Intensive Applications 2E - Chapter 11 Batch Processing]] "Joins and Grouping")を、無限に到着し続けるイベントに対して継続的に行う操作である。新しいイベントがいつでも到着しうるため、バッチジョインより難しい。書籍は3種類に分類する: (1) *stream–stream ジョイン*(2つのアクティビティイベントストリームをウィンドウ内で突き合わせる)、(2) *stream–table ジョイン*(アクティビティストリームをデータベースの変更ログでエンリッチする)、(3) *table–table ジョイン*(2つのデータベース変更ログを突き合わせ、マテリアライズドビューの変更ストリームを生成する)。3種はいずれもストリームプロセッサが一方の入力から状態を保持し、もう一方の入力を処理する際にその状態を照会するという構造を共有する。(Source: [[@2026__OReilly__Designing Data-Intensive Applications 2E - Chapter 12 Stream Processing]] "Stream Joins")
## stream–stream ジョイン(ウィンドウジョイン)
検索クエリイベントとクリックイベントをセッションIDで突き合わせクリックスルー率を計算する例が典型。クリックは検索から数秒後かもしれず、数週間後かもしれず、あるいは全く来ないかもしれない。実装は、直近のイベントをセッションIDでインデックス化した状態を保持し、新しいイベントが来るたびに反対側のインデックスを照会してマッチを探す。ウィンドウが期限切れになってもマッチが見つからなければ「クリックなし」のイベントを発行する。検索結果へクリック詳細を直接埋め込むだけではジョインの代替にならない——クリックされなかった検索を把握できず、正確なクリックスルー率の計算には両方のイベントストリームが必要になる。(Source: [[@2026__OReilly__Designing Data-Intensive Applications 2E - Chapter 12 Stream Processing]] "Stream–stream join (window join)")
## stream–table ジョイン(ストリームエンリッチメント)
アクティビティイベント(ユーザーIDを含む)をユーザープロファイルデータベースの情報で拡張(enrich)する処理。リモートデータベースへ都度クエリするのは低速でデータベースに過負荷をかけるリスクがあるため、データベースのコピーをストリームプロセッサへローカルにロードして*ハッシュジョイン*(インメモリハッシュテーブルまたはローカルディスクのインデックス)で結合する方式が使われる。バッチジョインが時点スナップショットを入力とするのに対し、ストリームプロセッサは長時間稼働するためローカルコピーを最新に保つ必要があり、これは CDC(→ [[変更データキャプチャ(CDC)]])で解決できる。ユーザープロファイルの変更ログを購読しローカルコピーを更新すれば、実質的に2つのストリーム(アクティビティイベントとプロファイル更新)のジョインになる。テーブル側の変更ログに対するウィンドウは「時間の始まりまで遡る」概念的に無限のウィンドウであり、新しいバージョンが古いバージョンを上書きする点が stream–stream ジョインと異なる。(Source: [[@2026__OReilly__Designing Data-Intensive Applications 2E - Chapter 12 Stream Processing]] "Stream–table join (stream enrichment)")
## table–table ジョイン(マテリアライズドビュー保守)
ソーシャルネットワークのホームタイムライン(フォロー中のユーザーの投稿をまとめた per-user のインボックス)がこのパターンの典型例である。投稿(`posts`)とフォロー関係(`follows`)という2つのテーブルを結合するクエリを継続的にマテリアライズドビュー化する処理として捉えられ、`posts` の変更はその時点のフォロワー集合と、`follows` の変更はその時点の投稿集合と、それぞれジョインされる。ストリームを状態の時間微分とみなす見方に立つと、この処理は積の微分法則 `(u·v)′ = u′v + uv′` に対応する。(Source: [[@2026__OReilly__Designing Data-Intensive Applications 2E - Chapter 12 Stream Processing]] "Table–table join (materialized view maintenance)") → [[マテリアライズドビューとデータキューブ]]
## ジョインの時間依存性
3種のジョインに共通するのは、状態を保守する側の入力の到着順序が結果に影響しうる点である。異なるストリーム(またはパーティション)間には一般に順序保証がないため、ある時点で状態が変化しつつある2つの入力をジョインする際、「どの時点の状態でジョインするか」という問いが生じる。ジョインの非決定性はデータウェアハウスで*slowly changing dimension*(SCD)として知られる問題であり、対処法は (1) 変更のたびに一意なバージョン識別子を発行し、その時点で有効だった識別子をイベントへ埋め込むことでジョインを決定的にする(ただし全バージョンの保持が必要になりログコンパクションができなくなる代償を伴う)、または (2) 必要な値(例: 税率)を送信時点のイベントへ直接非正規化して埋め込む、のいずれかである。(Source: [[@2026__OReilly__Designing Data-Intensive Applications 2E - Chapter 12 Stream Processing]] "Time dependence of joins")
## 横断的知見
- (この節は今後、複数ソースの突き合わせで得られた知見を蓄積する。現時点では単一ソースからの知見のみ。)
## 未解決の問い
- stream–table ジョインでローカルにキャッシュするデータベースコピーのサイズが大きい場合(例: 数億ユーザーのプロファイル)、CDC ベースの追従とストレージ容量のトレードオフはどこまでスケールするか。
- table–table ジョインの積の微分法則(u′v + uv′)は、3つ以上のテーブルを結合する場合にどのように一般化されるか。DBSP(差分計算)のような形式化はどこまでこれをカバーするか。
- slowly changing dimension のバージョン識別子方式とログコンパクションの両立不能性は、実務上どの程度の追加ストレージコストを要求するか。
## 関連
- ソース: [[@2026__OReilly__Designing Data-Intensive Applications 2E - Chapter 12 Stream Processing]]("Stream Joins")
- 概念: [[変更データキャプチャ(CDC)]] / [[マテリアライズドビューとデータキューブ]]
- 関連章: [[@2026__OReilly__Designing Data-Intensive Applications 2E - Chapter 11 Batch Processing]]("Joins and Grouping")
## 出典
- [[@2026__OReilly__Designing Data-Intensive Applications 2E - Chapter 12 Stream Processing]]("Stream Joins" 節)