从零到一:Kafka + Flink + Paimon 实时数据湖搭建实录

真实踩坑经历:从部署到调优,一步步打通实时数据链路

一、写在前面

这篇文章记录了我在三台虚拟机上搭建 Kafka → Flink → Paimon → HDFS 实时数据湖的全过程。从环境部署、数据模拟到最终的流式查询,我把每一步的命令、配置和踩过的坑都记录了下来。

如果你也在搭建类似的实时数据链路,希望这篇文章能帮你少走一些弯路。


二、整体架构

2.1 数据流向

text

复制代码
数据源(Kafka) → Flink实时计算 → Paimon数据湖 → HDFS存储

2.2 组件版本

组件 版本 部署方式
Kafka 3.2.3 三节点分布式
Flink 1.17.0 Standalone集群(三节点)
Paimon 0.9.0 Flink连接器
HDFS 3.1.3 三节点分布式
虚拟机 CentOS 7 hadoop102/103/104

2.3 集群规划

节点 角色
hadoop102 JobManager + TaskManager + NameNode + Kafka
hadoop103 TaskManager + DataNode + Kafka
hadoop104 TaskManager + DataNode + Kafka

三、环境准备

3.1 下载 Paimon 连接器

Paimon 需要下载对应的 Flink 连接器 Jar 包,放到所有节点的 $FLINK_HOME/lib/ 目录下

bash

复制代码
cd /opt/module/flink-1.17.0/lib

# 下载 Paimon Flink 连接器(注意:不是 -bin 版本,那个不存在)
wget https://repo1.maven.org/maven2/org/apache/paimon/paimon-flink-1.17/0.9.0/paimon-flink-1.17-0.9.0.jar

重要提醒 :Paimon 0.9.0 版本已经把 Hadoop 依赖合并到主 Jar 包中,不需要单独下载 paimon-hadoop 包。如果遇到 404 错误,说明这个独立包已经不存在了。

3.2 下载 Kafka 连接器

Flink 读取 Kafka 需要额外的连接器 Jar:

bash

复制代码
cd /opt/module/flink-1.17.0/lib

# Flink Kafka Connector
wget https://repo1.maven.org/maven2/org/apache/flink/flink-connector-kafka/1.17.0/flink-connector-kafka-1.17.0.jar

# Kafka 客户端依赖
wget https://repo1.maven.org/maven2/org/apache/kafka/kafka-clients/3.2.3/kafka-clients-3.2.3.jar

3.3 同步 Jar 到所有节点

关键步骤!如果只放到一个节点,任务调度到其他节点时会报 ClassNotFoundException

bash

复制代码
for host in hadoop103 hadoop104; do
  scp /opt/module/flink-1.17.0/lib/paimon-flink-1.17-0.9.0.jar $host:/opt/module/flink-1.17.0/lib/
  scp /opt/module/flink-1.17.0/lib/flink-connector-kafka-1.17.0.jar $host:/opt/module/flink-1.17.0/lib/
  scp /opt/module/flink-1.17.0/lib/kafka-clients-3.2.3.jar $host:/opt/module/flink-1.17.0/lib/
done

配置文件位于 $FLINK_HOME/conf/flink-conf.yaml。以下是调整后的参数:

yaml

复制代码
# 配置文件路径:/opt/module/flink-1.17.0/conf/flink-conf.yaml

# === 内存配置 ===
jobmanager.memory.process.size: 2048m
taskmanager.memory.process.size: 4096m
taskmanager.memory.managed.size: 1024m

# === 并行度配置 ===
taskmanager.numberOfTaskSlots: 4
parallelism.default: 4

# === Checkpoint 配置(Paimon 写入必须) ===
execution.checkpointing.interval: 10s

# === Hadoop 支持 ===
env.hadoop.conf.dir: /opt/module/hadoop-3.1.3/etc/hadoop

3.5 各节点差异化配置

在 hadoop103 和 hadoop104 上,需要显式指定 taskmanager.host,否则它们会注册到 hadoop102:

yaml

复制代码
# hadoop103 的 flink-conf.yaml 追加
taskmanager.host: hadoop103

# hadoop104 的 flink-conf.yaml 追加
taskmanager.host: hadoop104

3.6 启动集群

bash

复制代码
cd /opt/module/flink-1.17.0
./bin/start-cluster.sh

验证集群状态:

bash

复制代码
# 检查各节点进程
ssh hadoop102 "jps | grep -E 'TaskManager|StandaloneSession'"
ssh hadoop103 "jps | grep TaskManager"
ssh hadoop104 "jps | grep TaskManager"

四、创建 Paimon Catalog

4.1 启动 SQL Client

bash

复制代码
cd /opt/module/flink-1.17.0
./bin/sql-client.sh

4.2 创建 Filesystem Catalog

