經過前 21 天對流處理核心概念的深入理解,今天我們要進入一個全新的實務領域:如何用流處理技術支撐複雜的業務分析需求。
當你的老闆要求「我想看到每個客戶的訂單金額趨勢,按照付款方式和配送地區分組」時,你會發現傳統的 OLTP 架構開始力不從心。這正是大寬表設計與 OLAP 引擎組合登場的時刻。
在電商系統中,我們習慣用正規化的方式設計資料表:
傳統 OLTP 多表設計:
┌─────────────┐ ┌─────────────────┐ ┌─────────────┐
│ orders │ │ order_details │ │ customers │
├─────────────┤ ├─────────────────┤ ├─────────────┤
│ order_id │ │ order_id │ │ customer_id │
│ customer_id │ │ product_id │ │ name │
│ order_date │ │ quantity │ │ city │
│ status │ │ price │ │ age │
└─────────────┘ └─────────────────┘ └─────────────┘
┌─────────────┐ ┌─────────────────┐
│ payments │ │ deliveries │
├─────────────┤ ├─────────────────┤
│ order_id │ │ order_id │
│ amount │ │ address │
│ method │ │ delivery_date │
│ paid_at │ │ status │
└─────────────┘ └─────────────────┘
當業務分析需求來臨時:
-- 想要分析:客戶的訂單金額趨勢,按付款方式和地區分組
SELECT
c.city,
p.method,
DATE_TRUNC('month', o.order_date) as month,
SUM(p.amount) as total_amount
FROM orders o
JOIN customers c ON o.customer_id = c.customer_id
JOIN payments p ON o.order_id = p.order_id
JOIN deliveries d ON o.order_id = d.order_id
WHERE o.order_date >= '2024-01-01'
GROUP BY c.city, p.method, DATE_TRUNC('month', o.order_date);
問題就來了:
核心思想:用空間換時間,將 JOIN 操作前移到數據寫入階段。
Wide Table Processing Pipeline:
OLTP Systems:
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ orders │ │ details │ │customers │ │ payments │ │deliveries│
└──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘
│ │ │ │ │
│ CDC │ CDC │ CDC │ CDC │ CDC
Kafka▼ ▼ ▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ orders │ │ details │ │customers │ │ payments │ │deliveries│
└──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘
│ │ │ │ │
└────────────┼────────────┼────────────┼────────────┘
│ │ │
└────────────┼────────────┘
│
▼
┌─────────────────────┐
│ Flink SQL │
│ Stream JOIN │
└─────────────────────┘
│
▼
┌─────────────────────┐
│ OLAP │
└─────────────────────┘
│
▼
┌─────────────────────┐
│ API │
└─────────────────────┘
│
▼
┌─────────────────────┐
│ Dashboard │
└─────────────────────┘
查詢變得極簡:
-- 同樣的業務需求,只需單表查詢
SELECT
customer_city,
payment_method,
DATE_TRUNC('month', order_date) as month,
SUM(payment_amount) as total_amount
FROM order_wide_table
WHERE order_date >= '2024-01-01'
GROUP BY customer_city, payment_method, DATE_TRUNC('month', order_date);
性能提升顯著:
重要提醒
本文所有程式碼皆為教學與概念演示用的 Flink SQL,可以幫助你理解並解決實際場景的問題,但這裡的程式碼需要根據實際環境調整,主要目的是用來講解大寬表構建的設計思路與核心概念。
假設我們要構建一個電商平台的實時分析系統,需要整合以下業務實體:
-- 訂單表
CREATE TABLE orders (
...
) WITH (
'connector' = 'kafka',
'topic' = 'orders'
);
-- 客戶表
CREATE TABLE customers (
...
) WITH (
'connector' = 'kafka',
'topic' = 'customers'
);
-- 訂單明細表
CREATE TABLE order_details (
...
) WITH (
'connector' = 'kafka',
'topic' = 'order_details'
);
-- 付款表
CREATE TABLE payments (
...
) WITH (
'connector' = 'kafka',
'topic' = 'payments'
);
-- 配送表
CREATE TABLE deliveries (
...
) WITH (
'connector' = 'kafka',
'topic' = 'deliveries'
);
CREATE TABLE order_wide_table (
...
) WITH (
'connector' = 'jdbc',
'url' = 'jdbc:mysql://localhost:3306/analytics',
'table-name' = 'order_wide_table'
);
一次性 JOIN 所有表:
INSERT INTO order_wide_table
SELECT
...
FROM orders o
LEFT JOIN customers c ON o.customer_id = c.customer_id
LEFT JOIN order_details od ON o.order_id = od.order_id
LEFT JOIN payments p ON o.order_id = p.order_id
LEFT JOIN deliveries d ON o.order_id = d.order_id;
商業開發的核心:其實 Flink SQL 大寬表開發就這麼簡單 - 定義好各個 Kafka 源表,然後用一個 INSERT 語句 JOIN 所有相關表,最後輸出到 JDBC 目標表。核心邏輯非常直觀,這就是現代流處理框架的威力。
通過今天的探討,我們理解了為什麼現代數據架構選擇「大寬表 + OLAP」這個模式:
解決了什麼問題:
核心設計思想:
這種架構已經成為大型電商、金融、物聯網等行業的標準解決方案。當你的分析查詢開始變慢時,大寬表設計往往是最直接有效的解決方案。
基礎的大寬表只是開始!在實際的商業場景中,有些需求會更進階:
從基礎 JOIN 到進階運算,讓我們看看 Flink 如何支撐進一步的大規模數據處理!