iT邦幫忙

2025 iThome 鐵人賽

DAY 21
1
AI & Data

「知其然,更知其所以然:什麼是 Real-time (Streaming) Pipeline?從造輪子到 Flink 與 RisingWave」系列 第 21

【知其然,更知其所以然】Day 21: Flink 核心概念全解析

  • 分享至 

  • xImage
  •  

在上一篇我們理解了 Flink 的設計理念:真正的流式處理。今天我們要深入 Flink 的內部世界,掌握那些在生產環境中必須理解的核心概念。

通過前面構建 SimpleStream 的經驗,我們已經理解了流處理的基本原理。現在讓我們看看 Flink 這個工業級系統是如何解決同樣的問題。

網路上已經有很多關於如何架設 Flink 的文章,這邊不多加著墨。本篇會先介紹 Flink 中重要且對應我們 SimpleStream 的組件與概念,後續幾天會分享筆者在「用 Flink SQL 開發商業邏輯」方面的實務經驗。

JobManager vs TaskManager:誰是大腦,誰是手腳?

還記得我們在 SimpleStream 中構建的 SimpleStreamingEngine 嗎?它負責協調 Source、DataFrame 和 Sink 之間的工作。Flink 將這個概念擴展到分散式環境,形成了 JobManager 和 TaskManager 的架構。

想像 Flink 集群就像一個現代工廠:

         ┌─────────────────────┐
         │     JobManager      │  ◄── Factory Supervisor (Brain)
         │                     │
         │ + Task Schedule     │
         │ + Resource Control  │
         │ + Checkpoint        │
         └─────────────────────┘
                    │
                    │ Manage
                    ▼
┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────────┐
│     TaskManager     │ │     TaskManager     │ │     TaskManager     │
│                     │ │                     │ │                     │
│ + Execute Tasks     │ │ + Execute Tasks     │ │ + Execute Tasks     │
│ + State Management  │ │ + State Management  │ │ + State Management  │
└─────────────────────┘ └─────────────────────┘ └─────────────────────┘

JobManager(工廠總監)的職責

  • 任務調度:決定哪個任務在哪個 TaskManager 上執行
  • 資源管理:監控集群資源使用情況
  • 檢查點協調:統一管理分散式快照
  • 故障恢復:當 TaskManager 掛掉時重新分配任務

TaskManager(生產線工人)的職責

  • 執行任務:實際處理數據的計算邏輯
  • 管理狀態:維護本地的狀態數據
  • 網路通信:與其他 TaskManager 交換數據
  • 資源報告:定期向 JobManager 報告狀態

從 SimpleStreamingEngine 到分散式架構

在 SimpleStream 中,我們的 SimpleStreamingEngine 在單機上完成所有協調工作。Flink 將這個概念分散化:

  • SimpleStreamingEngineJobManager:負責全局協調和決策
  • 處理邏輯執行TaskManager:負責實際的數據處理

DataStream 與 Flink SQL:類似我們的設計

Flink 的 DataStream API 與我們在 SimpleStream 中設計的 SimpleDataFrame 概念相似,都支援鏈式調用和聲明式的數據處理風格。

Flink SQL 更是與我們在 Day 18 探討的「SQL 到流式處理轉換」理念不謀而合 - 讓熟悉 SQL 的工程師能直接用 SQL 語法進行流處理,系統自動轉換為對應的流式算子。

Source & Sink:數據的入口和出口

在 SimpleStream 中,我們實作了基礎的 Source 和 Sink。Flink 將這個概念擴展到工業級:

External Systems     Flink Cluster        External Systems
┌─────────┐    ┌─────────────────────┐    ┌─────────┐
│ Kafka   │--->│Source→Transform→Sink│--->│ Database│
│ Files   │    │                     │    │ Kafka   │
│ APIs    │    │                     │    │ Files   │
└─────────┘    └─────────────────────┘    └─────────┘

Source 的核心能力

  • 並行讀取:自動將數據源拆分成多個並行分區
  • 檢查點集成:記錄讀取進度,支援故障恢復
  • 背壓處理:當下游處理不過來時自動降低讀取速度

Sink 的核心能力

  • 批量寫入:提升寫入效率
  • 故障恢復:支援寫入失敗後的重試機制
  • 格式轉換:支援各種輸出格式

Micro Batch 優化:Flink 的靈活性

雖然 Flink 標榜 True Streaming,但在實際生產環境中,Flink 也提供了 MiniBatch 參數來優化性能。這個概念其實在我們 SimpleStream 的設計中也有體現 - 可以選擇逐筆處理或是累積一定數量後批量處理。

為什麼需要 Micro Batch?

在高吞吐量場景下,逐筆處理可能造成:

  • 頻繁的狀態更新:每筆數據都觸發狀態寫入,IO 開銷大
  • 重複計算開銷:聚合操作需要反覆計算相同的結果
  • 下游壓力:過於頻繁的輸出對下游系統造成負擔

