# Flink 实时数仓实战

0 阅读14分钟

大数据开发前沿技能:Lambda vs Kappa 架构、实时数仓分层设计、Flink SQL 实现、实时 GMV 计算、CDC 数据同步、生产环境案例,从 0 到 1 搭建企业级实时数仓


📌 前言

真实生产问题

问题场景:

某电商公司数据平台遇到的问题:

问题 1:T+1 数据太慢,业务等不起
- 老板早上 9 点要看昨日 GMV,但 ETL 任务 10 点才跑完
- 运营要做促销活动,需要实时监控订单量,但数据滞后 1 天
- 风控要识别刷单行为,需要秒级发现,但离线数仓做不到

问题 2:实时数据不准,不敢用
- 实时 GMV 与离线对不上,差异 5-10%
- 重启任务后,数据重复或丢失
- 乱序数据导致窗口计算结果波动

问题 3:架构复杂,维护成本高
- Lambda 架构:离线 + 实时两套代码,维护成本高
- 数据不一致:离线和实时计算结果对不上
- 开发效率低:一个需求要开发两遍

实时数仓解决:

- 时效性提升:T+1 → 秒级/分钟级
- 数据一致性:统一计算逻辑,离线实时一套代码
- 架构简化:Kappa 架构,只保留实时链路
- 业务价值:支持实时监控、实时风控、实时推荐

优化后效果:

- 数据延迟:1 天 → 3-5 秒
- 数据准确率:90% → 99.99%
- 开发效率:2 套代码 → 1 套代码
- 业务价值:支持双 11 大屏、实时风控、个性化推荐

🏗️ 实时数仓架构深度解析

Lambda 架构 vs Kappa 架构

Lambda 架构(经典但复杂)

                    数据源(Kafka)
                        │
            ┌───────────┴───────────┐
            ↓                       ↓
    ┌───────────────┐       ┌───────────────┐
    │  实时链路     │       │  离线链路     │
    │  Flink/Spark  │       │  Hive/Spark   │
    │  速度快       │       │  数据准       │
    │  可能不准     │       │  速度慢       │
    └───────┬───────┘       └───────┬───────┘
            │                       │
            └───────────┬───────────┘
                        ↓
                ┌───────────────┐
                │   服务层      │
                │  合并查询     │
                └───────────────┘

优点:
✓ 实时和离线互相校验
✓ 离线数据更准确
✓ 实时链路可降级

缺点:
✗ 两套代码,维护成本高
✗ 数据一致性难保证
✗ 开发效率低(一个需求做两遍)

适用场景:
- 对数据准确性要求极高(财务数据)
- 实时链路不稳定,需要离线兜底
- 团队资源充足,能维护两套系统

Kappa 架构(推荐)⭐

                    数据源(Kafka)
                        │
                        ↓
                ┌───────────────┐
                │  Flink 实时   │
                │  统一计算     │
                └───────┬───────┘
                        │
            ┌───────────┼───────────┐
            ↓           ↓           ↓
    ┌───────────┐ ┌───────────┐ ┌───────────┐
    │ 实时数仓  │ │ 数据服务  │ │ 离线导出  │
    │ Doris/    │ │  API/     │ │  Hive/    │
    │ StarRocks │ │  ClickHouse│ │  归档     │
    └───────────┘ └───────────┘ └───────────┘

优点:
✓ 一套代码,维护简单
✓ 数据天然一致
✓ 开发效率高

缺点:
✗ 对实时链路稳定性要求高
✗ 历史数据回溯需要重放

适用场景:
✓ 90% 的实时数仓场景
✓ 团队资源有限
✓ 追求开发效率

对比总结:

特性Lambda 架构Kappa 架构
代码套数2 套1 套
维护成本
数据一致性难保证天然一致
开发效率
系统复杂度
推荐使用⭐⭐⭐⭐⭐⭐⭐⭐

建议:优先使用 Kappa 架构,除非有明确的离线兜底需求。


实时数仓分层设计

实时数仓为什么要分层?

