今天在用 Kettle(PDI)做数据迁移时,踩了一个典型的中文编码坑。错误信息看起来挺吓人:
sql
Incorrect string value: '\xC9\xDC\xD0\xCB\xCA\xD0...' for column 'content' at row 1
乍一看以为是目标库字符集问题,结果一路排查下来,发现根源藏得特别深。今天把整个过程记录下来,希望能帮到遇到同样问题的朋友。
起因:一个看似简单的数据同步任务
我的任务很明确:
- 源库 :MySQL 8.0,IP
180.23.1.1:3306,库名oldtest - 目标库 :另一个 MySQL 8.0 实例,端口
3315,库名newtest - 工具:Kettle 7.1(别问为什么不用新版,老项目)
两个表结构几乎一样,content 字段都是 LONGTEXT 类型。我以为直接"表输入 → 表输出"就能搞定,结果一跑就报错。
第一阶段:误判为连接参数格式错误
最初报的错根本不是编码问题,而是:
python
Invalid ID for region-based ZoneId, invalid format: Asia/Shanghai;Driver class=...
我一开始完全懵了:明明 URL 写得好好的,怎么 driver class 被拼进参数值里了?
🔍 排查过程:
- 反复检查 Custom URL,确认没有多余字符
- 突然想到:会不会是 "Options" 表格里写了东西?
- 打开一看------果然!之前测试时随手在 Options 里填过一行,Value 列不小心粘贴进了
Driver class=...这种非法内容
✅ 解决 :清空 Options 表格,所有参数统一写进 Custom URL,用 & 分隔。
教训:Kettle 的 Options 表格和 Custom URL 是合并使用的,脏数据会污染整个连接串。
第二阶段:真正的敌人------中文编码问题浮出水面
清理完连接参数后,能连上数据库了,但一写数据就报:
c
Incorrect string value: '\xC9\xDC\xD0\xCB\xCA\xD0...' for column 'content'
\xC9\xDC 是啥?查了一下,这是 GBK 编码下"深"字的十六进制。说明数据源里存的是 GBK 字节,但 MySQL 当 UTF8 解析,自然失败。
🔍 初步判断:
- 源表声明是
utf8,但实际存的是 GBK 数据(历史遗留) - 目标表是
utf8mb4,理论上兼容,但前提是传入的是合法 UTF8 字符串
于是我在两个连接里分别加了编码参数:
- 源库:
characterEncoding=GBK - 目标库:
characterEncoding=utf8
但还是失败!
第三阶段:被忽略的关键参数------useUnicode=true
这时候我开始怀疑人生:配置明明没错,为什么还是不行?
灵机一动,想起以前看过 MySQL JDBC 文档提到:从 5.1 开始,必须显式开启 Unicode 支持,否则 characterEncoding 可能被忽略。
赶紧加上 useUnicode=true:
text
# 源库
jdbc:mysql://...?useUnicode=true&characterEncoding=GBK&...
# 目标库
jdbc:mysql://...?useUnicode=true&characterEncoding=utf8&...
改完之后,还是失败。
我差点放弃,直到做了最后一件事------重启 Spoon。
神奇的事情发生了:转换成功跑通,中文正常写入!
为什么重启才生效?
后来复盘发现,Kettle 7.1 有个隐藏坑点:
数据库连接对象会被缓存,即使你修改了 URL,旧的连接可能还在用
也就是说,我虽然改了连接配置,但 Kettle 内部仍然用着之前没加 useUnicode=true 的连接池实例。只有重启 Spoon,才能彻底清空缓存,让新配置生效。
最终验证:确保每一步都正确
为了保险,我在"表输入"和"表输出"之间加了个"写日志"步骤,打印 content 字段。看到控制台输出的是"深圳市某某单位"这样的正常中文,心里才踏实。
同时确认目标表确实是 utf8mb4:
sql
SHOW CREATE TABLE target_table;
-- 输出包含:CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
经验总结
-
不要相信表的字符集声明
很多老系统把 GBK 数据塞进
utf8表,实际存储的是 GBK 字节。要通过HEX(content)验证真实编码。 -
JDBC 连接必须显式开启 Unicode
光写
characterEncoding=GBK不够,一定要加useUnicode=true。 -
Kettle 的 Options 表格是双刃剑
宁可全写进 Custom URL,也不要混用,避免参数污染。
-
改完配置记得重启 Spoon
Kettle 7.1 的连接缓存机制会让你误以为配置没生效。
-
用"写日志"步骤验证中间数据
在关键节点打印字段值,能快速定位是读取问题还是写入问题。
附:推荐的完整连接配置
源库(读取 GBK 数据):
ini
jdbc:mysql://180.23.1.1:3306/oldtest?
useUnicode=true&
characterEncoding=GBK&
useSSL=false&
allowPublicKeyRetrieval=true&
serverTimezone=Asia/Shanghai
目标库(写入 UTF8MB4):
ini
jdbc:mysql://180.23.1.1:3315/newtest?
useUnicode=true&
characterEncoding=utf8&
useSSL=false&
allowPublicKeyRetrieval=true&
serverTimezone=Asia/Shanghai
连接类型选 Generic database ,Driver class 填 com.mysql.cj.jdbc.Driver,Options 表格留空。
这次排查花了我大半天时间,但搞清楚原理后,以后再遇到类似问题就能秒解。希望这篇记录能帮你少走弯路。