真实踩坑经历:从部署到调优,一步步打通实时数据链路
一、写在前面
这篇文章记录了我在三台虚拟机上搭建 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
3.4 Flink 关键配置
配置文件位于 $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
七、启动实时写入(Flink SQL)
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-kafka 和 kafka-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:流式查询无数据输出
排查步骤:
-
确认 INSERT 作业在运行:
sql
SHOW JOBS; -
确认 Kafka 有数据:
bash
kafka-console-consumer.sh --bootstrap-server hadoop102:9092 --topic user_behavior --max-messages 5 -
确认 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 关键认知
-
Paimon 写入依赖 Checkpoint:没有 Checkpoint 完成,数据不可见
-
Catalog 是会话级别 :重启 SQL Client 需要重建(或用
-i脚本初始化) -
Jar 包必须同步到所有 TaskManager 节点:否则任务调度时会报 ClassNotFoundException
-
时间格式避免 TIMESTAMP:先用 STRING 存储,查询时转换
-
流式查询用
SET 'execution.runtime-mode' = 'streaming':批处理查询用batch