注意:Catalog 是会话级别的,每次重启 SQL Client 后需要重新创建。

sql

复制代码
-- 创建 Paimon Catalog,元数据存储在 HDFS
CREATE CATALOG paimon_fs WITH (
    'type' = 'paimon',
    'warehouse' = 'hdfs://hadoop102:8020/paimon_warehouse'
);

-- 查看 Catalog 列表
SHOW CATALOGS;

-- 切换到 Paimon Catalog
USE CATALOG paimon_fs;

-- 创建数据库
CREATE DATABASE IF NOT EXISTS test_db;
USE test_db;

关于 Catalog 的存储位置 :这里使用的是 Filesystem Catalog,元数据直接存储在 warehouse 指定的 HDFS 路径下。这种方式不需要额外部署 Hive Metastore,适合快速搭建。


五、建表

5.1 创建 Paimon 结果表

sql

复制代码
CREATE TABLE user_behavior_hdfs (
    user_id BIGINT,
    item_id BIGINT,
    event_type STRING,
    event_time STRING,      -- 注意:保持 STRING,避免时间格式问题
    cnt BIGINT,
    PRIMARY KEY (user_id, item_id) NOT ENFORCED
) WITH (
    'bucket' = '4',
    'changelog-producer' = 'input'
);

关键参数说明

  • bucket = '4':数据分 4 个桶存储,影响并行读写能力

  • changelog-producer = 'input':每条写入都生成变更日志,支持流式读取

为什么要用 STRING 存储时间 :Kafka 中的数据时间格式是 ISO 8601(如 2026-05-17T09:12:06.099),Flink 默认的 TIMESTAMP 解析会报错,所以先保持 STRING,查询时用 TO_TIMESTAMP() 转换。

5.2 创建 Kafka 源表

Kafka 源表需要创建在 default_catalog 中,因为 Paimon Catalog 只支持 Paimon 表,不支持创建 Kafka 连接器表。

sql

复制代码
USE CATALOG default_catalog;
USE default_database;

CREATE TABLE kafka_user_behavior (
    user_id BIGINT,
    item_id BIGINT,
    event_type STRING,
    event_time STRING,
    cnt BIGINT
) WITH (
    'connector' = 'kafka',
    'topic' = 'user_behavior',
    'properties.bootstrap.servers' = 'hadoop102:9092',
    'properties.group.id' = 'paimon_group_final',
    'scan.startup.mode' = 'earliest-offset',
    'format' = 'json',
    'json.ignore-parse-errors' = 'true'
);

scan.startup.mode 参数说明

  • earliest-offset:从 Topic 最早的数据开始消费

  • latest-offset:只消费启动后的新数据(生产环境常用)


六、启动数据模拟(Kafka 生产者)

6.1 Python 生产者脚本

python

复制代码
#!/usr/bin/env python3
# kafka_producer.py

import json
import random
import time
from datetime import datetime
from kafka import KafkaProducer

producer = KafkaProducer(
    bootstrap_servers=['hadoop102:9092'],
    value_serializer=lambda v: json.dumps(v).encode('utf-8')
)

event_types = ['click', 'view', 'purchase', 'like', 'share']

while True:
    data = {
        'user_id': random.randint(1, 1000),
        'item_id': random.randint(1, 500),
        'event_type': random.choice(event_types),
        'event_time': datetime.now().isoformat(timespec='milliseconds'),
        'cnt': 1
    }
    producer.send('user_behavior', value=data)
    time.sleep(0.05)  # 每秒 20 条

6.2 启动生产者

bash

复制代码
pip install kafka-python
python3 kafka_producer.py

6.3 验证 Kafka 数据

bash

复制代码
cd /opt/module/kafka
bin/kafka-console-consumer.sh --bootstrap-server hadoop102:9092 --topic user_behavior --from-beginning --max-messages 5

7.1 提交 INSERT 作业

sql

复制代码
-- 确保在正确的 Catalog 和数据库
USE CATALOG paimon_fs;
USE test_db;

-- 开启 Checkpoint
SET 'execution.checkpointing.interval' = '10s';

-- 提交写入作业
INSERT INTO paimon_fs.test_db.user_behavior_hdfs
SELECT 
    user_id,
    item_id,
    event_type,
    event_time,
    cnt
FROM default_catalog.default_database.kafka_user_behavior;

7.2 查看作业状态

sql

复制代码
SHOW JOBS;

输出示例:

text

复制代码
+----------------------------------+--------------------------------------------------+----------+
|                           job id |                                         job name |   status |
+----------------------------------+--------------------------------------------------+----------+
| 684f6d494e89a3d5628270b683cf3bc6 | insert-into_paimon_fs.test_db.user_behavior_hdfs |  RUNNING |
+----------------------------------+--------------------------------------------------+----------+

