Flink SQLとDataStream APIの比較: 新規開発者のための包括ガイド

> 本記事は[こちらのブログ](https://www.alibabacloud.com/blog/compare-flink-sql-and-datastream-api-comprehensive-guide-for-new-developers_602133?spm=a2c65.11461433.0.0.4ee35355DE9DWe)を参考にしています。
> 翻訳にはアリババクラウドの[ModelStudio](https://www.alibabacloud.com/en/product/modelstudio)([Qwen](https://www.alibabacloud.com/en/solutions/generative-ai/qwen))を使用しております。

# Apache Flink: ストリーム処理のための主要なフレームワーク

Apache Flinkは、ストリーム処理向けの主要なフレームワークとして台頭しており、2つの異なるプログラミングインターフェースを提供しています：Flink SQL（宣言型）とDataStream API（命令型）。このガイドでは、コード例、アーキテクチャ図、およびパフォーマンスに関する洞察を通じて、開発者がこれらの違い、強み、および理想的な使用例を理解する手助けをします。

## Flink SQL: SQL駆動型ストリーム処理

Flink SQLは、標準的なSQL構文を使用してバウンダリーのある（バッチ）データやバウンダリーのない（ストリーミング）データを処理するための宣言型アプローチを提供します。主な特徴は以下の通りです：

- **統合されたバッチ/ストリーム処理**: 歴史的またはリアルタイムデータに対して同じSQLクエリを実行可能です。
- **ウィンドウ関数**: トレインブリング、スライディング、セッションウィンドウに対応したビルトインサポート。
- **コネクタエコシステム**: Kafka、JDBC、Hiveなどとのネイティブ統合。sql
-- Kafkaベースのテーブルを作成
CREATE TABLE user_clicks (
  user_id INT,
  url STRING,
  event_time TIMESTAMP(3),
  WATERMARK FOR event_time AS event_time - INTERVAL 5 SECOND
) WITH (
  connector = kafka,
  topic = clicks,
  properties.bootstrap.servers = localhost:9092,
  format = json
);

-- Window TVFを使用したトレインブリングウィンドウ集計
SELECT 
  window_start,
  window_end,
  COUNT(user_id) AS click_count
FROM TABLE(
  TUMBLE(
    TABLE user_clicks,
    DESCRIPTOR(event_time),
    INTERVAL 1 MINUTE
  )
)
GROUP BY window_start, window_end;

## Flink SQL: 中核となる概念

### ダイナミックテーブル

Flink SQLは、時間とともに進化するダイナミックテーブルとしてストリームを扱います。これにより、無限のデータストリームに対するSQL操作が可能になります。

![ダイナミックテーブル](https://img.alicdn.com/imgextra/i3/O1CN01qeTpBw27Bg7aeZlij_!!6000000007759-0-tps-1618-172.jpg)

> **Tips:** ダイナミックテーブルについてさらに学ぶには、[Apache Flinkのドキュメント](https://nightlies.apache.org/flink/flink-docs-master/docs/dev/table/concepts/dynamic_tables/)をご覧ください。

### 時間属性

イベントタイムと処理タイムは、Apache Flinkにおける2つの基本的な概念であり、特にリアルタイムデータを扱うシナリオにおいて、ストリーム処理がどのように機能するかを理解する上で重要です。

- **イベントタイム (Event Time)**: イベントが実際に発生した時刻を指します。この時間は通常、データ自体に埋め込まれたタイムスタンプとして存在し、センサーが読み取りを行った時やログエントリが生成された場合などが該当します。例えば、IoTアプリケーションでは、センサーが変化を検出した瞬間に記録されたタイムスタンプがイベントタイムになることがあります。
  
- **処理タイム (Processing Time)**: 一方で、処理タイムはイベントがシステムによって処理された時間を指します。これは一般的に、操作を実行しているマシンのローカルシステムクロックによって決定されます。処理タイムはイベントタイムと比較して管理が簡単で、システムクロックに依存するため直感的に扱いやすいです。

以下はFlink SQLでのイベントタイムと処理タイムの使用例です：sql
CREATE TABLE user_actions (
  user_id STRING,
  action STRING,
  event_time TIMESTAMP(3),                  -- データから取得されるイベントタイム
  processing_time AS PROCTIME(),            -- Flinkによって生成される処理タイム
  WATERMARK FOR event_time AS event_time - INTERVAL 5 SECOND  -- イベントタイムのウォーターマーク
) WITH (
  connector = kafka,
  topic = user_actions,
  format = json
);

### ウィンドウ集計

ウィンドウ集計は、データ分析における強力な技術で、時間やその他の基準に基づいて定義されたデータウィンドウに対して計算を行うために使用されます。これにより、特定の間隔やデータのパーティションに対して合計、平均、カウントなどのメトリクスを計算できます。

**時系列ごとのユーザー活動を計算:**sql
SELECT 
  window_start,
  window_end,
  COUNT(DISTINCT user_id) AS active_users
FROM TABLE(
  TUMBLE(
    TABLE user_actions,               
    DESCRIPTOR(event_time),         
    INTERVAL 1 HOUR                
  )
)
GROUP BY window_start, window_end;

### ストリーム結合

Flink SQLのストリーム結合は、Flinkの統合されたバッチ/ストリーム処理モデルを利用して無限のデータストリームをリアルタイムに関連付けます。有限のデータセットを処理するバッチ結合とは異なり、ストリーム結合は継続的なデータ到達、順序外のイベント、状態管理の課題に対応しなければなりません。以下は主要概念と実装詳細に関する構造化された分析です。

Flink SQLはストリームデータに対して3つの主要な結合パターンをサポートしています：

| 結合タイプ | 特徴 | 状態管理 | 使用例 |
|------------|-------|----------|--------|
| **通常結合 (Regular Join)** | 新しいデータ到着時に継続的に更新、すべてのSQL結合タイプ（INNER/LEFT/RIGHT/FULL）をサポート | 有効期限（TTL）まで無制限の状態保持 | リアルタイムダッシュボードのメトリクス |
| **インターバル結合 (Interval Join)** | 指定された時間範囲内でイベントを結合、時間属性（処理時間またはイベントタイム）を使用 | 境界あり状態（時間ウィンドウ外のイベントは破棄） | 5分間隔での不正検出 |
| **テンポラル結合 (Temporal Join)** | ストリームをバージョン管理されたテーブル（例：ディメンションテーブル）と結合、`FOR SYSTEM_TIME AS OF`構文を使用 | テンポラルテーブルの更新頻度に紐づく状態 | 注文データを為替レートで充填 |
| **ルックアップ結合 (Lookup Join)** | 外部テーブルデータでストリームを充填、オンデマンド外部システムクエリ | 永続的な状態なし（外部ルックアップ） | データベースからの製品情報を注文に追加 |

以下は、Flink SQLでクリックストリームデータをユーザープロファイルで充填する例です：sql
CREATE TABLE user_profiles (  
  user_id STRING,  
  country STRING,  
  PRIMARY KEY (user_id) NOT ENFORCED  
) WITH (  
  connector = jdbc,  
  url = jdbc:mysql://localhost:3306/mydb,  
  table-name = users  
);  

SELECT  
  u.user_id,  
  u.country,  
  COUNT(c.click_id) AS click_count  
FROM clicks c  
JOIN user_profiles u  
ON c.user_id = u.user_id;  

### MATCH_RECOGNIZEによるパターン検出

MATCH_RECOGNIZE句は、2016年にSQL:2016標準の一部として導入された強力なSQL機能で、リレーショナルデータ内のパターン認識を目的として設計されています。これにより、ユーザーはデータの行内に特定のパターンを定義して検出でき、複雑なイベント処理（CEP）や時系列分析に特に有用です。

**失敗したログインシーケンスの検出:**sql
SELECT *  
FROM login_attempts  
MATCH_RECOGNIZE (  
  PARTITION BY user_id  
  ORDER BY event_time  
  MEASURES  
    START_ROW.event_time AS start_time,  
    LAST(FAIL.event_time) AS end_time  
  AFTER MATCH SKIP TO LAST FAIL  
  PATTERN (START FAIL{3})  
  DEFINE  
    FAIL AS action = login_failed  
);

### Flink SQLにおけるストリームとバッチ

Flink SQLは、バッチ処理を入力データがバウンダリーである特別なケースとして扱います。この統合アプローチにより
# Flink SQLのストリーミングモードとバッチモードにおける時間処理

![1](https://img.alicdn.com/imgextra/i3/O1CN01Tl5ChP1t0m4UEQSkX_!6000000001568-0-tps-1716-522.jpg)

## 時間処理の概要

| 面 | ストリーミングモード | バッチモード |
| --- | --- | --- |
| **時間の意味** | イベントタイム/処理タイム（ウォーターマーク付き） | 暗黙的な時間（データの順序に関係なし） |
| **ORDER BYサポート** | 時間に基づくソートのみ | 任意のカラムによるソート |
| **ウォーターマーク** | イベントタイム処理に必要 | 適用不可 |

### サンプルウォーターマーク定義sql
CREATE TABLE orders (
  order_id STRING,
  order_time TIMESTAMP(3),
  WATERMARK FOR order_time AS order_time - INTERVAL 5 SECOND
) WITH (...);

## 結合操作

| 結合タイプ | ストリーミングモード | バッチモード |
| --- | --- | --- |
| **通常の結合** | 新しいデータが到着するたびに継続的に更新 | 単一の完全な計算 |
| **テンポラル結合** | 時間属性を使用して最適化 | 利用不可 |
| **ステート管理** | 継続的なステート保持が必要 | ジョブ中にはステートなし |

### ストリーミング結合の例sql
SELECT * 
FROM orders
JOIN currency_rates FOR SYSTEM_TIME AS OF orders.order_time
ON orders.currency = currency_rates.currency

## 時間の意味とウォーターマーク

効果的なストリーミング結合は、正確な時間処理に依存します。

### イベントタイム結合sql
-- クリックイベントテーブル (ウォーターマーク付き)
CREATE TABLE clicks (
  user_id STRING,
  click_time TIMESTAMP(3),
  WATERMARK FOR click_time AS click_time - INTERVAL 30 SECOND
) WITH (
  connector = kafka,
  topic = clicks,
  format = json
);

-- 購入イベントテーブル (ウォーターマーク付き)
CREATE TABLE purchases (
  user_id STRING,
  purchase_time TIMESTAMP(3),
  amount DECIMAL(10,2),
  WATERMARK FOR purchase_time AS purchase_time - INTERVAL 20 SECOND
) WITH (
  connector = kafka,
  topic = purchases,
  format = json
);

-- イベントタイムインターバル結合クエリ
SELECT
  c.user_id,
  c.click_time,
  p.purchase_time,
  p.amount,
  TIMESTAMPDIFF(MINUTE, c.click_time, p.purchase_time) AS mins_diff
FROM clicks c
JOIN purchases p 
ON c.user_id = p.user_id
AND p.purchase_time 
  BETWEEN c.click_time - INTERVAL 30 MINUTE 
  AND c.click_time + INTERVAL 15 MINUTE;

### ウォーターマーク伝搬メカニズム

両方のテーブルからのウォーターマークは、結合操作を通じて伝搬され、下流のウォーターマークは次のように計算されます:  
`MIN(clicks_watermark, purchases_watermark)`

### ステート保持ポリシー

Flinkは自動的にステートデータを `30分 + 15分 = 45分` 保持します。この間隔を超えた古いステートは自動的にクリーンアップされます。

### 遅延データの処理

ウォーターマークが `purchase_time + 15分` を超えると、対応するクリックイベントは新しい購入イベントを待たなくなり、最終的なウィンドウ結果が出力されます。

### ルックアップ結合sql
SELECT * FROM A
JOIN B FOR SYSTEM_TIME AS OF A.PROCTIME()
ON A.key = B.key

これはディメンションテーブルの最新バージョンを使用します。

## Flink SQLの使用タイミング

| 使用ケース | 例 |
| --- | --- |
| **リアルタイムダッシュボード** | 5秒ごとのメトリクス集計 |
| **ETLパイプライン** | IoTデバイスデータのクリーニングと変換 |
| **不正検出** | トランザクションストリームでのパターンマッチング |
| **顧客分析** | クリックストリームとユーザープロファイルの結合 |

## Flink SQLと他のFlink APIとの関係

Apache Flinkは、アクセス性、柔軟性、パフォーマンスのバランスを取るために階層的なAPIアーキテクチャを提供します。Flink SQLはこの階層の最上位にありますが、DataStream APIやTable APIなどの低レベルAPIと深い相互運用性を持っています。

![2](https://img.alicdn.com/imgextra/i3/O1CN01Tl5ChP1t0m4UEQSkX_!6000000005840-2-tps-974-410.png)

FlinkのAPIは異なる抽象化レベルとユースケースに対応するために構成されています。

| APIレイヤー | 主な特徴 |
| --- | --- |
| **Flink SQL** | • ANSI SQL準拠<br>• バッチ/ストリーム統合のための宣言的シンタックス<br>• 最高レベルの抽象化 (#user-content-15) |
| **Table API** | • 言語統合型 (Java/Scala/Python)<br>• 関係演算（例: select, join）<br>• Flink SQLと共通のプランナーを使用 (#user-content-3) |
| **DataStream API** | • 命令型プログラミング (Java)<br>• 時間、ステート、ウィンドウに対する詳細な制御<br>• ストリーム処理の基盤 |

---

### DataStream API: 詳細な制御

DataStream APIは、複雑なイベント処理に理想的な低レベルのストリーミングロジック制御を提供します。

#### State管理

- 手動でステートフル操作を制御。
- ユーザー定義関数 (UDF) を構築し、独自の要件に対応。
- イベントタイムと処理タイムの明示的な処理。java
DataStream<UserClick> clicks = env
  .addSource(new FlinkKafkaConsumer<>(clicks, new JSONDeserializer(), properties));

DataStream<ClickCount> counts = clicks
  .keyBy(click -> click.userId)
  .window(TumblingEventTimeWindows.of(Time.minutes(1)))
  .process(new CountClicksPerWindow());

counts.addSink(new ElasticsearchSink<>());

---

### DataStream APIを使用したデータ処理

#### プログラミングモデル

すべてのDataStreamプログラムは次の構造に従います:java
// 1. 実行環境を作成
StreamExecutionEnvironment env =  
    StreamExecutionEnvironment.getExecutionEnvironment();  

// 2. データソースを定義
DataStream<String> text = env.readTextFile("input.txt");  

// 3. 変換を適用
DataStream<Tuple2<String, Integer>> counts =  
    text.flatMap(new Tokenizer())  
        .keyBy(0)  
        .sum(1);  

// 4. 出力シンクを定義
counts.print();  

// 5. 実行
env.execute("WordCount");

---

### 主要コンポーネント

| コンポーネント | 説明 |
| --- | --- |
| **StreamExecutionEnvironment** | ジョブ設定のエントリーポイント |
| **DataStream** | 不変の分散データコレクション |
| **Transformation** | データ処理ロジックを定義するオペレータ |
| **Sink** | 出力システム (Kafka, JDBC, ファイルなど) |

---

### 主要操作の説明

![3](https://img.alicdn.com/imgextra/i4/O1CN01M9p6bI1s8jKtA9QWk_!6000000005722-0-tps-1660-480.jpg)

#### 1. データ取り込み (Sources)

- **目的**: 外部システムからFlinkにデータを読み込む。
- **主なソース**:
  - メ
# DataStream APIとFlink SQLの詳細な比較

精度: 時間、状態、および分割に関するきめ細かい制御。  
回復力: 障害や遅れて到着するデータを処理するための組み込みメカニズム。  
スケーラビリティ: 分散キーとオペレーター全体での並列実行。  
新しい開発者は、シンプルなパイプライン（例：Source → Map → Sink）から始め、徐々に`keyBy`や`window`のようなステートフルな操作を取り入れて習熟度を高めてください。DataStream APIの真価は、これらの基本的な構成要素を複雑でプロダクションレベルのストリーミングロジックに組み合わせる能力にあります。

## DataStream APIを選ぶタイミングは？

Apache FlinkのDataStream APIは、ユーザーがストリーム処理のさまざまな側面に対してきめ細かい制御を必要とするシナリオで特に有利です。以下に、DataStream APIを選択するべきタイミングについて詳しく説明します。


| シナリオ                | DataStream APIの利点                 |
|-------------------------|--------------------------------------|
| カスタムウィンドウトリガー | ウィンドウロジックの完全な制御          |
| 低レベルのステートアクセス | 直接的なステート操作                    |
| マイクロ秒単位のレイテンシ | SQL最適化オーバーヘッドの回避           |
| 複雑なイベントパターン      | ネイティブProcessFunctionサポート     |


### カスタムウィンドウトリガー:
DataStream APIは、ウィンドウロジックに対する完全な制御を提供し、ユーザーがカスタムウィンドウオペレーター、アサイナー、トリガー、およびエビクターを定義できるようにします。この柔軟性は、データがどのようにグループ化され、時間間隔に基づいて処理されるかを正確にコントロールする必要があるアプリケーションにとって重要です。例えば、特定の条件または時間ベースのロジックに基づいてイベントを処理するために、`ProcessFunction`を使用してカスタムウィンドウトリガーを実装できます。

### 低レベルのステートアクセス:
DataStream APIはステート管理への直接アクセスを提供し、変換中にステートを直接操作することができます。これは、複雑なステートフル計算が必要な場合やセッションレベルの情報を維持する必要があるアプリケーションに特に有用です。ユーザーは、`ValueState`、`ReducingState`、および`ListState`などの低レベルAPIを利用してステートを効率的に管理できます。

### マイクロ秒単位のレイテンシ:
DataStream APIはSQL最適化のオーバーヘッドを回避することで、より低いレイテンシでの処理を可能にします。これはリアルタイム分析や即時の意思決定が重要なアプリケーションにとって不可欠です。例えば、リアルタイムモニタリングダッシュボードや金融取引プラットフォームなどのアプリケーションは、DataStream APIによって提供される低いレイテンシの恩恵を受けます。

### 複雑なイベントパターン:
DataStream APIは、ネイティブの`ProcessFunction`サポートを通じて複雑なイベントパターンをサポートしています。これにより、開発者はパターン認識、異常検出、ルールベースのアラートなど、高度なイベント駆動型ロジックを実装できます。さらに、FlinkのComplex Event Processing (CEP)ライブラリとの統合により、リアルタイムのパターン認識と処理機能が強化されています。

### Flink SQLとDataStream APIの主な違い一覧

| 機能             | Flink SQL                         | DataStream API                       |
|------------------|-----------------------------------|---------------------------------------|
| 抽象化レベル      | 高い（宣言的SQL）                  | 低い（命令型Java/Scala）               |
| 学習曲線          | SQLユーザー向けに簡単              | 急峻（コーディングスキルが必要）       |
| ステート管理      | 自動（Flinkによって管理）          | 手動（開発者による制御）               |
| カスタムロジック   | UDF/UDTFに限定                     | 完全な柔軟性（カスタムオペレーター）    |
| パフォーマンス     | Calciteプランナー経由で最適化        | 実装効率に依存                        |
| 使用ケース         | ETL、リアルタイム分析              | 複雑なイベント処理、低レイテンシ        |


## Flink SQL vs DataStream API: どちらを選ぶべきか？

| 基準               | Flink SQL                         | DataStream API                       |
|--------------------|-----------------------------------|---------------------------------------|
| 開発速度            | ✔️ （宣言的、コーディング不要）     | ❌ （Java/Scalaの専門知識が必要）      |
| カスタムステートロジック | ✔️ （UDAFやPTFで拡張可能）          | ✔️ （KeyedStateで完全な制御）          |
| BIツール統合        | ✔️ （JDBC/ODBCコネクタ利用可能）    | ❌ （カスタムシンク開発が必要）        |
| レイテンシプロファイル | ✔️ サブ秒レベルのレイテンシ（一般的には100ms-1s）✔️ マイクロバッチ最適化 | ✔️ ミリ秒レベルのレイテンシ✔️ 真のイベントごとの処理✔️ ネイティブの低レベル最適化 |


### トレードオフ:
**Flink SQLを使用する場合**: プロトタイピングの迅速化、標準的な操作、開発の容易さが必要な場合、特に簡単なデータ処理タスク（データクリーニング、リアルタイムレポート作成、データウェアハウスなど）に取り組む際に有効です。  
**DataStream APIに切り替える場合**: 高度なステート管理、複雑なロジック、または低レイテンシが必要なシナリオで役立ちます。特に、処理パイプライン全体を完全に制御し、より複雑な開発に時間を投資できる場合に有効です。

### ハイブリッドアプローチ:
場合によっては、ハイブリッドアプローチが有益なこともあります。例えば、最初のデータ処理にはFlink SQLを使用し、その後結果をDataStreamに変換してさらに複雑な処理を行うことで、両方のAPIの長所を活用できます。

Flink SQLとDataStream APIの選択は、プロジェクトの具体的な要件（開発速度、ステートロジックの複雑さ、BIツールとの統合、レイテンシ要件など）に基づいて行われるべきです。また、両方のAPIの利点を組み合わせたハイブリッドアプローチも考慮に入れることができます。

## はじめに役立つヒント

- **SQLから始める**: DataStreamに進む前に、Flink SQLを使用して概念実証を行いましょう。  
- **APIを組み合わせる**: 必要に応じて、SQLクエリとDataStreamオペレーターを組み合わせて使用してください。  
- **コネクタを利用する**: Kafka、JDBCなどのプリビルトコネクタを使用して、ボイラープレートコードを回避しましょう。  
- **メトリクスを監視する**: スループット（numRecordsOutPerSecond）とレイテンシをFlinkのREST API経由で追跡します。

## Apache Flinkを今すぐ始めましょう！

リアルタイムデータ処理の可能性を最大限に引き出したいですか？ [Alibaba Clouds Realtime Compute for Apache Flink](https://www.alibabacloud.com/en/product/realtime-compute?_p_lc=1&spm=a2c65.11461447.0.0.2ec0187fbriGmO) をぜひ試してみてください！ Flink SQLとDataStream APIのゲームチェンジ能力を体感してください。複雑なイベント駆動型アプリケーションの構築やリアルタイム分析の実行に関わらず、当社のプラットフォームは開発を簡素化し、スケーラブルに成長するためのツールを提供します。  

[無料トライアル](https://www.alibabacloud.com/en/free?_p_lc=1&product_id=78ef1316-18f57869f0d-1f0d549b416) を利用して、Alibaba Cloud