目录

1.环境与背景

2. 核心依赖检查 (至关重要)

3.数据准备

4. 方案一:基于 CTAS 的全量复制(推荐中小表)

4.1 启动Flink 会话

4.2 定义 Catalog

4.3 执行迁移

5.1 解决 HDFS 权限问题(关键步骤)

5.2 执行原地转换

5.3 物理搬家与注册(解决跨库重命名难题)

6.1 步骤1:解决 HDFS 权限问题 (Sticky Bit)

6.2 步骤2:启动 Flink SQL Client

6.3 步骤 3:注册 Catalog

6.4 步骤 4:执行迁移命令 (Paimon 1.1.1 语法)

6.5 步骤 5:验证数据

7.常见报错与排查手册 (Troubleshooting)

7.1 签名不匹配 (Signature mismatch)

7.2 权限拒绝 (Permission denied by sticky bit)

7.3 未知分区格式 (Unknown partition format / PaimonSerDe)

7.4 路径跨库错误 (Path ... is different from where it should be)

7.5 目标库无表 (Success but table missing)

7.6 文件系统存在 Schema (Schema in filesystem exists)

8.总结与最佳实践

8.1 迁移策略选择

8.2 成功的三个关键支柱


1.环境与背景

现目前是调研批流一体数仓架构,整体的流程图如下:

之前使用的离线数仓架构,需要对架构进行调整,因此原有的Hive表需要迁移到新架构下的Paimon表中。本文档是基于调研的架构进行测试的,特别是解决Jar包版本冲突HDFS权限限制以及元数据污染这三大核心障碍后的最终总结。调研在 CDH 6.3.2 环境下,使用 Flink 1.19Paimon 1.1.1 实现 Hive 到 Paimon 零拷贝(Zero-Copy)秒级迁移 的完整最佳实践。

  • Hadoop 平台:CDH 6.3.2 (Hadoop 3.0.0, Hive 2.1.1)

  • 计算引擎:Flink 1.19.3 (Session Cluster)

  • 数据湖组件:Apache Paimon 1.1.1

  • 迁移目标

    • 源表:Hive test.test_table (ORC 格式)

    • 目标表:Paimon test_db.test_table_*

    • 要求:保留历史数据,无需重写文件(零拷贝),支持重命名

2. 核心依赖检查 (至关重要)

在开始任何操作前,必须检查 Flink 的 lib 目录,防止“Jar包地狱”导致的功能缺失。

1. 检查 Jar 包冲突 进入 Flink 安装目录的 lib 文件夹,执行:

ls -l $FLINK_HOME/lib/paimon*.jar
# 若查找不到,则需要搭建Flink的实际安装路径
ls -l /home/bigdata/download/flink-1.19.3/lib/paimon*.jar

2. 正确的依赖状态

  • 必须删除paimon-flink-1.19-0.9.0.jar (或任何低于 1.1.1 的版本,会导致无法使用 target_table 参数)。

  • 必须保留paimon-flink-1.19-1.1.1.jar (核心包)。

  • 必须保留paimon-hive-connector-2.1-cdh-6.3-1.1.1.jar (CDH 适配包)。

3. 重启集群 如果有 Jar 包变更,必须重启 Flink 集群(Standalone 或 YARN Session)以及 SQL Client,否则内存中的旧代码不会更新。

3.数据准备

为了模拟真实的迁移场景,我们在 Hive 中创建标准的 ORC 表。

Hive SQL (在 Beeline 或 Hue 中执行):

在CDH 6.3.2集群中创建Hive的数据库、表以及插入测试数据,相应的SQL语句如下:

