做数据库替换的人,最怕的问题不是"怎么迁",是"你敢不敢给个数"。工期多少,风险在哪,哪些对象要改,改多少。以前这些问题我全靠经验估,估完自己心里都发虚。直到最近用上数据迁移工具 KDMS 做了一轮正经的迁移前评估,才头一回拿到一份能量化的报告,兼容度多少,不兼容多少处,分布在哪些对象类型上,全都列出来。这上下两篇就聊聊我这次评估的完整过程,上篇讲采集,下篇讲报告本身和怎么用它做决策,写的时候想到哪说到哪,可能有点乱,凑合看。
先把痛点说透,不然有人觉得评估就是个走过场。
评估难到底难在哪
我干替换项目四五年,每次立项会都是同一个剧本。业务方问,这个库迁过去,要多久。我说得看对象复杂度。对方追问,多复杂。我说几百个存储过程,里面啥写法都有。对方再问,那到底几个人几周。到这一步我就开始编了。拍个脑袋,报三个月,心里想的是最好能拖到四个月。
问题出在哪。出在没数据。你手里只有一堆模糊的印象,这个库挺老的,存储过程挺多的,有几段代码特别恶心。印象没法拿去排期,更没法拿去扛责任。有次我报了三个月,做到第二个月发现有个排产过程两千三百行,游标套游标,中间还夹着动态 SQL 拼 MERGE,光理清主干就花了两天。那项目最后超期了,超期的原因复盘来复盘去,就是评估阶段根本不知道库里有这种货色。
这事还牵扯到一个更麻烦的点,就是兼容这个词本身。
兼容度这个词,先得掰扯清楚
市面上一说国产库兼容 Oracle 兼容 MySQL,听着都挺唬人,但兼容度到底怎么算出来的,各家口径不一样。我在论坛上翻过不少帖子,同一个库,有人说兼容度九十多,有人说不兼容的地方多得吓人,吵得不可开交。后来我琢磨明白了,不是谁撒谎,是大家量的东西不在一层。
我自己习惯把兼容掰成三层看。最底下一层是"建得起来",DDL 能跑,对象能创建,存储过程能编译通过。中间一层是"跑得对",语义等价,结果跟原来一模一样。最上面一层是"跑得快",性能不掉链子。
工具类评估,包括 KDMS 这种,能量化的其实主要是第一层。它把源库对象抓过来,在目标库上做转换和编译验证,编译过了算兼容,编译报错算不兼容。这个界定不完美,我很清楚它的局限,第二层的语义等价它测不了,比如两个函数空值处理行为差一点点,语法上谁都没错,结果就是对不上。第三层性能更不用想,评估环境里根本没有真实流量。
但话说回来,第一层难道就没价值吗。不是的。我宁可要一个明明白白的第一层数字,也不要一句"基本兼容,问题不大"。前者我至少知道有多少对象连编译都过不去,后者就是一句空话。所以我对评估工具的定位一直很明确,它给不了全部真相,但它能把最硬的那部分骨头先啃出来,剩下的靠人工补。
这个认知是吃亏吃出来的。而且这个界定本身还有个绕不开的问题,评估工具算兼容度的时候,分子分母怎么定,是按对象个数算,还是按 SQL 语句条数算。同样一个库,一百个表一个不兼容,十个过程五个不兼容,按对象算兼容度 95%,按过程这个维度看就只剩一半了。数字这个东西,算法一变,面貌全变。所以后面看到报告里的百分比,我的习惯是先翻到明细去看统计口径,分类型看了心里才踏实。这个后面还会提。
往下倒几年,这活儿全靠手工。
早几年怎么评估,时间线捋一捋
最早那会儿,大概五六年前,没人拿工具评估。大家的办法土得掉渣。连上源库,把 dba_objects 之类的视图查一遍,数一数有多少表多少过程,Excel 里一填。然后 grep 关键字,Oracle 的库就搜 MERGE、ROWNUM、CONNECT BY 出现次数,MySQL 的库就搜 ON DUPLICATE KEY、GROUP_CONCAT,数出来个大概,乘以一个拍脑袋系数,就是工期。
sql
-- 那时候的土办法,大概长这样
SELECT object_type, COUNT(*) AS cnt
FROM dba_objects
WHERE owner = 'MYTEST'
GROUP BY object_type
ORDER BY cnt DESC;
这个方法的毛病显而易见,只数了数量,没看内容。三百个存储过程和三百个存储过程,难度能差十倍,关键看里面写了啥。而且那时候的统计粒度也糙,表和过程混在一个数里,你没法回答"不兼容的都在哪类对象上"这种问题。业务方其实就要这么个答案,表好办还是过程好办,直接决定改造重点压在 DBA 身上还是压在开发身上。土办法给不出,只能含糊说都有一些。这种含糊在立项会上就是被追着打的软肋。
后来大家学聪明了,开始抽样,把存储过程按行数排序,挑最长的十个打开人工看。
sql
-- 按行数排序找硬骨头,这个土办法我到现在还留着
SELECT name, line
FROM dba_source
WHERE owner = 'MYTEST'
GROUP BY name
ORDER BY line DESC
FETCH FIRST 10 ROWS ONLY;
这招我到现在还在用,说实话某些时候比工具的数字还准,因为你看到的是真实的代码长相,恶心程度一目了然。但抽样的软肋同样明显,看十个未必代表三百个,万一第十一行开始全是另一种流派呢。统计学的常识,样本外的风险永远存在。所以土办法和工具不是替代关系,是互为补充,工具给全量,人去看典型。
再往后,厂商们开始做工具化的评估。这背后有个大势,替换项目多了,实施团队就那几个人,不可能每个项目都扑上去人工数对象。工具化的思路也分两派,一派是做静态扫描,扫代码扫对象出报告,另一派偏重在线测试,拿一堆典型 SQL 到目标库上跑分。两派各有道理也各有盲区,静态扫描覆盖全但深度浅,在线测试深度够但样本有限。论坛上这俩路线的支持者也吵,我的看法是别二选一,覆盖面的问题用扫描解决,关键路径用测试解决,评估报告只是起点不是终点。
KDMS 走的是前一条路线,采集加静态评估。全名挺长,叫金仓数据库迁移评估系统,下面我全用简称。它整体分三块,数据库采集,应用采集,兼容度评估。这个产品定位说白了就是把评估这步从"老师傅带徒弟"变成"人人可复制的流程",老师傅的经验编码成了采集范围和评估规则,换个新人来也能跑出一份像样的报告。当然报告的解读还是得靠人,这是后面下篇要说的重点。下面挨个说我上手的体验。
部署:比想象中轻
先说部署。这东西跑在 Linux 上,资源要求不算高,内存 16G,软件包占 2G 磁盘,安装路径再留 5G,就这些。我们拿了一台虚拟机就装了。建议用独立账号跑,别拿 root 或者公用账号凑合。
bash
shell# adduser kdms
shell# su - kdms
有个小坑提醒下,默认安装目录是 /opt/KDMS,你要直接用 kdms 这个用户装,装到一半会提示没权限,因为 /opt 下建目录的权限不在它手里,得提前把目录授权给这个用户。访问端口默认 19007,跟机器上已有的服务撞了的话,安装过程中可以改。装完先做个功能验证,看下日志,别急着连生产。
整体感觉这工具不是那种笨重的企业软件,采集端和评估端是分开的,采集客户端可以放到源库那边的机器上跑,评估系统单独部署。这个分离设计后面会说到它的好处。
好处其实一句话就能说明白,采集包是隔空传话的载体。很多替换项目,源库在内网 A 区,评估的机器在 B 区,中间隔着防火墙甚至物理隔离,你不可能让评估系统直连生产库。分离之后,采集器进 A 区拿包,人把 zip 拷出来,传到 B 区的评估系统上传,链路就通了。数据不出区,包能出区,安全部门那边也好交代。要是评估系统直连源库的方案,光安全评审就能卡你半个月。我这次是测试环境无所谓,真到生产那就是另一套流程了,所以提前把这种架构跑熟是有意义的。
采集:结构信息,不碰业务数据
重点说说采集,这是评估的地基。地基歪了后面全歪。
先说让我最安心的一条,KDMS 的数据库采集只抓结构信息,表、视图、触发器、约束、序列、函数、存储过程这些的 DDL,不读业务数据。对生产库来说这点太重要了。你想想评估阶段就要连生产库,DBA 第一个问题就是会不会影响业务,会不会拖数据出来。只采结构这个答案能把 DBA 的戒心放下一大半。哦对了,它还有个可选项,能顺手采每张表有多少条数据、占多大磁盘空间,这个是统计信息,也不碰数据本身,后面评估概要里会用到。
采集用户也别用业务账号。手册里明确建议建临时采集用户,采完就删。Oracle 这边我实际用的授权,大概这么一堆。
sql
-- 建采集用户,给权限,采完删掉
create user KINGBASE_USER identified by "kingbasePASSWORD"
default tablespace USERS;
grant connect, resource, select_catalog_role,
select any dictionary to KINGBASE_USER;
grant execute on DBMS_LOGMNR to KINGBASE_USER;
grant execute on dbms_metadata to KINGBASE_USER;
grant select any transaction to KINGBASE_USER;
grant analyze any to KINGBASE_USER;
grant EXP_FULL_DATABASE to KINGBASE_USER;
能看到这些权限对应啥吗。DBMS_LOGMNR 是挖日志的,DBMS_METADATA 是抽对象 DDL 的,EXP_FULL_DATABASE 是为了采 DBLINK 结构。每个权限都有明确的用途,这点我喜欢,不是让你直接 root 级别梭哈。要是 Oracle 12c 的 CDB 架构,还有点麻烦,得连到 CDB 上建 COMMON USER,用户名得带 c## 前缀,授权全要加 container=all,踩过一次坑就记住了。
sql
-- 12c CDB 模式,用户名前缀 c## 不能少
create user c##KINGBASE_USER identified by "kingbasePASSWORD"
default tablespace USERS;
grant connect, resource, select_catalog_role,
select any dictionary to c##KINGBASE_USER container=all;
grant execute on dbms_metadata to c##KINGBASE_USER container=all;
grant select any table to c##KINGBASE_USER container=all;
支持的源库类型,Oracle、DB2、MySQL、SQL Server、Sybase,主流的都覆盖了。新建采集的时候填 IP 端口用户密码,然后选对象类型。Oracle 那边能选的对象类型列出来感受下,INDEX、SEQUENCE、SYNONYM、TABLE、VIEW、MATERIALIZED VIEW、TYPE、TYPE BODY、FUNCTION、PACKAGE、PACKAGE BODY、PROCEDURE、TRIGGER、DATABASE LINK、JOB、CONTEXT。十六种,比我手工评估时看的全。以前我就看表和过程,SYNONYM 和 TYPE 这些经常漏,漏了之后迁移时就出幺蛾子。
有个细节,Oracle 填 SID 还是服务名,RAC 环境必须选服务名,填 Schema 的时候注意大小写。这种小地方手册里都会提一句,但真上手的时候十个人有八个第一次就栽在这,我算其中一个。还有采集耗时的事,我这个测试库两千来个对象,从开始到采集完成一分半钟,进度条到 100% 就能下载采集包了。包很小,压缩前不到 1M。当然这是结构信息的量级,要是勾了数据量统计会稍微大一点,但也就几十 M 的级别,跟数据本身比可以忽略。别小看这点,有的评估方案要把数据导出来搭影子库再测,几 T 的库光倒腾一遍就得几天,评估还没开始成本先堆上去了。
顺便说下采集包里的结构。解压开是个按时间戳命名的文件夹,里面是各对象的 DDL 文件,再配上前面说的那个 meta 文件。这里有个坑,COLLECTOR_META.dat 记录了这次采集的元信息,建评估项目的时候要校验它,不存在或者不合法就建不了。所以不同库的采集包别混着用,也别拿一个 meta 文件到处复制,老老实实一次采集一个包。我一开始没当回事,两个库的包放一个目录里改名混用,结果建评估项目直接报错,翻日志才发现是 meta 校验没过。规矩就是规矩,人家这么设计也是为了防止张冠李戴,评估一个库的结果挂在另一个库头上,那报告就成废纸了。
历史SQL采集:从 shared pool 里捞真实语句
上面说的是对象采集,评估的是库里"住着"的东西。但还有一类东西对象采集看不见,就是应用实际跑的 SQL。老系统的 SQL 一半在存储过程里,还有一大半在应用代码里拼着,光看库不知道业务到底怎么用它。
KDMS 给了两条路补这块。一个叫历史 SQL 采集,原理挺有意思,走 Oracle 的 v$sqlarea 视图。
sql
-- 采集器本质上是查这个视图,字段说明
SELECT SQL_FULLTEXT, -- SQL 完整文本,含绑定变量
LOADS, -- 加载进共享池的次数
LAST_ACTIVE_TIME, -- 最后活动时间,用它控制采集范围
COMMAND_TYPE, -- 语句类型 SELECT/INSERT/...
PARSING_SCHEMA_NAME -- 执行时用的模式名
FROM v$sqlarea;
这个视图持续跟踪 shared pool 里所有共享 cursor,跑过的 SQL 基本都在里面躺着。好处是不用改应用不用重启,坏处也明显,shared pool 是滚动的,老 SQL 会被挤出去,所以得定期采,LAST_ACTIVE_TIME 就是拿来圈时间范围的。这个思路不是它一家独有,但做成工具自动跑确实省事。
采这个有什么用,我举自己的例子。有个老系统,库里的存储过程看着风平浪静,评估兼容度也高,但业务方死活不敢说应用不用改。为什么,因为应用里 Mybatis 拼的 SQL 有一千多句,全在 java 代码和 xml 里,你评估库对象等于只看了半张地图。这时候历史 SQL 采集就顶用了,把 shared pool 里捞出来的真实语句跑一遍评估,哪些写法在目标库上会翻车,列得清清楚楚。比翻代码一页页人肉看强太多。当然它也有覆盖盲区,刚跑完一批不常见的报表 SQL,shared pool 还没来得及淘汰,正好在你采集窗口里,它就被抓到了,反过来错过窗口的就成了漏网之鱼。所以真要较真,就得在业务高峰后采一次,月底结账后再采一次,多采几轮拼全貌。
另一条路是应用采集,分静态和动态。静态的扫代码,把 Java 项目里的 Mapper xml 文件和 SQL 脚本打包成 zip 传上去,选好语法类型就开扫。动态的更狠,挂到运行中的应用上,采实际执行的 SQL 加调用堆栈。我这次只试了静态的 Mapper 扫描,动态那个需要在应用侧动东西,得开发配合,评估阶段还没走到那步。
踩坑记录
采集环节报错不少,捡几个典型的。先说结论,这环节的报错九成都是环境和权限问题,跟工具本身关系不大,但每个都得耗你一阵。Oracle 那边最容易碰 insufficient privileges,就是采集用户权限没给够,回去对着授权清单补,补完重试就好,最气的是它不会一次告诉你要哪些权限,缺一个报一个。MySQL 那边我碰到过两次报错,一次是 Latin1 is not supported,字符集的事,源库建库用了 latin1,得先统一到 utf8 再采,这个没绕的办法,老库改字符集本身就是个课题。另一次是 CLIENT_PLUGIN_AUTH is required,这个是驱动版本对不上,老驱动连新协议不支持,换个高版本的驱动包就好了。SQL Server 的坑更刁钻,连接报 18456 登录失败,查了半天发现是实例认证模式的问题,改完又冒出来一个 SetARITHABORT 不正确,执行采集语句前得把 ARITHABORT 设置对齐,还有个"备份集中的数据库备份与现有的数据库不同"的报错,那是拿错备份还原的库,元数据对不上,这种属于自己坑自己。一个个都解决了,但过程确实烦,建议采集前先拿个小库把流程走通,别一上来就连生产。
测试连接失败这种通用报错就不用说了,先查网络和端口。
采集完就到评估了。把采集包上传,系统自动识别出源库类型和兼容模式,评估名称带出来可以改,然后选目标版本,点确认,评估任务就跑起来了。如果部署的时候装了评估插件,采集完成页面上会直接给个"立即评估"按钮,一步到位,省得下载再上传那个 zip。
采完了顺手还试了个小功能,在线评估。评估菜单下面单独一个入口,进去就是一个 SQL 编辑页,选好目标版本和兼容模式,把一句话贴进去,立刻告诉你这句在目标库上能不能跑。这功能看着不起眼,评审会上特别好使。有次开发质疑某个写法迁过去行不行,我当场贴进去跑给他看,比嘴上争十分钟管用。当然它也是只验编译不验语义,这个前面说过的局限在这里同样成立,别迷信。
评估跑完进详情页,第一眼看到概要,兼容数量,不兼容数量,还有一个大大的兼容度百分比。我看到那个数字的第一反应不是高兴,是犯嘀咕,这数字怎么算的,可信吗,能直接拿去汇报吗。
而且这轮评估我还发现一个此前没细想的点,评估对象是死的,人是活的。同一个库,这周采一遍和下周采一遍,结果可能就不一样,因为开发可能又改了两个过程。评估报告是有时效的快照,不是一锤定音的判决书。所以我的做法是把评估放在对象冻结之后,跟开发约好,评估窗口期内谁也别动库,评估完再解冻。这么点小事,但它决定了报告的严肃性。
这些疑问的答案,还有那份评估报告到底长啥样、细到什么程度、怎么拿它算工作量,放下篇细说。采集这块最后再啰嗦一句,临时用户采完记得删,别嫌麻烦,留着就是留风险。