问题:直接把 Kafka 数据写入数据库不行吗?

答案:可以,但会有问题

问题 1:数据质量无法保证
- 源数据有错误(空值、异常值)
- 直接写入,错误传播到下游

问题 2:计算逻辑重复
- 订单 GMV 计算:A 任务算一遍,B 任务又算一遍
- 计算逻辑变更,10 个任务都要改

问题 3:数据不一致
- 同一指标,不同任务计算结果不同
- 老板问:为什么两个报表数据不一样?

解决方案:分层架构
- ODS 层:原始数据,保持原貌
- DWD 层:清洗后的明细数据
- DWS 层:轻度聚合,统一指标
- ADS 层:面向应用的指标

实时数仓分层架构:

数据源(MySQL Binlog / 日志)
        ↓ Kafka
        ↓
┌─────────────────────────────────────────┐
│  ODS 层(实时)                          │
│  - 保持与源数据一致                      │
│  - 表:ods_order_info, ods_user_info    │
│  - 技术:Flink CDC → Kafka → Paimon     │
└─────────────────────────────────────────┘
        ↓ Flink SQL
┌─────────────────────────────────────────┐
│  DWD 层(实时)                          │
│  - 数据清洗、维度关联                    │
│  - 表:dwd_order_info, dwd_user_info    │
│  - 技术:Flink SQL → Kafka → Doris      │
└─────────────────────────────────────────┘
        ↓ Flink SQL
┌─────────────────────────────────────────┐
│  DWS 层(实时)                          │
│  - 轻度聚合,统一指标                    │
│  - 表:dws_gmv_1min, dws_user_order_1d  │
│  - 技术:Flink SQL → Doris/StarRocks    │
└─────────────────────────────────────────┘
        ↓
┌─────────────────────────────────────────┐
│  ADS 层(实时)                          │
│  - 面向具体应用                          │
│  - 表:ads_gmv_screen, ads_realtime_rank│
│  - 技术:Doris/ClickHouse → 大屏/API    │
└─────────────────────────────────────────┘

与离线数仓对比:

特性离线数仓实时数仓
数据延迟小时/天秒/分钟
存储格式ORC/ParquetDoris/StarRocks
计算引擎Hive/SparkFlink
调度方式定时调度持续运行
数据更新批量覆盖流式更新
表类型静态表动态表

🔧 Flink SQL 深度实战

Flink SQL 基础

为什么用 Flink SQL?

方案对比:

方案 1:Flink DataStream API
优点:灵活,可精细控制
缺点:代码复杂,维护成本高
代码量:500+ 行

方案 2:Flink SQL ⭐推荐
优点:简洁,易维护,声明式
缺点:复杂逻辑支持有限
代码量:50+ 行

建议:
- 简单 ETL、聚合:Flink SQL
- 复杂自定义逻辑:DataStream API
- 混合使用:SQL + UDF

环境搭建:

# Flink 配置(flink-conf.yaml)
taskmanager.memory.process.size: 4096m
jobmanager.memory.process.size: 2048m

# 状态后端配置
state.backend: rocksdb
state.checkpoints.dir: hdfs:///flink/checkpoints
state.savepoints.dir: hdfs:///flink/savepoints

# Checkpoint 配置
execution.checkpointing.interval: 60000  # 1 分钟
execution.checkpointing.mode: EXACTLY_ONCE
execution.checkpointing.timeout: 600000
execution.checkpointing.max-concurrent-checkpoints: 1
execution.checkpointing.min-pause: 30000

实时 ODS 层:Flink CDC 同步

需求:将 MySQL 订单表实时同步到 Kafka

MySQL 源表:

-- 源表:MySQL 订单表
CREATE TABLE orders (
    order_id BIGINT PRIMARY KEY,
    user_id BIGINT,
    pay_amount DECIMAL(18,2),
    order_status INT,
    create_time DATETIME,
    update_time DATETIME
);

-- 开启 Binlog
-- my.cnf 配置:
-- server_id=1
-- log_bin=mysql-bin
-- binlog_format=ROW
-- binlog_row_image=FULL