CREATE DATABASE IF NOT EXISTS test;
​
USE test;
​
DROP TABLE IF EXISTS test_table;
​
-- 创建源表 (必须是 Hive 支持的格式,如 ORC, Parquet, Avro)
CREATE TABLE test.test_table (
    id          INT,
    name        STRING,
    age         INT,
    city        STRING,
    salary      DECIMAL(10,2),
    create_time TIMESTAMP
)
STORED AS ORC;
​
INSERT INTO test.test_table VALUES
( 1,'Alice',23,'Beijing',18000.00,'2024-11-20 08:00:00'),
( 2,'Bob',30,'Shanghai',22000.50,'2024-11-20 08:01:00'),
( 3,'Carol',25,'Guangzhou',19500.00,'2024-11-20 08:02:00'),
( 4,'David',28,'Shenzhen',21000.00,'2024-11-20 08:03:00'),
( 5,'Eva',22,'Chengdu',17000.00,'2024-11-20 08:04:00'),
( 6,'Frank',35,'Hangzhou',25000.00,'2024-11-20 08:05:00'),
( 7,'Grace',27,'Wuhan',20000.00,'2024-11-20 08:06:00'),
( 8,'Henry',29,'Nanjing',20500.00,'2024-11-20 08:07:00'),
( 9,'Iris',24,'Xi''an',18500.00,'2024-11-20 08:08:00'),
(10,'Jack',31,'Chongqing',23000.00,'2024-11-20 08:09:00'),
(11,'Kate',26,'Tianjin',19800.00,'2024-11-20 08:10:00'),
(12,'Leo',33,'Suzhou',24000.00,'2024-11-20 08:11:00'),
(13,'Mona',21,'Qingdao',17500.00,'2024-11-20 08:12:00'),
(14,'Nick',34,'Dalian',24500.00,'2024-11-20 08:13:00'),
(15,'Olivia',28,'Xiamen',21500.00,'2024-11-20 08:14:00'),
(16,'Paul',36,'Ningbo',25500.00,'2024-11-20 08:15:00'),
(17,'Quinn',23,'Foshan',18200.00,'2024-11-20 08:16:00'),
(18,'Rose',30,'Wuxi',22500.00,'2024-11-20 08:17:00'),
(19,'Sam',27,'Changsha',20200.00,'2024-11-20 08:18:00'),
(20,'Tina',32,'Zhengzhou',23500.00,'2024-11-20 08:19:00'),
(21,'Uma',25,'Dongguan',19000.00,'2024-11-20 08:20:00'),
(22,'Victor',29,'Jinan',20800.00,'2024-11-20 08:21:00'),
(23,'Wendy',24,'Harbin',18700.00,'2024-11-20 08:22:00'),
(24,'Xander',37,'Kunming',26000.00,'2024-11-20 08:23:00'),
(25,'Yara',26,'Nanchang',19300.00,'2024-11-20 08:24:00'),
(26,'Zane',31,'Guiyang',22800.00,'2024-11-20 08:25:00'),
(27,'Abby',22,'Haikou',17800.00,'2024-11-20 08:26:00'),
(28,'Bryan',35,'Lanzhou',25200.00,'2024-11-20 08:27:00'),
(29,'Cora',28,'Taiyuan',21200.00,'2024-11-20 08:28:00'),
(30,'Derek',33,'Hefei',24200.00,'2024-11-20 08:29:00');
​
SELECT COUNT(*) AS cnt FROM test_db.test_table;
SELECT * FROM test_db.test_table LIMIT 10;
​
# copy表方案一:存储格式orc
CREATE TABLE test.test_table_copy
LIKE test.test_table; 
​
INSERT INTO test.test_table_copy
SELECT * FROM test.test_table;
​
# copy表方案二:存储格式TEXTFILE
# 这里我使用的这种方式进行copy表
CREATE TABLE test.test_table_copy
AS
SELECT * FROM test.test_table;

4. 方案一:基于 CTAS 的全量复制(推荐中小表)

适用场景:数据量在可接受范围内(如 <几百GB),追求稳定,无需修改 HDFS 底层权限。 原理:通过 Flink 读取 Hive 源表数据,写入新的 Paimon 表。

4.1 启动Flink 会话

在Flink的bin目录下执行下述语句,进入Flink SQL会话

./sql-client.sh

4.2 定义 Catalog

在 Flink SQL Client 中执行:

-- 1. 定义 Source Catalog (读取 Hive)
CREATE CATALOG source_hive WITH (
    'type' = 'hive',
    'hive-conf-dir' = '/etc/hive/conf.cloudera.hive',  -- CDH 标准配置路径
    'hadoop-conf-dir' = '/etc/hadoop/conf.cloudera.hdfs' -- CDH 标准配置路径
);
​
-- 2. 定义 Target Catalog (写入 Paimon)
CREATE CATALOG paimon_catalog WITH (
  'type'        = 'paimon',
  'warehouse'   = 'hdfs:///user/hive/warehouse',
  'metastore'   = 'hive',
  'hive-conf-dir' = '/etc/hive/conf.cloudera.hive'
);

4.3 执行迁移