7.3 验证数据写入

批处理查询

sql

复制代码
SET 'execution.runtime-mode' = 'batch';
SELECT COUNT(*) FROM user_behavior_hdfs;
流式实时查询

sql

复制代码
SET 'execution.runtime-mode' = 'streaming';
SET 'sql-client.execution.result-mode' = 'tableau';
SELECT * FROM user_behavior_hdfs;

tableau 模式会以表格形式实时滚动显示新数据,每条数据前带有 +I(新增)标识。


八、踩坑记录

坑 1:ClassNotFoundException: KafkaSource

现象

text

复制代码
java.lang.ClassNotFoundException: org.apache.flink.connector.kafka.source.KafkaSource

原因:缺少 Kafka 连接器 Jar 包。

解决 :将 flink-connector-kafkakafka-clients Jar 包放入所有节点的 lib 目录。


坑 2:时间格式解析失败

现象

text

复制代码
DateTimeParseException: Text '2026-05-17T09:12:06.099' could not be parsed

原因 :Kafka 数据时间格式是 ISO 8601(带 T),Flink 默认 TIMESTAMP 不支持。

解决 :建表时 event_time 保持 STRING 类型,查询时用 CAST(event_time AS TIMESTAMP(3)) 转换。


坑 3:ClassNotFoundException: StaticFileStoreSource

现象INSERT 能成功,但 SELECT 时报 ClassNotFoundException: org.apache.paimon.flink.source.StaticFileStoreSource

原因:Paimon Jar 未同步到所有节点,任务调度到没有 Jar 的节点时失败。

解决 :将 paimon-flink Jar 复制到 hadoop103 和 hadoop104 的 lib 目录。


坑 4:流式查询无数据输出

排查步骤

  1. 确认 INSERT 作业在运行:

    sql

    复制代码
    SHOW JOBS;
  2. 确认 Kafka 有数据:

    bash

    复制代码
    kafka-console-consumer.sh --bootstrap-server hadoop102:9092 --topic user_behavior --max-messages 5
  3. 确认 Checkpoint 已开启:

    sql

    复制代码
    SET 'execution.checkpointing.interval' = '10s';

原因:Paimon 的数据提交依赖 Checkpoint。没有 Checkpoint 完成,数据不会写入。


坑 5:Catalog 重启后丢失

现象 :每次重启 SQL Client,paimon_fs Catalog 消失。

原因:Flink 默认 Catalog 存储在内存中,会话结束后丢失。

解决:每次启动 SQL Client 后重新创建 Catalog,或使用初始化脚本:

bash

复制代码
# 创建初始化脚本
cat > /opt/module/flink-1.17.0/conf/init.sql << 'EOF'
CREATE CATALOG IF NOT EXISTS paimon_fs WITH (
    'type' = 'paimon',
    'warehouse' = 'hdfs://hadoop102:8020/paimon_warehouse'
);
USE CATALOG paimon_fs;
EOF

# 启动时加载
./bin/sql-client.sh -i /opt/module/flink-1.17.0/conf/init.sql

-i 参数会在 SQL Client 启动时执行指定的初始化脚本。


坑 6:Schema in filesystem exists

现象:重建表时提示 Schema 冲突。

原因:HDFS 上已有旧表的数据文件残留。

解决:删除 HDFS 上的旧数据后再重建:

bash

复制代码
hdfs dfs -rm -r /paimon_warehouse/test_db.db/user_behavior_hdfs

九、核心命令速查表

操作 命令
启动 Flink 集群 ./bin/start-cluster.sh
停止 Flink 集群 ./bin/stop-cluster.sh
启动 SQL Client ./bin/sql-client.sh
加载初始化脚本 ./bin/sql-client.sh -i init.sql
查看作业状态 SHOW JOBS;
取消作业 CANCEL JOB 'job-id';
删除 HDFS 数据 hdfs dfs -rm -r /path
查看 Paimon 表快照 SELECT * FROM 表名$snapshots;

十、总结

10.1 核心链路

text

复制代码
Kafka生产者 → Kafka Topic → Flink SQL INSERT → Paimon表 → HDFS存储
     ↑              ↑              ↑              ↑          ↑
  Python脚本   user_behavior   Checkpoint     bucket-0    parquet文件

10.2 关键认知

  1. Paimon 写入依赖 Checkpoint:没有 Checkpoint 完成,数据不可见

  2. Catalog 是会话级别 :重启 SQL Client 需要重建(或用 -i 脚本初始化)

  3. Jar 包必须同步到所有 TaskManager 节点:否则任务调度时会报 ClassNotFoundException

  4. 时间格式避免 TIMESTAMP:先用 STRING 存储,查询时转换

  5. 流式查询用 SET 'execution.runtime-mode' = 'streaming' :批处理查询用 batch