【金仓数据库征文】不装中间件的 MySQL→金仓在线迁移,mysql_fdw 全流程,和一个差点漏掉的 emoji

一、迁移的两种痛

把一个 MySQL 业务库迁到 KingbaseES,传统路径通常绕不开两样东西,一个导出导入的停机窗口,和一个额外部署的数据同步中间件。前者要业务方点头给时间,后者要多维护一个组件、多配一套规则。对中小库或者灰度试点来说,这两样都嫌重。

有没有更轻的办法?有,金仓自带的 mysql_fdw,全称 MySQL Foreign Data Wrapper,外部数据包装器。它让金仓能像访问本地表一样直接读远端 MySQL,于是迁移可以简化成一条 INSERT ... SELECT,不导文件,不装中间件,源库不停机。

这篇我在鲲鹏服务器上把这条路走通了。而且在最后的对账环节,还抓到了一个只数行数绝对发现不了的静默数据损坏。这个坑,才是整篇文章我最想分享的东西。

二、造一个「有刺」的迁移源

为了让测试有意义,我在 MySQL 侧造了一个电商库 shop,特意埋了几类迁移里最容易翻车的数据。

两张表,customers 8 行,orders 10 行。刺埋在这些地方,中文的名字和城市,NULL 的邮箱,decimal(12,2) 的精确金额,datetime 时间戳。最关键的一根刺在 orders 里,有一行 note 写的是 生日礼物🎁,带一个 utf8mb4 的 emoji,4 字节字符,整个字段 16 字节。你先记住它,后面它是主角。

三、mysql_fdw,不搬数据先挂载

迁移第一步不是搬数据,是让金仓能看见 MySQL。三条 DDL 就够了。

SQL 复制代码
CREATE EXTENSION mysql_fdw;
CREATE SERVER shop_mysql FOREIGN DATA WRAPPER mysql_fdw
  OPTIONS (host '127.0.0.1', port '3306');
CREATE USER MAPPING FOR system SERVER shop_mysql
  OPTIONS (username 'fdw', password 'Fdw#2026');

然后为 MySQL 表建外部表,把 MySQL 类型映射成 KES 类型。挂载完成,直接在金仓里 SELECT。

\d ft_customers 能看到外部表的完整定义,列类型、所属 Server,还有 OPTIONS (dbname 'shop', table_name 'customers')。执行 SELECT 的时候,数据物理上还在 MySQL 里,SQL 由金仓执行、实时拉取。类型映射很自然,MySQL 的 int、decimal(12,2)、datetime、tinyint,分别落到 KES 的 int、numeric(12,2)、timestamp、smallint。

这一步真正的价值,是先建立了通路。有了通路,迁移、对账、增量追平全在同一条通路上完成,不需要第二个工具。

四、差点漏掉的 emoji

正当我准备一把梭迁移的时候,随手验了一下那行 emoji。

出事了。

金仓通过 fdw 读到的 note 是 生日礼物?,emoji 变成了问号。用字节数一验,事情就清楚了。MySQL 源里是 16 字节,hex 结尾 F09F8E81,正是 🎁 的 utf8mb4 四字节编码。fdw 读到的只有 13 字节,hex 结尾 3F,就是一个问号。

那 4 个字节,在传输途中被悄悄吃掉了。根因是 mysql_fdw 默认用 3 字节的 utf8 字符集连接 MySQL,遇到 4 字节的 utf8mb4 字符,emoji、部分生僻字、CJK 扩展区汉字,就地转成问号。

我先试着给 server 加一个 character_set 选项,被拒了,它不在合法选项列表里。但报错的 HINT 里列出的合法选项中,有一个 init_command,它能在每次建立连接时执行一条 SQL。于是有了这一句。

SQL 复制代码
ALTER SERVER shop_mysql OPTIONS (ADD init_command 'SET NAMES utf8mb4');

再查,生日礼物🎁 回来了,16 字节,hex 结尾 F09F8E81,分毫不差。

这个坑最可怕的地方是静默。不报错,不中断,行数一分不少,只是每个 4 字节字符悄悄变成问号。如果迁移验证只对一下行数,甚至只对一下金额总和,它能一路蒙混过关,直到某天用户投诉,我备注里的表情怎么全变成问号了。迁移前先校准 fdw 字符集,这是我用这次经历换来的第一条铁律。

五、迁移,一条 INSERT 搞定

字符集校准好,迁移就是水到渠成的一条语句。

SQL 复制代码
CREATE TABLE customers (...);      -- KES 本地表
INSERT INTO customers SELECT * FROM ft_customers;   -- 8 行
CREATE TABLE orders (...);
INSERT INTO orders SELECT * FROM ft_orders;         -- 10 行