-- 确保当前使用的是 Paimon Catalog
USE CATALOG paimon_catalog;
USE test_db;
​
-- 创建并写入数据
-- 这会启动一个 Flink Job,读取 Hive 数据并以 ORC 格式写入 Paimon
CREATE TABLE test_table2 
WITH (
    'file.format' = 'orc',
    'bucket' = '-1' -- 如果是 Paimon 0.6+ 建议加上这个,表示不分桶的追加表,写入更快
)
AS SELECT * FROM source_hive.test.test_table;

结果验证: 执行完成后,Paimon 会自动在 test_db 下创建 test_table2 并完成数据加载。该操作本质是 MapReduce/Spark/Flink 任务的数据重写。对应结果如下:

集群上查看表结构,存储格式也是为ORC:

适用场景:TB/PB 级数据,无法承受重写数据的时间成本,必须“原地转换”。 原理:利用 sys.migrate_table 修改元数据,并配合 HDFS 操作完成库的变更。

jar包下载网址:Central Repository: org/apache/paimon/paimon-flink-1.19/0.9.0

5.1 解决 HDFS 权限问题(关键步骤)

由于 Paimon 需要移动文件,而 CDH 默认开启 Sticky Bit,普通用户无法移动 root 创建的 Hive 文件。 在 Linux 终端执行(需 sudo 或 hdfs 用户):

# 修改源表文件的所有者为运行 Flink 的用户 (例如 bigdata)
sudo -u hdfs hdfs dfs -chown -R bigdata:hive /user/hive/warehouse/test.db/test_table

# 验证属主是否正确
hdfs dfs -ls -R /user/hive/warehouse/test.db/test_table

5.2 执行原地转换

在 Flink SQL Client 中执行:

-- 定义 Target Catalog (写入 Paimon)
CREATE CATALOG paimon_catalog WITH (
  'type'        = 'paimon',
  'warehouse'   = 'hdfs:///user/hive/warehouse',
  'metastore'   = 'hive',
  'hive-conf-dir' = '/etc/hive/conf.cloudera.hive'
);
​
USE CATALOG paimon_catalog;
USE test_db;
​
-- 调用迁移过程 (注:因版本限制,无法指定 target_table,默认原地转换)
CALL sys.migrate_table(
    connector => 'hive',
    source_table => 'test.test_table',
    options => 'file.format=orc'
);

5.3 物理搬家与注册(解决跨库重命名难题)

由于 migrate_table 默认保留在源库 (test),且 Hive Catalog 对路径有严格校验,我们需要手动将数据移动到 test_db 的标准路径下并重新挂载。

步骤 1:HDFS 文件移动(Linux Shell)

# 1. 创建目标库目录
hdfs dfs -mkdir -p /user/hive/warehouse/test_db.db

# 2. 将数据从 test库 移动到 test_db库,并重命名
hdfs dfs -mv /user/hive/warehouse/test.db/test_table /user/hive/warehouse/test_db.db/test_table4

# 3. (Hack操作) 暂时将数据重命名,骗过 Create Table 检测
hdfs dfs -mv /user/hive/warehouse/test_db.db/test_table4 /user/hive/warehouse/test_db.db/test_table4_bak

步骤 2:创建表结构(Flink SQL)

USE test_db;
​
-- 创建表元数据(此时路径为空,创建成功)
CREATE TABLE test_table4 (
    id INT,
    name STRING,
    age INT,
    city STRING,
    salary DECIMAL(10,2),
    create_time TIMESTAMP(3)
) WITH (
    'connector' = 'paimon',
    'file.format' = 'orc'
);

步骤 3:恢复数据(Linux Shell)

# 删除 Flink 刚创建的空目录
hdfs dfs -rm -r /user/hive/warehouse/test_db.db/test_table4

# 将真实数据移回原位
hdfs dfs -mv /user/hive/warehouse/test_db.db/test_table4_bak /user/hive/warehouse/test_db.db/test_table4

步骤 4:验证

SELECT * FROM test_table4 LIMIT 5;

使用dbeaver查看数据结果如下:

这是最高效的方案,利用 Paimon 1.1.1 的 migrate_table 过程,仅修改元数据,不移动也不重写 HDFS 上的物理数据文件。但是使用这种方式进行迁移,会将Hive源表给删除,因此在执行的之前需要将表进行备份。

jar包下载网址:Central Repository: org/apache/paimon/paimon-flink-1.19/1.1.1

6.1 步骤1:解决 HDFS 权限问题 (Sticky Bit)