Flink 的 MiniBatch 設定

-- 啟用 MiniBatch 聚合
SET table.exec.mini-batch.enabled = true;
-- 設定批次大小(筆數)
SET table.exec.mini-batch.allow-latency = 1s;
-- 設定最大延遲時間
SET table.exec.mini-batch.size = 1000;

這讓 Flink 能在延遲吞吐量之間找到最佳平衡點,這正是工業級系統需要的彈性。

Backpressure:當下游處理不過來時

想像一條高速公路突然遇到施工:

正常情況:
車流 ───▶ 車流 ───▶ 車流 ───▶ 出口
快速     快速     快速

遇到瓶頸:
車流 ───▶ 車流 ───▶ 堵車 ───▶ 施工區
快速     快速     慢慢      很慢

背壓傳播:
慢行 ───▶ 慢行 ───▶ 堵車 ───▶ 施工區
調節     調節     慢慢      很慢

Flink 的背壓機制

  1. 檢測:TaskManager 監控輸出緩衝區的使用情況
  2. 傳播:當緩衝區接近滿載時,向上游發送背壓信號
  3. 調節:上游接收到信號後降低處理速度
  4. 恢復:當瓶頸解除後,自動恢復正常處理速度

為什麼背壓很重要?

  • 防止 OOM:避免記憶體因數據堆積而耗盡
  • 保持穩定:確保系統不會因為短期瓶頸而崩潰
  • 自動調節:無需人工介入,系統自動適應處理能力

Checkpointing 與 Exactly-once

這是 Flink 最精妙的設計之一
通過 Checkpoint 機制實現,基於 Chandy-Lamport 分散式快照算法:

詳細的 Checkpoint 流程:

1. 觸發階段:
   JobManager 定期(例如每5秒)向所有 Source 節點發送 checkpoint 觸發指令

2. Barrier 注入:
   Source 接收到指令後,在當前數據流中插入一個特殊的 checkpoint barrier
   barrier 包含唯一的 checkpoint-id(如 checkpoint-1001)

3. 快照保存:
   節點收到 barrier 時的行為:
   - 算子狀態保存:將當前算子的所有狀態快照保存到 StateBackend(如 HDFS、S3)
   - 偏移量記錄:記錄外部系統的消費位置(如 Kafka offset)
   - 狀態元數據:記錄狀態的版本、大小、存儲位置等信息
   - 將 barrier 繼續向下游傳播

4. 同步等待:
   下游節點必須等待所有上游的 barrier 都到達後,才能保存自己的狀態
   這確保了全局狀態的一致性

5. 完成確認:
   當所有節點都完成快照保存,JobManager 收到確認後
   此 checkpoint 被標記為成功,可以用於故障恢復

故障恢復過程:
- 檢測到故障 → 停止所有處理
- 從最近成功的 checkpoint 恢復所有節點狀態
- 從保存的 offset 位置重新消費數據
- 重新處理故障點之後的數據

為什麼這樣能保證 Exactly-once?

  • 要麼整個 checkpoint 成功(所有節點狀態+外部系統偏移量都保存)
  • 要麼整個 checkpoint 失敗(回滾到上一個成功點)
  • 不存在部分成功的情況,避免了數據重複或丟失
實際例子(電商訂單統計):
時刻 T1: Kafka offset=100
         算子狀態: {customer_123: count=5, customer_456: count=3}
時刻 T2: Kafka offset=150  ← checkpoint 成功
         算子狀態: {customer_123: count=8, customer_456: count=6, customer_789: count=2}
時刻 T3: Kafka offset=200
         算子狀態: {customer_123: count=12, customer_456: count=8, customer_789: count=4}
時刻 T4: 故障發生

恢復後:
- 恢復 Kafka Consumer 的 offset=150
- 恢復算子狀態: {customer_123: count=8, customer_456: count=6, customer_789: count=2}
- 重新處理 offset 150-200 的數據,繼續累加統計
- 結果:每個客戶的訂單數量恰好被統計一次,無重複無遺漏

StateBackend:狀態存儲方案的選擇策略

Flink 提供三種 StateBackend,對應不同的使用場景:

MemoryStateBackend:
┌─────────────┐
│   JVM Heap  │ ← 狀態存儲在 TaskManager 記憶體
│             │ 
│ State Data  │ 
└─────────────┘
適用:開發測試、小狀態場景
限制:受 JVM 記憶體大小限制

FsStateBackend:  
┌─────────────┐    ┌─────────────┐
│   JVM Heap  │--->│ File System │ ← CheckPoint 存到分散式檔案系統
│             │    │  (HDFS/S3)  │
│ State Data  │    │             │
└─────────────┘    └─────────────┘
適用:中等規模生產環境
特點:平衡性能和可靠性

