上半篇光顾着吐槽和讲KDMS评估了,这篇往下说,聊聊报告拿到手之后,一个迁移项目到底是怎么一步步落地的。
先交代下背景,还是拿我最熟的那个运营商的项目说吧。这个项目规模在我做过的单子里算大的,而且时间紧,典型的"又想马儿跑又想马儿不吃草"。但也正因为难做,整个流程跑得比较完整,拿出来讲比较有说服力。
项目背景先简单说两句
客户是某省运营商的资源中心,管的是全省的网络资源数据——基站啊、光缆啊、机房啊、设备端口啊这些,全在这个库里。你们可以想象一下这个数据量级,一个省的通信网络资产,几千万条记录都是少的。
源端是Oracle,老库跑了很多年,业务系统7×24小时不能停。日均数据增量我记得当时测下来是45TB左右。对,你没看错,日增量45TB,不是总量。这数据量让我头都大了。
客户的要求很明确:一不能长时间停机,二数据不能丢不能错,三迁完性能不能拉胯。这三条任何一条出问题,都是生产事故。
进场之后第一步不用说,老规矩,KDMS采集评估。这个过程上篇讲过了就不重复,直接说评估出来的结果怎么用。
KDTS 迁移任务配置(示意)
─────────────────────────────
源端:Oracle / 192.168.x.x:1521/ORCL
目标端:KingbaseES / 10.x.x.x:54321/resource_db
迁移对象:☑表 ☑索引 ☑约束 ☑序列 ☑视图
☐存储过程(单独处理)
迁移方式:只迁结构 / 结构+数据
并发线程数:8
失败处理:记录日志并继续
─────────────────────────────
结构迁移的成功率一般很高,我们几个项目基本都在99%以上,但每次我都会核对几个东西:字段类型映射对不对、字符集有没有坑、自增列/序列的初始值对不对、外键约束建全了没。
字符集这个事情单独说一句,太容易踩坑了。源库如果是ZHS16GBK,目标库用UTF8,迁移过程中字符集转换是自动的,大部分中文没问题,但生僻字、特殊符号偶尔会出乱码或者长度溢出——GBK里一个汉字两个字节,UTF8里三个字节,你要是有个字段定义成VARCHAR2(10),里面存了五个汉字,GBK正好10字节,转UTF8要15字节,目标库字段如果按字节长度算,直接插不进去。
所以我习惯把存中文的字段按字符语义定义,或者干脆长度留足余量:
-- 稳妥的写法,按字符算长度
CREATE TABLE t_customer (
id INTEGER,
name VARCHAR(50 CHAR)
);
这种细节评估报告有时候会提醒,有时候不会,全靠自己记性好。我反正是吃过亏的,现在逢人就念叨。
重头戏:45TB日增量下的全链路并行同步
结构搭好了,存量数据也好处理,KDTS直接全量拉过去,虽然量大但不复杂,跑就是了,跑慢一点而已。真正要命的是增量——你全量迁移总要花时间吧,这几天里源库业务不停,新产生的数据怎么办?
靠KFS。KFS就是金仓那套异构数据同步的东西,逻辑日志解析那套机制,从源库的redo日志里把变更捞出来,实时搬到目标库。原理我不展开,网上资料很多,重点说说这个项目里最折磨人的部分——性能。
全链路并行拆开之后,大概是这么个结构:
KFS 全链路并行架构(简化版)
─────────────────────────────────────
源端Oracle
│
▼
[日志读取] ── 多线程并行解析
│
├── 按表/事务分组,分发到多个通道
│
▼
[网络传输] ── 多通道并发传输,支持压缩
│
▼
[目标端入库] ── 多事务并行应用
│ (有依赖的保序,无依赖的并发)
▼
目标库 KingbaseES
─────────────────────────────────────
这里头技术上最难的是最后一环——并行入库。你想啊,事务之间是有依赖的,事务A改了某行,事务B也改同一行,这俩要是并行应用、顺序反了,目标库的数据就错了。但如果为了保序把所有事务串起来,又退化成单线程了。
它的做法是分析事务依赖关系,有冲突的事务之间保证顺序,没有依赖的——比如改的是不同的表、不同的行——就可以放心并行。这个依赖分析是实时做的,基于主键、外键和实际变更行的信息。
配置上有几个关键参数,当时我们调了好久:
# KFS 并行同步相关参数(节选,具体名字以实际版本为准)
# 解析并行度
parser.parallel.threads=8
# 传输通道数
channel.count=16
# 目标端应用并行度
applier.parallel.threads=24
# 大表是否拆分并行
bigtable.parallel.enable=true
# 冲突检测策略:基于行依赖
dependency.track.mode=row
# 批量提交大小
applier.batch.size=2000
# 网络传输压缩
transport.compress=lz4
参数不是越大越好,线程数开太多,目标库那边连接数、CPU、IO扛不住,反而变慢。我们当时是一边压测一边调,从8线程开始往上加,盯着目标库的负载和同步延迟,最后找到一个平衡点——24个应用线程的时候,目标库CPU大概六七十,IO没打满,延迟稳定在秒级。再往上加线程,数据库成了瓶颈,吞吐不涨反跌。
还有个杀手锏,大表的初始同步可以拆成多段并行跑。那种几十亿行的超大表,单线程select拉数据得拉好几天,按主键范围切成几十段,多线程同时拉,时间直接压缩到原来的几分之一。这个对我们这种存量数据巨大的项目特别关键。
-- 大表分段的思路,大概就是这样按范围切
-- 每一段独立抽取、独立装载
-- 第1段
SELECT * FROM t_resource WHERE id BETWEEN 1 AND 100000000;
-- 第2段
SELECT * FROM t_resource WHERE id BETWEEN 100000001 AND 200000000;
-- ...以此类推
那段时间我天天盯监控,KFS自己的控制台能看到每个通道的TPS、堆积量、延迟。有天半夜告警,某个通道延迟突然飙到几分钟,我爬上去一看,是源端半夜跑批量大事务,单个事务改了几千万行,按顺序应用的时候把通道堵住了。后来调整了大事务的拆分策略,才稳住。这种问题不经历一次真是想都想不到。
割接那一夜
前面所有事情——评估、改造、全量、增量同步、校验——都是为了最后这一下:割接。
割接的核心目标说起来就一句话:选一个业务低峰期,把源库停写,等KFS把最后一点增量追平、最后做一次快速校验确认,然后把应用切到新库。
但就是这么简单一件事,每次做都紧张得不行。因为这是唯一没有后悔药的环节,前面出问题都能重来,割接失败就得回退,动静越大越被动。
我们当晚的安排大概是这样:
割接时间线(凌晨低峰窗口 0:00 - 4:00)
────────────────────────────────────
23:30 全员到位,最后确认各系统状态
KFS延迟检查(要求秒级)
应用、监控、回退脚本全部就绪
00:00 应用停止写入源库(只读模式/停应用)
通知:割接正式开始
00:05 KFS进入追尾模式,消费剩余日志
实时监控延迟归零
00:20 增量追平,源端目标端位点一致
00:25 最终快速校验(链式校验的精简模式)
关键表行数、关键聚合值确认
00:45 校验通过
00:50 修改应用数据源配置,指向新库
(提前准备好配置,切个开关的事)
01:00 应用启动,内部冒烟测试
登录、查询、下单、修改 全流程走一遍
01:30 小流量灰度,放少量真实用户进来
盯错误日志、盯性能指标
02:00 全量放开
02:00-04:00 全员留守观察
重点盯:慢SQL、报错率、业务指标
────────────────────────────────────
我为什么一直强调前期的评估和校验重要?你们看这个时间线,割接窗口只有四个小时,其中留给"出意外"的余量非常小。如果同步延迟追不平、或者校验发现大量差异,这个窗口根本兜不住,只能取消割接、白熬一夜,还要跟客户解释为什么延期。
而同步能不能按时追平,取决于你前面的并行架构调没调好;校验能不能快速通过,取决于链式校验靠不靠谱;甚至割接后应用会不会报错,都取决于前期KDMS有没有把应用里的SQL扫干净。割接那一夜的从容,全是前面几个月攒下的。
那天晚上实际还真出了个小插曲。应用切过去之后,冒烟测试发现一个查询接口超时,要十多秒才返回。当时全场气氛都凝固了。我们DBA赶紧上去看执行计划,发现是一张大表的统计信息没收集,优化器走了个糟糕的全表扫描。当场analyze了一下,再查,毫秒级返回。虚惊一场。
但这个事情给我提了醒,从那以后割接检查清单里我加了一条:全量数据迁完后、割接前,务必把目标库所有大表的统计信息重新收集一遍。这种坑踩一次就够记一辈子了。
回退方案也必须提前准备好。当时我们的预案是,如果新库出问题短时间解决不了,就把数据源切回源库,同时KFS开反向同步——把割接期间新库产生的增量数据同步回老库,保证回退之后数据不丢。所幸最后没用到,但带着这根救命稻草,心里踏实。
项目收尾和之后的事
割接成功不代表项目立马结束。后面还有一周左右的"护航期",团队轮班盯新库的运行情况,处理一些小问题——大多是边角报表SQL的兼容性,不影响主业务。这期间KFS反向链路一直挂着,作为回退保险。等一周过去系统稳稳当当,才把同步链路彻底关掉、老库做归档,项目才算真正收尾。
这个运营商项目整体做下来,我最大的感慨是:现在做迁移和我刚入行那会儿,真的是两个时代了。
刚入行的时候,迁移基本靠人肉,拿个老DBA的经验赌,迁完对数据靠运气,割接靠胆子。现在呢,评估有KDMS出量化报告,结构有KDTS自动迁移,增量有KFS全链路并行,校验有链式校验层层收敛,甚至割接后的差异都能自动修复。整个过程里,人更多是在做判断、做兜底、处理那些真正需要理解业务的复杂问题,而不是把时间浪费在机械重复的体力活上。
但我也不想把话说得太满,好像有了工具就能躺赢。工具再强,有几件事永远只能靠人:
一是理解业务。报告告诉你这个PACKAGE要改,但这个PACKAGE为什么这么设计、改动会不会影响某个业务约定,工具不知道。有次我们改一个计算逻辑,语法转得漂漂亮亮,结果上线后财务对账差了几分钱——老逻辑里有个四舍五入的特殊约定,代码里没有任何注释,全靠跟客户那边的老业务人员聊才挖出来。
二是做取舍。比如位图索引转B树、包级变量用什么方案模拟、某个慢SQL是改写法还是加索引,这些都不是有标准答案的题,得结合负载、结合运维成本、结合团队能力综合权衡。
三是应急。割接那晚统计信息的问题,工具不会自己跳出来帮你解决,靠的是DBA的经验和临场反应。
所以我对这些工具的定位一直很清楚:它们把迁移项目的下限抬高了,让你不至于因为"没看到某个角落的问题"而翻车;但项目的上限——能不能做得又快又稳又省心——还是取决于用工具的人。
最后说两句掏心窝子的
这两篇写得有点啰嗦,想到哪说到哪,大家凑合看。
如果你也在做或者即将做异构迁移,我的建议就一条:别省评估那点时间。我见过太多项目火急火燎地开工,迁到一半才发现存量复杂度远超预期,进退两难。花一天时间把KDMS跑一遍,拿到那份报告,你对项目的理解会上一个台阶——知道水有多深,才好决定怎么蹚。
评估报告里那些数字——多少对象、多少兼容、多少要改、多少人天——看着枯燥,但它们是整个项目里最值钱的东西。因为它们把一个本来充满不确定性的决策,变成了可以摆到桌面上讨论的事实。项目会上,领导问"风险有多大、多久能干完",你不用再支支吾吾说"应该问题不大",而是把报告摊开,一条一条指给他看。
这种感觉,干过项目的人都懂,踏实。
哦对,还漏了个事儿没说,就是那个环保集团的项目,顺便提一嘴,因为它代表了另一种场景。
那个客户是东部某省的环保集团,跟运营商项目不太一样,它不是单一大库,是十一个核心业务系统一起换——环保一体化、OA、投资管理、人力资源、数据中台、采购、供应链金融等等一大堆。原来底下用的库五花八门,好几种都有,等于要一锅端。
这种多系统的局面,KDMS评估反而是一个系统一个系统地跑,出了一摞报告,最后汇总。好处是每个系统的风险都清清楚楚,哪个系统先迁哪个后迁、各自需要多少资源,可以统筹着排兵布阵,而不是十几条线同时开工乱成一锅粥。
实际做下来真正需要人工干预的适配问题只有两个,我印象很深。一个是某张叫sys_user的表跟系统视图重名了,应用查的时候解析到错误的对象上去,最后靠JDBC连接参数里加个配置解决的。另一个是一条监测汇总的SQL,老库上要跑15秒,迁过去之后我们重新建索引、把关联精简了一下,最后1.8秒,快了快九成。客户那边负责的人原话是"干预极少,全程几乎没什么问题"——这话从甲方嘴里说出来,分量你们懂的。
举这个例子是想说,不管是运营商那种单一大库大流量,还是环保集团这种多系统多源端,前期评估这一步的逻辑是通用的:先把家底盘清楚,再动手。盘子不一样,道理是一个道理。
至于KFS那套并行同步和链式校验,如果你面对的是TB级、不能停机的大项目,我只能说,早点用,少熬夜。我这几年头发是真的少了不少,一部分得怪早年那些没有工具硬扛的项目。
行了,不废话了,祝各位迁移顺利,割接零故障。