INSERT 0 8INSERT 0 10,数据从 MySQL 直接流进金仓本地表。没有中间的 CSV 文件,没有 mysqldump,没有第三方同步工具,外部表本身就是管道。迁移完成后,本地表是纯粹的 KES 原生表,不再依赖 MySQL。

六、对账,为什么只数行数会出事

迁移完成不等于迁移正确。前面的 emoji 事件已经证明,数据可以在行数完全一致的情况下悄悄损坏。所以我做了三级对账,一级比一级严。

对账级别 能抓住什么 会漏掉什么
① 行数 count 整行丢失、重复 行在但内容错,比如 emoji 变问号
② 数值列 SUM 金额、数量类错误 字符串损坏、时间偏移
③ MD5 全表指纹 逐行逐列的任何差异

第三级是关键。我在金仓里对外部表(源)和本地表(目标)执行完全相同的 SQL,把每行拼成字符串、排序后求 MD5。因为两边都由金仓引擎执行,口径完全一致,只要有任何一个字节不同,指纹就会不同。

结果,customers 三项全等,8 行对 8 行,金额总和 374550.02 对 374550.02,MD5 指纹 d4835ee4... 完全相同。orders 也三项全等,包括那行 emoji,MD5 572d7a44... 相同。MD5 一致,等于逐行逐列宣告无损。假如我没修字符集就迁移,这里的 MD5 会立刻对不上。三级对账存在的意义就在这,让静默损坏藏不住。

但这里还有一个坑中坑。我的目标库建的是 MySQL 兼容模式,而在 MySQL 兼容模式下,|| 不是字符串拼接,是逻辑或。我第一版对账脚本顺手用了 id||'|'||name||... 这种写法拼行,算出来的 MD5 指纹其实是假的。我专门做过一个验证,故意把目标表某一行的 email 改成错误值,用 || 拼出来的指纹,源和目标居然依旧相同,逻辑或运算把真实的字段值吃掉了,篡改完全漏检。换成 concat(id,'|',name,...) 之后,指纹立刻对不上,篡改当场现形。

一个会漏检数据损坏的对账脚本,比不对账更危险 ,它给你的是虚假的安全感。所以在金仓 MySQL 兼容库里做对账,拼接一律用 concat(),别用 ||

七、双轨共存与增量追平

真实迁移往往不能一刀切,需要一段灰度窗口,源库还在接单,新库先并行验证。mysql_fdw 天然支持这种双轨,因为那条通路一直在,随时能对账。演示一下。

我在 MySQL 侧新增了一单,id=99,模拟灰度期还在进来的订单。对账立刻兜住了,源库 11 行,目标 10 行,MD5 指纹对不上,差异秒级暴露。接着增量追平,INSERT INTO orders SELECT * FROM ft_orders WHERE id NOT IN (SELECT id FROM orders),只补目标缺的那一行。再对账,11 对 11,MD5 恢复一致。

源库持续写入,fdw 增量追平,MD5 对账兜底,这个循环就是平滑迁移窗口的核心机制。切换当天反复跑对账直到追平,确认一致之后,再把应用的连接串从 MySQL 换到金仓,风险可控。

八、结论

我用 mysql_fdw 走通了一条不停机、不装中间件的 MySQL 到金仓的迁移路径。外部表建通路,一条 INSERT...SELECT 迁数据,三级对账验正确,双轨增量做平滑,源库只读可回退。

最后再把那条教训放在这。行数一致,不等于数据无损。fdw 的字符集要先校准,对账拼接要用 concat(),这两个坑,我都替你踩过了。

相关推荐
C++、Java和Python的菜鸟8 小时前
第5章 后端Web基础 (MySQL基础)
前端·mysql·adb
一个儒雅随和的男子8 小时前
多租户方案的选型
数据库·oracle
listening7779 小时前
HarmonyOS 6.1 跨设备数据库实战:分布式账本的落地与一致性校验
数据库·harmonyos·分布式账本
147API9 小时前
Claude Tag 进入 Slack 后,团队智能体需要哪些任务与审计字段
java·开发语言·数据库
Database_Cool_9 小时前
AI Agent 应用数据库选型:阿里云 PolarDB-X 高并发分布式数据底座
数据库·人工智能·阿里云
黄焖鸡能干四碗9 小时前
IT数据架构规划设计方案(PPT文件)
大数据·网络·数据库·人工智能·架构·区块链
黑夜路人9 小时前
可靠 Agent 设计:从一句 Prompt 到稳定交付
数据库·人工智能·prompt
IT瑞先生9 小时前
MariaDB与Mysql差异及版本对照
数据库·mysql·mariadb
草莓熊Lotso10 小时前
【Linux网络】深入理解Linux IO多路复用:从本质到select服务器实战
linux·运维·服务器·c语言·网络·数据库·c++