RocksDBStateBackend:
┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│   JVM Heap  │--->│   RocksDB   │--->│ File System │
│             │    │ (Local SSD) │    │  (HDFS/S3)  │
│ Hot Data    │    │ State Data  │    │ CheckPoints │
└─────────────┘    └─────────────┘    └─────────────┘
適用:大規模生產環境(TB 級狀態)
特點:狀態大小不受記憶體限制

業務功能:強大的處理能力

Join 操作:連接不同的數據流

在實際業務中,我們經常需要關聯不同來源的數據:

Lookup Join:靜態數據關聯(無狀態)

Real-time Stream: User Click Events
     │
     ▼
┌─────────────┐  Instant Query  ┌─────────────┐
│ Click Event │ --------------> │ User Profile│
│user_id: 123 │   (Stateless)   │ (Database)  │
│item_id: 456 │                 │             │
└─────────────┘                 └─────────────┘
     │                                   │
     │ Immediate Response, No Waiting    │
     ▼                                   ▼
┌──────────────────────────────────────────┐
│ Enriched Event: Click + User Profile     │
│ user_id: 123, age: 25, city: "Taipei"    │
│ item_id: 456                             │
└──────────────────────────────────────────┘

特點:
+ 無需儲存流數據狀態
+ Instant query, no waiting delay
+ 記憶體消耗低
- 依賴外部系統可用性
- 查詢延遲影響整體性能

Streaming Join:動態數據關聯(有狀態)

Order Stream    ┌─────────────┐
--------------->│             │
                │   JOIN      │───────────▶ Complete Order Info
Payment Stream  │             │
--------------->│             │
                └─────────────┘

特點:
+ 不依賴外部系統
- 需要大量狀態儲存
- 記憶體消耗高

Streaming Join 的狀態儲存機制

                    Join 算子內部狀態
             ┌─────────────────────────────────┐
Order        │  Left State (Order)             │
Stream ----->│  ┌─────────────────────────┐    │
             │  │ order_id: 123           │    │
             │  │ amount: 100             │    │◄── Wait Payment
             │  │ timestamp: 10:30        │    │
             │  └─────────────────────────┘    │
             │                                 │
Payment      │  Right State (Payment)          │
Stream ----->│  ┌─────────────────────────┐    │
             │  │ order_id: 123           │    │◄── Wait Order
             │  │ status: paid            │    │
             │  │ timestamp: 10:32        │    │
             │  └─────────────────────────┘    │
             └─────────────────────────────────┘
                           │
                  When order_id matches
                           ▼
                  ┌─────────────────┐
                  │  Join Result    │
                  │  order_id: 123  │
                  │  amount: 100    │
                  │  status: paid   │
                  └─────────────────┘

其他 SQL 算子的支援

除了 Join 操作外,Flink 還實現了豐富的算子來滿足各種業務需求:

  • 聚合函數:COUNT、SUM、AVG、MAX、MIN 等標準聚合
  • 窗口函數:ROW_NUMBER、RANK、LAG、LEAD 等分析函數
  • 條件判斷:CASE WHEN、COALESCE、NULLIF 等邏輯控制
  • 集合運算:UNION、INTERSECT、EXCEPT 等集合操作

這些算子都經過流式處理的特殊優化,能在連續數據流上提供與傳統 SQL 一致的語義。每個算子背後都有複雜的狀態管理和性能優化機制,但對開發者來說使用方式與標準 SQL 完全相同,大大降低了流處理開發的門檻。

總結

Flink 的強大在於它的設計哲學:將複雜的分散式流處理包裝在簡潔的 API 之下。

當你在寫一行 SQL 或調用一個算子時,背後實際上是一套精密的分散式系統在運轉。JobManager 在協調全局,TaskManager 在並行處理,Checkpoint 在默默保障一致性。

這種設計讓開發者能專注業務邏輯,而不必糾結於分散式系統的技術細節。這就是工業級框架的價值。

Day 22 預告:Flink SQL 實務開發

理解了 Flink 的核心概念後,下一章我們將進入實務開發階段:如何用 Flink SQL 開發商業邏輯

我們將探討:

  • 大寬表設計模式:如何在流處理中構建高效的數據模型
  • 實時數據倉儲:OLAP 與流處理的結合

從理論學習走向實際應用,讓我們看看如何用這些知識解決真實的業務問題。


上一篇
【知其然,更知其所以然】Day20:Flink 的崛起 - True Streaming Processing
下一篇
【知其然,更知其所以然】Day 22: 大寬表設計與 OLAP 集成
系列文
「知其然,更知其所以然:什麼是 Real-time (Streaming) Pipeline?從造輪子到 Flink 與 RisingWave」26
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言