Flink CDC 同步任务:

-- Flink SQL 任务:MySQL → Kafka

-- 1. 创建 MySQL CDC 源表
CREATE TABLE ods_order_info_source (
    order_id BIGINT,
    user_id BIGINT,
    pay_amount DECIMAL(18,2),
    order_status INT,
    create_time TIMESTAMP(3),
    update_time TIMESTAMP(3),
    PRIMARY KEY (order_id) NOT ENFORCED
) WITH (
    'connector' = 'mysql-cdc',
    'hostname' = '192.168.1.100',
    'port' = '3306',
    'username' = 'flink',
    'password' = 'flink123',
    'server-time-zone' = 'Asia/Shanghai',
    'database-name' = 'ecommerce',
    'table-name' = 'orders',
    'scan.startup.mode' = 'initial'  -- initial:全量 + 增量
);

-- 2. 创建 Kafka 输出表
CREATE TABLE ods_order_info_sink (
    order_id BIGINT,
    user_id BIGINT,
    pay_amount DECIMAL(18,2),
    order_status INT,
    create_time TIMESTAMP(3),
    update_time TIMESTAMP(3),
    PRIMARY KEY (order_id) NOT ENFORCED
) WITH (
    'connector' = 'kafka',
    'topic' = 'ods_order_info',
    'properties.bootstrap.servers' = 'kafka1:9092,kafka2:9092,kafka3:9092',
    'properties.transaction.timeout.idle' = '15min',
    'format' = 'cdc-json',  -- CDC 格式,支持 update/delete
    'sink.parallelism' = '4'
);

-- 3. 数据同步
INSERT INTO ods_order_info_sink
SELECT * FROM ods_order_info_source;

提交任务:

# 打包 SQL 任务
cat > sync_order.sql << EOF
-- 上面创建的表和数据同步语句
EOF

# 提交到 Flink 集群
./bin/sql-client.sh -f sync_order.sql

# 或使用 Flink CLI
./bin/flink run -d \
  -c org.apache.flink.table.gateway.cli.SqlGateway \
  sql-gateway.jar \
  -f sync_order.sql

监控任务:

# 查看任务状态
./bin/flink list --running

# 查看 Checkpoint 状态
curl http://flink-jobmanager:8081/flink-ui/api/v1/checkpoints

# 查看任务延迟
curl http://flink-jobmanager:8081/flink-ui/api/v1/jobs/{job_id}/backpressure

实时 DWD 层:数据清洗与维度关联

需求:清洗订单数据,关联用户和商品维度

Flink SQL 任务:

-- 创建 Kafka 源表(ODS 层数据)
CREATE TABLE ods_order_info (
    order_id BIGINT,
    user_id BIGINT,
    pay_amount DECIMAL(18,2),
    order_status INT,
    create_time TIMESTAMP(3),
    update_time TIMESTAMP(3)
) WITH (
    'connector' = 'kafka',
    'topic' = 'ods_order_info',
    'properties.bootstrap.servers' = 'kafka1:9092',
    'format' = 'json',
    'scan.startup.mode' = 'latest-offset'
);

-- 创建维度表(MySQL)
CREATE TABLE dim_user (
    user_id BIGINT,
    user_name STRING,
    gender STRING,
    city STRING,
    member_level STRING
) WITH (
    'connector' = 'jdbc',
    'url' = 'jdbc:mysql://mysql:3306/ecommerce',
    'username' = 'flink',
    'password' = 'flink123',
    'table-name' = 'dim_user',
    'lookup.cache.max-rows' = '10000',  -- 维度缓存
    'lookup.cache.ttl' = '10min'
);

