KES 数据库迁移实战:从 Oracle/MySQL 到 KingbaseES 的平滑过渡指南
迁移背景与准备
这几年信创项目越来越多,数据库国产化替代成了不少企业的必修课。我参与过好几个从 Oracle 和 MySQL 迁移到 KingbaseES 的项目,有成功的也有踩坑的。说实话,迁移这事没有想象中那么可怕,但也没宣传得那么简单。关键是要做好前期评估,选对工具和策略,测试要充分。
这篇文章把迁移过程中积累的经验整理出来,重点讲实际操作层面的东西。如果你正在规划一个迁移项目,或者已经接到任务不知道从何下手,希望能给你一些参考。内容涵盖迁移评估、数据迁移、应用改造、测试验证这几个核心环节,尽量做到拿来就能用。
为什么要做迁移评估
很多项目一上来就直接开始迁数据,结果迁到一半发现一堆兼容性问题,应用改不动,最后只能回退。迁移评估的目的就是提前发现问题,评估工作量,制定合理的迁移方案。
评估维度主要包括:
- 对象兼容性: 表结构、索引、视图、存储过程、触发器等能否直接迁移
- SQL 兼容性: 应用里的 SQL 语句是否需要改写,改写工作量有多大
- 数据类型映射: 源数据库的数据类型在 KES 里有没有对应类型,精度是否一致
- 业务逻辑依赖: 有没有用到源数据库特有的功能,比如 Oracle 的包、MySQL 的自增主键等
- 性能影响: 迁移后性能会不会下降,哪些查询需要优化
评估工具与方法
KES 自带了一个迁移评估工具叫 KDTS(Kingbase Data Transfer Studio),能做初步的兼容性扫描。不过我更习惯先用 SQL 脚本做一次快速摸底:
-- 统计源数据库的对象数量-- Oracle 示例SELECTobject_type,count(*)FROMuser_objectsGROUPBYobject_type;-- MySQL 示例SELECTtable_schema,table_type,count(*)astable_countFROMinformation_schema.tablesWHEREtable_schema='your_database'GROUPBYtable_schema,table_type;拿到对象清单后,对照 KES 的兼容性文档逐个检查。重点关注这几类:
- 存储过程和函数: 这是重灾区,PL/SQL 和 KES 的 PL/SQL 语法虽然相似但有差异
- 触发器: 触发时机、NEW/OLD 变量的用法可能不一样
- 自定义类型: Oracle 的 TYPE、MySQL 的 ENUM 可能需要调整
- 序列和自增: Oracle 用 SEQUENCE,MySQL 用 AUTO_INCREMENT,KES 两种都支持但要统一规范
工作量估算经验值:
根据我做过的几个项目,大概的工作量分布是这样的:
| 迁移项 | 简单场景 | 中等复杂度 | 高复杂度 |
|---|---|---|---|
| 表结构迁移 | 1-2 天 | 3-5 天 | 1-2 周 |
| 数据迁移 | 半天 | 1-2 天 | 3-5 天 |
| 存储过程改造 | 几乎不用 | 1-2 周 | 1-2 月 |
| SQL 改写 | 少量 | 1-2 周 | 持续优化 |
| 应用代码调整 | 配置修改 | 1 周 | 2-4 周 |
| 测试验证 | 1 周 | 2-3 周 | 1-2 月 |
这个只是粗略估算,具体要看系统规模和复杂度。有个项目是从 Oracle 迁移,光存储过程就有 800 多个,改造花了将近 3 个月。所以前期评估一定要仔细,别低估了工作量。
数据迁移方案选择
数据迁移是迁移项目的核心环节,方案选对了后面会省很多事。常见的迁移方式有这么几种:全量迁移、增量同步、双写切换。每种方案适用不同的场景。
全量迁移方案
全量迁移就是一次性把数据从源库搬到 KES,适合数据量不大(比如小于 500GB)、可以接受停机时间的场景。
使用 KDTS 工具迁移
KDTS 是电科金仓官方提供的迁移工具,图形界面操作,支持 Oracle、MySQL、SQL Server 等多种源数据库。基本步骤:
- 安装 KDTS
从官方获取安装包,解压后运行安装程序。Windows 和 Linux 版本都有,建议在迁移目标服务器上安装,减少网络传输。
- 创建迁移项目
打开 KDTS,新建项目,选择源数据库类型和目标数据库(KingbaseES)。填写连接信息:
源数据库: 类型: Oracle 11g 主机: 192.168.1.100 端口: 1521 用户名: source_user 密码: xxxxxx 目标数据库: 类型: KingbaseES V9 主机: 192.168.1.200 端口: 54321 用户名: system 密码: xxxxxx- 选择迁移对象
KDTS 会自动扫描源数据库的对象列表,可以勾选要迁移的表、视图、索引等。建议先只迁表结构和数据,存储过程和函数后面手动处理,这样更可控。
- 配置映射规则
数据类型映射是关键。KDTS 有默认的映射规则,但有些情况下需要手动调整:
Oracle → KES 常见映射: NUMBER(p,s) → NUMERIC(p,s) VARCHAR2(n) → VARCHAR(n) DATE → TIMESTAMP CLOB → TEXT BLOB → BYTEA MySQL → KES 常见映射: INT → INTEGER VARCHAR(n) → VARCHAR(n) DATETIME → TIMESTAMP TEXT → TEXT LONGTEXT → TEXT DECIMAL(p,s) → NUMERIC(p,s)特别注意日期类型。Oracle 的 DATE 包含时间部分,对应 KES 的 TIMESTAMP;MySQL 的 DATETIME 也是对应 TIMESTAMP。如果映射错了,迁移后时间数据会出问题。
- 执行迁移
点击"开始迁移",KDTS 会先创建表结构,然后导入数据。迁移过程中可以看到进度条和日志。如果中途报错,可以根据错误信息调整后重新执行。
迁移后的验证
迁移完成后要做数据一致性校验:
-- 对比行数SELECTcount(*)FROMorders;-- 在源库和目标库分别执行-- 抽样对比数据SELECT*FROMordersWHEREidBETWEEN1AND100;-- 对比汇总数据SELECTsum(amount),avg(amount),max(created_at)FROMorders;如果数据量很大,可以用 MD5 校验:
-- 在源库生成校验值SELECTmd5(string_agg(id::text||amount::text||created_at::text,','ORDERBYid))FROMorders;-- 在 KES 执行同样的查询,对比结果全量迁移的优缺点
优点:
- 操作简单,工具自动化程度高
- 一次性完成,不需要维护同步链路
- 适合小中型数据库
缺点:
- 需要停机窗口,业务中断时间长
- 大数据量迁移耗时长,风险高
- 迁移期间源库有新数据会产生不一致
增量同步方案
对于不能长时间停机的核心业务系统,增量同步是更好的选择。思路是先做一次全量迁移,然后通过 CDC(Change Data Capture) 技术实时同步源库的变更,等两边数据基本一致后再切换。
基于日志解析的增量同步
Oracle 可以用 LogMiner 或 GoldenGate 抽取 redo log,MySQL 可以用 binlog。KES 这边通过编写消费程序或者使用第三方工具来接收增量数据并应用到目标库。
以 MySQL binlog 为例,大致流程:
# 1. 确保 MySQL 开启了 binlog# my.cnf 配置[mysqld]log-bin=mysql-bin binlog-format=ROW server-id=1# 2. 使用 canal 或 Debezium 监听 binlog# canal 配置示例canal.instance.master.address=192.168.1.100:3306canal.instance.dbUsername=canal_usercanal.instance.dbPassword=canal_passwordcanal.instance.defaultDatabaseName=test_db# 3. 编写消费者程序,将变更应用到 KES# Java 伪代码public class BinlogConsumer{public void onEvent(BinlogEntry entry){if(entry.getType()==INSERT){// 转换为 KES 的 INSERT 语句并执行 ksesTemplate.update(convertToKESInsert(entry));}elseif(entry.getType()==UPDATE){ksesTemplate.update(convertToKESUpdate(entry));}elseif(entry.getType()==DELETE){ksesTemplate.update(convertToKESDelete(entry));}}}这种方式技术门槛比较高,需要自己开发或者购买商业工具。如果对实时性要求不是特别高(分钟级延迟可接受),可以用定时增量同步的方式。
定时增量同步
思路是定期从源库抽取新增和变更的数据,批量插入或更新到 KES。实现方式比较简单:
-- 在源库标记已同步的数据-- 给每张表加一个 sync_flag 字段ALTERTABLEordersADDCOLUMNsync_flagSMALLINTDEFAULT0;-- 每次同步时只取未同步的数据SELECT*FROMordersWHEREsync_flag=0ANDcreated_at>:last_sync_time;-- 同步成功后更新标记UPDATEordersSETsync_flag=1WHEREidIN(:synced_ids);配合定时任务(比如每 5 分钟执行一次),可以实现准实时的数据同步。这种方式虽然有几分钟的延迟,但对于大多数业务场景已经够用了。
增量同步的切换流程
当增量同步稳定运行一段时间(建议至少观察一周),确认数据一致性没有问题后,就可以安排切换了:
- 停写源库: 在业务低峰期,暂停对源库的写入操作
- 等待同步完成: 等增量同步把最后一批数据处理完
- 数据校验: 再次做全量数据对比,确保完全一致
- 切换应用: 修改应用配置,指向 KES 数据库
- 恢复写入: 启动应用,开始向 KES 写入数据
- 观察监控: 密切监控系统运行状态,有问题及时回滚
整个切换过程控制在 30 分钟以内比较理想。如果超过这个时间,说明前期准备工作做得不够充分。
双写方案
双写是在过渡期内让应用同时向源库和 KES 写入数据,读操作先从 KES 查,查不到再 fallback 到源库。这种方式可以实现零停机切换,但应用改造工作量大,而且存在数据不一致的风险。
双写实现思路
@ServicepublicclassOrderService{@AutowiredprivateDataSourcesourceDataSource;// 源数据库@AutowiredprivateDataSourcekesDataSource;// KES 数据库publicvoidcreateOrder(Orderorder){// 双写try{sourceTemplate.insert(order);// 写入源库kesTemplate.insert(order);// 写入 KES}catch(Exceptione){// 记录异常,人工介入处理log.error("双写失败",e);thrownewRuntimeException("订单创建失败");}}publicOrdergetOrder(Longid){// 优先从 KES 读try{returnkesTemplate.selectById(id);}catch(Exceptione){// fallback 到源库log.warn("KES 查询失败,fallback 到源库",e);returnsourceTemplate.selectById(id);}}}双写方案的优点是切换灵活,可以随时回退;缺点是代码复杂度高,而且要保证两个库的事务一致性比较困难。一般只在极端场景下(比如绝对不能停机)才会考虑。
我的建议
做过几个迁移项目后,我的经验是:
- 数据量小于 500GB、可以接受 2-4 小时停机的,用全量迁移最简单
- 数据量大或者停机窗口短的,用全量 + 增量同步的组合方案
- 双写方案除非万不得已,否则不建议用,维护成本太高
应用改造要点
数据迁过去了,应用还得能正常运行。这部分的工作量往往比数据迁移本身还大,因为涉及到 SQL 改写、驱动更换、配置调整等多个方面。
JDBC 驱动更换
第一步是把数据库驱动换成 KES 的。KES 提供了专门的 JDBC 驱动 kingbase8.jar, Maven 坐标一般是内部仓库提供:
<dependency><groupId>com.kingbase</groupId><artifactId>kingbase8</artifactId><version>9.0.0</version></dependency>如果没有 Maven 仓库,可以把 jar 包放到项目的 lib 目录下手动引入。
驱动类名和 URL 格式
# Oracle 原配置 jdbc.driver=oracle.jdbc.OracleDriver jdbc.url=jdbc:oracle:thin:@192.168.1.100:1521:orcl # 切换到 KES jdbc.driver=com.kingbase8.Driver jdbc.url=jdbc:kingbase8://192.168.1.200:54321/testdb注意 URL 格式的变化,端口号、数据库名都要改成 KES 的实际配置。
SQL 改写
这是应用改造的重头戏。即使 KES 提供了兼容模式,也不可能 100% 兼容所有源数据库的语法。以下是一些常见的需要改写的场景:
Oracle 特有语法
-- 1. DECODE 函数-- OracleSELECTDECODE(status,1,'启用',0,'禁用','未知')FROMusers;-- KES (标准模式)SELECTCASEstatusWHEN1THEN'启用'WHEN0THEN'禁用'ELSE'未知'ENDFROMusers;-- 如果启用了 Oracle 兼容模式,DECODE 可以直接用-- 2. NVL 函数-- OracleSELECTNVL(phone,'未填写')FROMusers;-- KES (标准模式)SELECTCOALESCE(phone,'未填写')FROMusers;-- 3. ROWNUM-- OracleSELECT*FROMordersWHEREROWNUM<=10;-- KESSELECT*FROMordersLIMIT10;-- 4. TO_CHAR / TO_DATE 格式字符串-- OracleSELECTTO_CHAR(created_at,'YYYY-MM-DD HH24:MI:SS')FROMorders;-- KES (格式基本一致,但要注意时区处理)SELECTTO_CHAR(created_at,'YYYY-MM-DD HH24:MI:SS')FROMorders;-- 5. 外连接写法-- OracleSELECT*FROMorders o,users uWHEREo.user_id=u.id(+);-- KES (标准 SQL)SELECT*FROMorders oLEFTJOINusers uONo.user_id=u.id;MySQL 特有语法
-- 1. LIMIT offset, count-- MySQLSELECT*FROMordersLIMIT10,20;-- KES (标准 SQL)SELECT*FROMordersLIMIT20OFFSET10;-- 2. GROUP_CONCAT-- MySQLSELECTdepartment,GROUP_CONCAT(employee_name SEPARATOR', ')FROMemployeesGROUPBYdepartment;-- KESSELECTdepartment,STRING_AGG(employee_name,', 'ORDERBYemployee_name)FROMemployeesGROUPBYdepartment;-- 3. IFNULL-- MySQLSELECTIFNULL(nickname,username)ASdisplay_nameFROMusers;-- KESSELECTCOALESCE(nickname,username)ASdisplay_nameFROMusers;-- 4. 反引号-- MySQLSELECT`id`,`name`FROM`users`;-- KES (双引号或不加引号)SELECTid,nameFROMusers;-- 或者SELECT"id","name"FROM"users";-- 5. 自增主键-- MySQLCREATETABLEusers(idINTAUTO_INCREMENTPRIMARYKEY,nameVARCHAR(100));-- KES (用 SERIAL 或 IDENTITY)CREATETABLEusers(idSERIALPRIMARYKEY,nameVARCHAR(100));-- 或者CREATETABLEusers(idINTGENERATEDBYDEFAULTASIDENTITYPRIMARYKEY,nameVARCHAR(100));日期函数差异
-- OracleSELECTSYSDATEFROMdual;SELECTSYSDATE+7FROMdual;-- 7天后-- KESSELECTnow();SELECTnow()+INTERVAL'7 days';-- MySQLSELECTNOW();SELECTDATE_ADD(NOW(),INTERVAL7DAY);-- KESSELECTnow();SELECTnow()+INTERVAL'7 days';分页查询统一写法
不同数据库的分页语法差异很大,建议在应用层统一封装:
publicclassPageHelper{publicstaticStringbuildPageSql(StringoriginalSql,intpageNum,intpageSize,DatabaseTypedbType){switch(dbType){caseORACLE:return"SELECT * FROM ("+originalSql+") WHERE ROWNUM <= "+(pageNum*pageSize);caseMYSQL:returnoriginalSql+" LIMIT "+((pageNum-1)*pageSize)+", "+pageSize;caseKINGBASE:returnoriginalSql+" LIMIT "+pageSize+" OFFSET "+((pageNum-1)*pageSize);default:thrownewIllegalArgumentException("Unsupported database type");}}}不过更好的做法是用 MyBatis Plus、JPA 这类 ORM 框架,它们已经做了数据库方言的适配,切换数据库时只需要改配置就行。
存储过程改造
如果源数据库里有大量存储过程,这部分改造工作量会比较大。KES 支持 PL/SQL,语法跟 Oracle 很接近,但还是有一些差异需要注意。
常见差异点
-- 1. 包(Package)的支持-- Oracle 有包的概念,KES V9 也支持,但老版本可能不支持-- 如果有包,需要确认 KES 版本是否支持,或者拆分成独立的函数/过程-- 2. 异常处理-- OracleEXCEPTIONWHENNO_DATA_FOUNDTHENDBMS_OUTPUT.PUT_LINE('未找到数据');WHENOTHERSTHENRAISE;-- KES (基本一致,但异常名称可能不同)EXCEPTIONWHENNO_DATA_FOUNDTHENRAISE NOTICE'未找到数据';WHENOTHERSTHENRAISE;-- 3. 动态 SQL-- OracleEXECUTEIMMEDIATE'SELECT count(*) FROM '||table_nameINTOv_count;-- KES (语法相同)EXECUTEIMMEDIATE'SELECT count(*) FROM '||table_nameINTOv_count;-- 4. 游标处理-- OracleOPENcurFORSELECT*FROMusersWHEREstatus=1;FETCHcurINTOv_user;CLOSEcur;-- KES (语法相同)OPENcurFORSELECT*FROMusersWHEREstatus=1;FETCHcurINTOv_user;CLOSEcur;改造建议
- 先在测试环境编译: 把所有存储过程拿到 KES 测试环境编译,看有哪些报错
- 逐个修复: 根据错误信息逐个修改,大部分是语法细节问题
- 功能测试: 编译通过后要做功能测试,确保逻辑正确
- 性能测试: 有些存储过程在 KES 上执行效率可能不如源库,需要优化
有个项目从 Oracle 迁移,800 多个存储过程里有 600 多个可以直接编译通过,剩下 200 多个需要修改。主要问题是包的使用、自定义类型的引用、还有一些 Oracle 特有的内置函数。花了差不多 2 个月才全部改完。
事务和隔离级别
KES 默认的事务隔离级别是 READ COMMITTED,跟 Oracle 一样,但跟 MySQL 的默认级别(REPEATABLE READ)不同。如果应用依赖 MySQL 的 REPEATABLE READ 特性,可能需要调整。
-- 查看当前隔离级别SHOWTRANSACTIONISOLATIONLEVEL;-- 设置会话级别的隔离级别SETTRANSACTIONISOLATIONLEVELREPEATABLEREAD;-- 或者在 kingbase.conf 中全局设置default_transaction_isolation='repeatable read'不过我建议尽量用标准的 READ COMMITTED,避免依赖特定隔离级别的行为。这样以后换其他数据库也方便。
测试验证策略
迁移完成后,测试验证是保证质量的最后一道关口。这个阶段要做得细致,不然上线后出问题就麻烦了。
数据一致性测试
全量对比
对于核心表,要做全量的数据对比。可以用前面说的 MD5 校验,也可以逐字段对比:
# Python 脚本示例:对比两张表的数据importpsycopg2importcx_Oracledefcompare_tables(source_conn,target_conn,table_name):# 获取源库数据source_cursor=source_conn.cursor()source_cursor.execute(f"SELECT * FROM{table_name}ORDER BY id")source_data=source_cursor.fetchall()# 获取目标库数据target_cursor=target_conn.cursor()target_cursor.execute(f"SELECT * FROM{table_name}ORDER BY id")target_data=target_cursor.fetchall()# 对比行数iflen(source_data)!=len(target_data):print(f"行数不一致: 源库={len(source_data)}, 目标库={len(target_data)}")returnFalse# 逐行对比foriinrange(len(source_data)):ifsource_data[i]!=target_data[i]:print(f"第{i+1}行数据不一致")print(f"源库:{source_data[i]}")print(f"目标库:{target_data[i]}")returnFalseprint(f"{table_name}数据一致性校验通过")returnTrue这种全量对比适合数据量不大的表。如果表里有几千万行数据,全量对比太慢,可以用抽样对比加汇总对比的组合方式。
业务场景验证
挑几个典型的业务场景,在测试环境完整跑一遍流程:
- 用户注册登录
- 下单支付
- 数据查询报表
- 批量导入导出
每个场景都要验证功能是否正常、数据是否正确、性能是否达标。最好能让业务方参与测试,他们更了解实际使用情况。
性能测试
迁移后性能会不会下降是大家最关心的问题。要做系统的性能测试,跟迁移前的基准数据做对比。
测试方法
- 单 SQL 性能对比: 挑出 TOP 50 常用的查询,分别在源库和 KES 上执行,对比响应时间
-- 记录执行时间和执行计划EXPLAIN(ANALYZE,BUFFERS,FORMAT JSON)SELECT*FROMordersWHEREuser_id=1001ANDcreated_at>'2026-01-01'ORDERBYcreated_atDESCLIMIT100;并发压力测试: 用 JMeter 或 LoadRunner 模拟真实并发场景,测试 TPS、响应时间、错误率等指标
长时间稳定性测试: 持续压测 24-48 小时,观察是否有内存泄漏、连接泄漏等问题
性能优化建议
如果发现某些查询在 KES 上明显变慢,可以从以下几个方面优化:
- 检查执行计划: 看看是不是走了全表扫描,是否需要加索引
- 统计信息更新: 执行 ANALYZE 更新统计信息,让优化器做出更好的决策
- 参数调优: 调整 shared_buffers、work_mem 等内存参数
- SQL 改写: 有些 SQL 在 KES 上可能需要换个写法才能发挥最佳性能
有个项目迁移后发现某个报表查询从原来的 2 秒变成了 15 秒。排查下来是因为 KES 的优化器选错了索引。手动加了 Hint 之后,性能恢复到 1.5 秒,比原来还快。
回滚预案
不管测试多充分,上线还是有风险。一定要准备好回滚预案,万一出问题能快速切回源库。
回滚步骤
- 停止应用: 暂停所有对 KES 的写入
- 数据同步: 如果迁移后有数据产生,要把这部分数据同步回源库
- 切换配置: 修改应用配置,指回源数据库
- 重启应用: 重新启动应用服务
- 验证功能: 确认业务恢复正常
回滚时间要求
回滚操作要在 30 分钟内完成,否则业务中断时间太长。这就要求:
- 回滚脚本要提前写好并测试过
- 配置切换要自动化,不要手工改
- 相关人员要到位,明确分工
我在一个项目里见过因为回滚预案没准备好,出问题后手忙脚乱搞了 2 个小时才恢复,被领导骂惨了。所以这块一定要重视。
迁移案例分享
最后分享两个实际的迁移案例,希望能给大家一些参考。
案例一:Oracle 到 KES 的政务系统迁移
项目背景
某市级政务服务平台,后端用 Oracle 11g,数据量约 200GB,有 300 多张表、500 多个存储过程。要求迁移到 KES V9,停机窗口不超过 4 小时。
迁移方案
采用"全量迁移 + 离线验证"的方案:
前期准备(2 周)
- 用 KDTS 做兼容性评估,发现 80% 的对象可以直接迁移
- 梳理需要改造的存储过程和 SQL,列出清单
- 搭建 KES 测试环境,版本跟生产一致
应用改造(3 周)
- 更换 JDBC 驱动,修改配置文件
- 改写不兼容的 SQL,主要是 DECODE、ROWNUM、外连接这些
- 改造存储过程,主要是包的处理和一些 Oracle 特有函数
- 单元测试和集成测试
数据迁移演练(1 周)
- 在测试环境做多次全量迁移演练
- 记录迁移耗时,优化迁移脚本
- 验证数据一致性
正式迁移(1 天)
- 凌晨 0 点开始停服
- 0:00-0:30 备份源库
- 0:30-2:30 执行全量迁移(实际耗时 1 小时 50 分)
- 2:30-3:30 数据校验和应用切换
- 3:30-4:00 冒烟测试
- 4:00 恢复服务
遇到的问题
存储过程编译失败: 有 30 多个存储过程用到了 Oracle 的 UTL_FILE 包,KES 不支持。解决方案是改用 KES 的文件操作函数重写。
性能下降: 迁移后发现某个复杂查询从 3 秒变成了 12 秒。通过分析执行计划,发现是缺少合适的索引。加了复合索引后降到 2 秒。
字符集问题: 源库用的是 ZHS16GBK,迁移到 KES 的 UTF8 后,有些特殊字符显示异常。后来在迁移工具里设置了正确的字符集转换规则,问题解决。
总结
整个项目历时 2 个月,最终顺利上线。关键成功因素:
- 前期评估充分,提前发现了大部分兼容性问题
- 测试环境跟生产环境保持一致,避免了环境差异导致的问题
- 迁移演练做了 3 次,正式迁移时心里有底
- 回滚预案准备充分,虽然没用上,但给了团队信心
案例二:MySQL 到 KES 的电商系统迁移
项目背景
某电商平台,后端用 MySQL 5.7,数据量约 80GB,主要是订单、商品、用户数据。没有存储过程,但有很多复杂的查询和定时任务。要求迁移过程对业务影响最小。
迁移方案
采用"全量 + 增量同步"的方案,实现近零停机切换:
全量迁移(周末进行)
- 周六凌晨 2 点开始全量迁移,耗时 3 小时
- 迁移完成后启动增量同步
增量同步(持续 1 周)
- 用 canal 监听 MySQL binlog,实时同步到 KES
- 监控同步延迟,保持在 5 秒以内
- 每天做数据一致性校验
灰度切换(1 周)
- 周一先切 10% 的流量到 KES,观察一天
- 周三切 50%,继续观察
- 周五全量切换
下线源库(切换后 1 个月)
- 保留 MySQL 作为备份,观察 1 个月
- 确认无问题后下线
遇到的问题
- 自增主键冲突: MySQL 的 AUTO_INCREMENT 和 KES 的 SERIAL 行为不完全一致,导致偶尔出现主键冲突。解决方案是在迁移时手动设置序列的起始值为最大值 + 1000,留出缓冲空间。
-- 迁移后调整序列SELECTsetval('orders_id_seq',(SELECTMAX(id)FROMorders)+1000);- 分页性能: MySQL 的 LIMIT offset, count 在大偏移量时性能很差,KES 的 LIMIT/OFFSET 也有类似问题。解决方案是改用游标分页或者基于 ID 的范围查询。
-- 不好的写法SELECT*FROMordersORDERBYidLIMIT100OFFSET100000;-- 改进的写法SELECT*FROMordersWHEREid>:last_idORDERBYidLIMIT100;- GROUP BY 严格模式: MySQL 默认允许 GROUP BY 不包含所有非聚合列,KES 遵循标准 SQL,要求更严格。需要改写一些 SQL,把缺失的列加到 GROUP BY 里或者用聚合函数包裹。
总结
这个项目最大的亮点是实现了近零停机切换,业务方几乎无感知。关键在于增量同步的稳定性和灰度切换的策略。整个迁移过程平稳,没有出现重大故障。
写在最后
数据库迁移是个系统工程,涉及技术、流程、人员多个方面。技术层面要做好评估、选对工具、充分测试;流程层面要有明确的计划和应急预案;人员层面要协调好开发、测试、运维、业务各方。
KingbaseES 在兼容性方面做得不错,尤其是 Oracle 兼容模式,能大幅降低迁移成本。但再好的工具也不能代替人的工作,前期的评估和测试一定要做扎实。我见过太多项目因为前期工作不到位,后期各种问题爆发,最后不得不返工。
迁移这件事,说难也难,说不难也不难。关键是态度要认真,方法要科学,执行要到位。希望这篇文章能帮你少走一些弯路,顺利完成迁移任务。加油!