Hive 表的数据通常由 roothive 用户创建,且 CDH 默认开启粘滞位(Sticky Bit)。Flink 运行用户(如 bigdata)默认无权操作这些文件。

在 Linux 终端执行(需 root 或 sudo 权限):

# 将源表 HDFS 文件的所有者修改为 Flink 运行用户 (例如 bigdata)
# 注意:替换路径为实际 Hive 表路径
sudo -u hdfs hdfs dfs -chown -R bigdata:hive /user/hive/warehouse/test.db/test_table

# 验证属主是否正确
hdfs dfs -ls -R /user/hive/warehouse/test.db/test_table

6.2 步骤2:启动 Flink SQL Client

export HADOOP_CLASSPATH=`hadoop classpath`
./bin/sql-client.sh

6.3 步骤 3:注册 Catalog

-- 定义 Target Catalog (写入 Paimon)
CREATE CATALOG paimon_catalog WITH (
  'type'        = 'paimon',
  'warehouse'   = 'hdfs:///user/hive/warehouse',
  'metastore'   = 'hive',
  'hive-conf-dir' = '/etc/hive/conf.cloudera.hive'
);
​
USE CATALOG paimon_catalog;

6.4 步骤 4:执行迁移命令 (Paimon 1.1.1 语法)

使用 target_table 参数实现跨库重命名迁移。

CALL sys.migrate_table(
    connector    => 'hive',
    source_table => 'test.test_table',       -- 源表
    target_table => 'test_db.test_table_3',  -- 目标表 (1.1.1 新特性)
    delete_origin => false,                  -- 保留源表的元数据(可选)
    options      => 'file.format=orc, bucket=-1' -- bucket=-1 表示追加表,性能最好
);

执行结果: 应当返回 Success

6.5 步骤 5:验证数据

SELECT * FROM test_db.test_table_3;

对应结果如下:

使用dbeaver查看数据结果如下:

7.常见报错与排查手册 (Troubleshooting)

在本次 CDH 6.3.2 + Flink 1.19 + Paimon 1.1.1 的迁移实战中,踩过了以下典型坑点。遇到报错时,可按此清单逐一排查。

7.1 签名不匹配 (Signature mismatch)

  • 报错信息

    [ERROR] Could not execute SQL statement. Reason:
    org.apache.calcite.sql.validate.SqlValidatorException: No match found for function signature migrate_table(connector => <CHARACTER>, source_table => <CHARACTER>, target_table CTER>, delete_origin => <BOOLEAN>, options => <CHARACTER>).
    Supported signatures are:
    migrate_table(connector => STRING, source_table => STRING, options => STRING)

  • 根本原因Jar 包版本冲突(Jar Hell)。 Flink 的 lib 目录下同时存在旧版本(如 0.9.0)和新版本(1.1.1)的 Paimon Jar 包。Flink 类加载器优先加载了旧版 Jar,而旧版 migrate_table 不支持 target_table 参数。

  • 解决方案

    1. 检查 Flink 库目录:ls -l $FLINK_HOME/lib/paimon*.jar

    2. 删除所有旧版本(如 paimon-flink-1.19-0.9.0.jar)。

    3. 仅保留 paimon-flink-1.19-1.1.1.jar 和对应的 Hive Connector。

    4. 重启集群(必须重启才能生效)。

    5. 使用方案二可以解决

7.2 权限拒绝 (Permission denied by sticky bit)

  • 报错信息

    [ERROR] Could not execute SQL statement. Reason:
    org.apache.hadoop.ipc.RemoteException(org.apache.hadoop.security.AccessControlException): Permission denied by sticky bit: user=bigdata, path="/user/hive/warehouse/test.db/test_table/000000_0":root:hive:-rwxrwxrwt, parent="/user/hive/warehouse/test.db/test_table":root:hive:drwxrwxrwt

  • 根本原因HDFS 粘滞位限制。 Hive 表数据通常由 roothive 用户创建。CDH 默认开启 Sticky Bit,导致 Flink 运行用户(如 bigdata)无权移动或重命名属于 root 的文件,即使 bigdata 有写权限。

  • 解决方案: 在执行迁移前,必须使用超级用户修改源表文件的所有者。

    # 将源表 HDFS 文件的所有者修改为 Flink 运行用户
    sudo -u hdfs hdfs dfs -chown -R bigdata:hive /user/hive/warehouse/test.db/test_table

    注意:每次重建 Hive 源表后,文件归属会变回 root,需要重新执行此命令。