-- 创建 DWD 层输出表(Doris)
CREATE TABLE dwd_order_info (
    order_id BIGINT,
    user_id BIGINT,
    user_name STRING,
    gender STRING,
    city STRING,
    member_level STRING,
    pay_amount DECIMAL(18,2),
    order_status INT,
    status_name STRING,
    create_time TIMESTAMP(3),
    dt STRING,  -- 分区字段
    PRIMARY KEY (order_id) NOT ENFORCED
) WITH (
    'connector' = 'doris',
    'fenodes' = 'doris-fe:8030',
    'username' = 'root',
    'password' = 'doris123',
    'table.identifier' = 'ecommerce.dwd_order_info',
    'sink.enable.batch-mode' = 'true',
    'sink.buffer-flush.interval-ms' = '10000'
);

-- 数据清洗与维度关联
INSERT INTO dwd_order_info
SELECT 
    o.order_id,
    o.user_id,
    u.user_name,
    u.gender,
    u.city,
    u.member_level,
    o.pay_amount,
    o.order_status,
    CASE 
        WHEN o.order_status = 1 THEN '待支付'
        WHEN o.order_status = 2 THEN '已支付'
        WHEN o.order_status = 3 THEN '已完成'
        WHEN o.order_status = 4 THEN '已取消'
        ELSE '未知'
    END AS status_name,
    o.create_time,
    DATE_FORMAT(o.create_time, 'yyyy-MM-dd') AS dt
FROM ods_order_info o
JOIN dim_user FOR SYSTEM_TIME AS OF o.create_time AS u
  ON o.user_id = u.user_id
WHERE o.pay_amount > 0  -- 过滤异常值
  AND o.user_id IS NOT NULL;

关键点解析:

1. 维度关联(Temporal JoinJOIN dim_user FOR SYSTEM_TIME AS OF o.create_time
   含义:关联订单创建时刻的用户维度(支持缓慢变化维)

2. 维度缓存
   'lookup.cache.max-rows' = '10000'
   'lookup.cache.ttl' = '10min'
   作用:减少数据库查询,提高性能

3. 数据清洗
   WHERE o.pay_amount > 0 AND o.user_id IS NOT NULL
   作用:过滤异常数据,保证数据质量

实时 DWS 层:轻度聚合

需求:实时计算每分钟 GMV、订单数

Flink SQL 任务:

-- 创建 DWD 层源表
CREATE TABLE dwd_order_info (
    order_id BIGINT,
    user_id BIGINT,
    city STRING,
    member_level STRING,
    pay_amount DECIMAL(18,2),
    order_status INT,
    create_time TIMESTAMP(3),
    dt STRING,
    WATERMARK FOR create_time AS create_time - INTERVAL '5' SECOND  -- Watermark
) WITH (
    'connector' = 'doris',
    'fenodes' = 'doris-fe:8030',
    'table.identifier' = 'ecommerce.dwd_order_info',
    'scan.startup.mode' = 'latest-offset'
);

-- 创建 DWS 层输出表
CREATE TABLE dws_gmv_1min (
    window_start TIMESTAMP(3),
    window_end TIMESTAMP(3),
    gmv DECIMAL(18,2),
    order_count BIGINT,
    pay_user_count BIGINT,
    avg_order_value DECIMAL(18,2),
    update_time TIMESTAMP(3)
) WITH (
    'connector' = 'doris',
    'fenodes' = 'doris-fe:8030',
    'table.identifier' = 'ecommerce.dws_gmv_1min',
    'sink.enable.batch-mode' = 'true'
);

-- 实时聚合(滚动窗口)
INSERT INTO dws_gmv_1min
SELECT
    TUMBLE_START(create_time, INTERVAL '1' MINUTE) AS window_start,
    TUMBLE_END(create_time, INTERVAL '1' MINUTE) AS window_end,
    SUM(pay_amount) AS gmv,
    COUNT(DISTINCT order_id) AS order_count,
    COUNT(DISTINCT user_id) AS pay_user_count,
    SUM(pay_amount) / COUNT(DISTINCT order_id) AS avg_order_value,
    CURRENT_TIMESTAMP AS update_time
FROM dwd_order_info
WHERE order_status >= 2  -- 已支付订单
GROUP BY TUMBLE(create_time, INTERVAL '1' MINUTE);

Watermark 配置详解:

WATERMARK FOR create_time AS create_time - INTERVAL '5' SECOND

含义:
- 允许数据乱序 5- 当前最大 Event Time10:00:00,则 Watermark = 09:59:55
- 当 Watermark >= 窗口结束时间,触发窗口计算

参数调优:
| 乱序程度 | 延迟设置 | 适用场景 |
|---------|---------|---------|
| 轻微(< 1 秒) | 2-3| 内网传输 |
| 中等(1-5 秒) | 5-10| 公网传输 |
| 严重(> 5 秒) | 10-30| 跨地域 |

迟到数据处理:
-- 允许迟到 10 秒(Flink DataStream API)
.window(TumblingEventTimeWindows.of(Time.minutes(1)))
.allowedLateness(Duration.ofSeconds(10))

实时 ADS 层:应用指标

需求:实时大屏 GMV、订单排行

Flink SQL 任务:

-- 创建 ADS 层输出表
CREATE TABLE ads_gmv_screen (
    stat_time STRING,
    gmv_total DECIMAL(18,2),
    gmv_today DECIMAL(18,2),
    order_count_today BIGINT,
    pay_user_count_today BIGINT,
    top1_city STRING,
    top1_city_gmv DECIMAL(18,2),
    update_time TIMESTAMP(3)
) WITH (
    'connector' = 'doris',
    'fenodes' = 'doris-fe:8030',
    'table.identifier' = 'ecommerce.ads_gmv_screen',
    'sink.enable.batch-mode' = 'true'
);

-- 实时大屏指标
INSERT INTO ads_gmv_screen
SELECT
    DATE_FORMAT(CURRENT_TIMESTAMP, 'yyyy-MM-dd HH:mm:ss') AS stat_time,
    (SELECT SUM(gmv) FROM dws_gmv_1min) AS gmv_total,
    (SELECT SUM(gmv) FROM dws_gmv_1min 
     WHERE DATE_FORMAT(window_start, 'yyyy-MM-dd') = DATE_FORMAT(CURRENT_TIMESTAMP, 'yyyy-MM-dd')
    ) AS gmv_today,
    (SELECT SUM(order_count) FROM dws_gmv_1min 
     WHERE DATE_FORMAT(window_start, 'yyyy-MM-dd') = DATE_FORMAT(CURRENT_TIMESTAMP, 'yyyy-MM-dd')
    ) AS order_count_today,
    (SELECT SUM(pay_user_count) FROM dws_gmv_1min 
     WHERE DATE_FORMAT(window_start, 'yyyy-MM-dd') = DATE_FORMAT(CURRENT_TIMESTAMP, 'yyyy-MM-dd')
    ) AS pay_user_count_today,
    (SELECT city FROM dwd_order_info 
     WHERE DATE_FORMAT(create_time, 'yyyy-MM-dd') = DATE_FORMAT(CURRENT_TIMESTAMP, 'yyyy-MM-dd')
     GROUP BY city ORDER BY SUM(pay_amount) DESC LIMIT 1
    ) AS top1_city,
    (SELECT SUM(pay_amount) FROM dwd_order_info 
     WHERE DATE_FORMAT(create_time, 'yyyy-MM-dd') = DATE_FORMAT(CURRENT_TIMESTAMP, 'yyyy-MM-dd')
     GROUP BY city ORDER BY SUM(pay_amount) DESC LIMIT 1
    ) AS top1_city_gmv,
    CURRENT_TIMESTAMP AS update_time
FROM dws_gmv_1min;

🏭 生产环境完整案例

案例背景

公司规模:
- 日均订单:500 万单
- 峰值 QPS:10 万+/秒
- 数据源:MySQL(订单/用户/商品)+ 日志(点击/浏览)

需求:
1. 实时 GMV 大屏(秒级更新)
2. 实时订单排行(分钟级更新)
3. 实时用户画像(小时级更新)
4. 实时风控(秒级预警)

技术选型:
- 计算引擎:Flink 1.17
- 消息队列:Kafka 3.0
- 实时数仓:Doris 2.0
- 数据存储:Paimon(数据湖)

架构设计

数据流向:

MySQL Binlog ──→ Flink CDC ──→ Kafka (ODS)
                                      ↓
日志文件  ─────→ Flink Source ──→ Kafka (ODS)
                                      ↓
                              Flink SQL (DWD)
                                      ↓
                              Doris (DWD 层)
                                      ↓
                              Flink SQL (DWS)
                                      ↓
                              Doris (DWS 层)
                                      ↓
                              Doris (ADS 层)
                                      ↓
                              大屏/API/报表

表结构设计

ODS 层(Kafka):

// Topic: ods_order_info
{
    "order_id": 1001,
    "user_id": 5001,
    "pay_amount": 100.50,
    "order_status": 2,
    "create_time": "2026-03-24T10:30:00",
    "op_type": "INSERT"  // CDC 操作类型
}

DWD 层(Doris):

CREATE TABLE dwd_order_info (
    order_id BIGINT,
    user_id BIGINT,
    user_name STRING,
    gender STRING,
    city STRING,
    member_level STRING,
    pay_amount DECIMAL(18,2),
    order_status INT,
    status_name STRING,
    create_time TIMESTAMP,
    dt STRING
)
DUPLICATE KEY (order_id)
PARTITION BY RANGE(dt) ()
DISTRIBUTED BY HASH(order_id) BUCKETS 32
PROPERTIES (
    "replication_num" = "3",
    "light_schema_change" = "true"
);

DWS 层(Doris):

CREATE TABLE dws_gmv_1min (
    window_start TIMESTAMP,
    window_end TIMESTAMP,
    gmv DECIMAL(18,2),
    order_count BIGINT,
    pay_user_count BIGINT,
    avg_order_value DECIMAL(18,2),
    update_time TIMESTAMP
)
DUPLICATE KEY (window_start)
PARTITION BY RANGE(window_start) ()
DISTRIBUTED BY HASH(window_start) BUCKETS 10
PROPERTIES (
    "replication_num" = "3",
    "aggregation_policy" = "AUTO"
);

性能优化

1. Checkpoint 优化

# Flink Checkpoint 配置
execution.checkpointing.interval: 60000  # 1 分钟
execution.checkpointing.mode: EXACTLY_ONCE
execution.checkpointing.timeout: 600000  # 10 分钟
execution.checkpointing.max-concurrent-checkpoints: 1
execution.checkpointing.min-pause: 30000  # 30 秒

# RocksDB 状态后端
state.backend: rocksdb
state.backend.rocksdb.memory.managed: true
state.backend.rocksdb.memory.high-prio-pool-ratio: 0.1

2. 并行度优化

-- 设置任务并行度
SET parallelism.default = 4;

-- 单个算子并行度
INSERT INTO dwd_order_info
SELECT /*+ OPTIONS('parallelism'='8') */ * FROM ods_order_info;

3. 状态优化

-- 状态 TTL(自动清理过期状态)
SELECT * FROM dwd_order_info
/*+ OPTIONS('state.ttl'='1d') */;

-- 状态后端配置
state.backend.rocksdb.memory.fixed-per-slot: 256m

⚠️ 常见坑点与解决方案

坑点 1:数据重复

问题:

任务重启后,数据重复计算
GMV 从 100 万变成 120 万,多了 20 万

原因:

- Checkpoint 未完成,Source 重放数据
- Sink 未实现幂等写入
- 窗口重复触发

解决:

-- 方案 1:使用主键表(Doris/StarRocks)
CREATE TABLE dwd_order_info (
    order_id BIGINT,
    ...
    PRIMARY KEY (order_id) NOT ENFORCED
) WITH (...);

-- 方案 2:Sink 端 UPSERT
-- Doris Connector 自动实现幂等写入

-- 方案 3:去重窗口
SELECT 
    window_start,
    COUNT(DISTINCT order_id) AS order_count  -- 去重计数
FROM dwd_order_info
GROUP BY TUMBLE(create_time, INTERVAL '1' MINUTE);

坑点 2:数据延迟

问题:

数据延迟严重,大屏显示滞后 10 分钟

原因:

- 数据倾斜:某个 Key 数据量过大
- Checkpoint 超时:状态太大,Checkpoint 时间过长
- 背压:下游处理慢,上游堆积

解决:

-- 方案 1:数据倾斜处理(加盐)
SELECT 
    CONCAT(order_id, '_', FLOOR(RAND() * 10)) AS salted_key,
    SUM(pay_amount) AS partial_gmv
FROM dwd_order_info
GROUP BY CONCAT(order_id, '_', FLOOR(RAND() * 10));

-- 方案 2:优化 Checkpoint
execution.checkpointing.interval: 30000  -- 缩短间隔
state.backend.rocksdb.memory.managed: true

-- 方案 3:监控背压
curl http://flink-jobmanager:8081/flink-ui/api/v1/jobs/{job_id}/backpressure

坑点 3:乱序数据

问题:

窗口计算结果忽高忽低,不稳定

原因:

- 数据乱序到达
- Watermark 配置不合理
- 窗口触发过早

解决:

-- 方案 1:调整 Watermark
WATERMARK FOR create_time AS create_time - INTERVAL '10' SECOND

-- 方案 2:允许迟到
-- Flink DataStream API
.window(TumblingEventTimeWindows.of(Time.minutes(1)))
.allowedLateness(Duration.ofSeconds(10))

-- 方案 3:侧输出迟到数据
final OutputTag<Order> lateTag = new OutputTag<Order>("late-data") {};
dataStream
    .window(TumblingEventTimeWindows.of(Time.minutes(1)))
    .allowedLateness(Duration.ofSeconds(10))
    .sideOutputLateData(lateTag);

📋 最佳实践清单

架构设计

  • 优先使用 Kappa 架构(一套代码)
  • 实时数仓分层(ODS/DWD/DWS/ADS)
  • 统一指标口径(只在一个地方计算)
  • 预留离线导出接口

开发规范

  • 使用 Flink SQL(简洁易维护)
  • 合理设置 Watermark(根据乱序程度)
  • 开启 Checkpoint(Exactly-Once)
  • 实现幂等写入(主键表/UPSERT)

性能优化

  • 合理设置并行度(根据数据量)
  • 使用 RocksDB 状态后端(大状态)
  • 开启维度缓存(Lookup Join)
  • 监控背压和延迟

监控告警

  • 监控任务状态(Running/Failed)
  • 监控 Checkpoint 延迟
  • 监控数据延迟(Source → Sink)
  • 监控数据质量(对账)

📌 总结

核心要点

概念要点推荐使用
架构选择Lambda vs KappaKappa⭐⭐⭐⭐⭐
计算引擎Flink SQL vs DataStreamSQL⭐⭐⭐⭐⭐
状态后端Memory vs RocksDBRocksDB(大状态)
存储引擎Doris/StarRocksDoris⭐⭐⭐⭐⭐

实践原则

1. 简单优先
   Flink SQL 能解决的不用 DataStream

2. Exactly-Once
   开启 Checkpoint,保证数据不重不漏

3. 监控完善
   任务状态、数据延迟、数据质量

4. 持续优化
   根据生产数据调整参数

下一步

完成实时数仓后:
1. 接入 BI 工具(实时大屏)
2. 开发实时 API(数据服务)
3. 建立数据质量监控(对账、告警)
4. 优化性能(并行度、Checkpoint)

💡 实时数仓是大数据发展的趋势,建议尽早掌握 Flink 技术!


👋 感谢阅读!


🔗 系列文章

  • [01-SQL 窗口函数从入门到精通](./01-SQL 窗口函数从入门到精通.md)
  • [02-Spark 性能优化 10 个技巧](./02-Spark 性能优化 10 个技巧.md)
  • 03-数据仓库分层设计指南
  • 04-维度建模实战
  • 05-Flink 实时数仓实战(本文)
  • [下一篇:Kafka 消息队列实战指南](./06-Kafka 消息队列实战指南.md)