7.3 未知分区格式 (Unknown partition format / PaimonSerDe)

  • 报错信息

    java.lang.UnsupportedOperationException: Unknown partition format: SerDeInfo(name:null, serializationLib:org.apache.paimon.hive.PaimonSerDe, parameters:{})

  • 根本原因源表元数据被污染。 通常是因为之前的 migrate_table 命令执行了一半(例如:因不支持 target_table 导致默认在原地修改了元数据,将 Hive 表属性改成了 Paimon 属性,但未改名)。当你再次尝试将这个“已经是 Paimon 格式”的表当作“Hive 源表”进行迁移时,程序会报错。

  • 解决方案

    1. 重置源表:在 Hive/Spark 中 DROP TABLE test.test_table

    2. 重建源表:重新执行 CREATE TABLE ... STORED AS ORC 并导入数据。

    3. 重新赋权:执行 6.1 中的 chown 操作。

    4. 再次迁移:执行 CALL sys.migrate_table(...)

7.4 路径跨库错误 (Path ... is different from where it should be)

  • 报错信息

    CatalogException: Path '.../test.db/test_table' is different from where it should be '.../test_db.db/test_table'
  • 根本原因Hive Catalog 路径规范限制。 Paimon 的 Hive Catalog 严格遵循 Hive 目录规范。如果你尝试手动 CREATE TABLE 或使用 migrate_table 但未正确重命名路径,导致表名所在的库(test_db)与底层 HDFS 路径(test.db)不匹配,会被拒绝。

  • 解决方案

    • 首选:使用 Paimon 1.1.1 的 target_table 参数,让程序自动处理跨库移动。

    • 备选:手动使用 hdfs dfs -mv 将数据移动到规范路径后,再使用 register_table

7.5 目标库无表 (Success but table missing)

  • 现象migrate_table 显示成功,但在目标库 test_db 中找不到表,表还在源库 test 中。

  • 根本原因原地转换。 这是使用了不支持 target_table 的旧版 Jar 包,或未指定该参数时的默认行为。Paimon 仅修改了源表的元数据,未执行移动操作。

  • 解决方案

    • 参考 6.1 升级 Jar 包并指定 target_table

    • 或者使用 ALTER TABLE test.test_table RENAME TO test_db.test_table_2 手动完成最后一步。

7.6 文件系统存在 Schema (Schema in filesystem exists)

  • 现象:使用 CREATE TABLE 挂载已有数据时报错。

  • 根本原因:Paimon 保护机制。目标目录下已存在 Paimon 格式的数据(manifest/schema文件),禁止通过 CREATE 覆盖。

  • 解决方案:使用 CALL sys.register_table(...) 替代 CREATE TABLE

8.总结与最佳实践

在 CDH 6.3.2 这种较老的 Hadoop 环境中使用新版 Paimon (1.1.1) 进行 Hive 迁移,成功的关键在于环境纯净度权限控制

8.1 迁移策略选择

  • 策略 A:零拷贝秒级迁移 (sys.migrate_table) ——【推荐】

    • 适用场景:海量数据(TB/PB级),无法承受数据重写的时间和存储成本。

    • 优势:速度极快,不占用额外存储,保留历史文件。

    • 要求:必须解决 HDFS 权限问题,必须使用正确的 Paimon 版本以支持跨库重命名。

  • 策略 B:CTAS 数据复制 (CREATE TABLE AS SELECT)

    • 适用场景:中小规模数据,源表格式不兼容(如 TextFile),或需要清洗数据。

    • 优势:操作简单,规避了底层权限和元数据污染问题,是最稳妥的兜底方案。

    • 劣势:速度慢,消耗计算资源和双倍存储。

8.2 成功的三个关键支柱

  1. 纯净的依赖环境 (Dependency)

    • 坚决杜绝 lib 目录下多版本 Jar 包共存。这是导致“签名不匹配”等玄学问题的根源。务必删除旧包,保留新包,并重启集群。

  2. 正确的权限管理 (Permission)

    • HDFS 的 Sticky Bit 是迁移操作的最大拦路虎。Flink 任务用户(bigdata)默认无权操作 Hive 原生数据。必须使用 sudo -u hdfs 手动介入修改文件所有者

  3. 正确的版本特性使用 (Features)

    • 充分利用 Paimon 1.1.1 的新特性(如 target_table)。它能自动处理跨库、重命名和路径移动,避免了手动 HDFS 操作带来的风险和繁琐。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