一、先说背景,一台SQL Server撑了八年
我在一家连锁零售企业干活,职位写的是DBA,实际数据库的活我干,后端的锅我也背,公司人少,没那么多讲究。手里这套ERP跑了八年,订单、库存、会员三个大模块,外加十来张BI报表,底座是SQL Server 2016标准版,数据1.4TB,表423张,存储过程136个。
去年11月,两件事撞一块了。一件是SQL Server按核数授权的续约费用又涨了,采购把报价单转给我的时候附了三个字:"你看看"。另一件是公司层面下了国产化替代的通知,年底前完成,白纸黑字。
领导把我叫进会议室,原话大概是:"选型你来定,出了事你负责。"我点头点得挺痛快,走出会议室腿有点软。
不是怕迁移本身,是怕性能翻车。BI报表月底高峰本来就慢,业务群里天天有人@我,截图配文"这报表是在转圈还是睡着了"。这要是换完库更慢,我真没法做人。
@TOC
二、迁移前夜,我打鼓的三个地方
选型最后定了金仓数据库,KES V9R4C019,SQL Server兼容模式。中间POC测了两轮,过程不啰嗦,就说拍板前我自己心里过不去的坎。
最愁的是那136个存储过程。MERGE、OUTPUT、TOP配ORDER BY,什么写法都有,还有一坨是离职同事留下的,注释一个字没有。真要人肉改语法,这活儿看不到头。
再一个就是性能,尤其是标量子查询。我们一堆报表SQL在SELECT列表里挂子查询,在SQL Server上靠索引硬扛着,属于数据量再涨涨就要出事的写法。换个引擎还扛不扛得住,没人敢打包票。
还有件事我没好意思跟领导说:半夜库挂了咋办。SQL Server我们熟,闭着眼都能处理;换了新库,排障、备份、恢复全得从头学。这个怕,说出来显得自己没本事,只能自己消化。
三、方案怎么定的
方案不复杂,电科金仓的工具链分工挺清楚。KDMS管前期评估,连上源库采集对象,出评估报告,能自动转的语法直接转,转不了的给改写建议。数据搬迁用KDT和KFS,全量加增量同步,最后在停机窗口里追平、校验。切换前还有一道真实流量回放,把生产流量录下来在目标库跑一遍,坑提前踩。
KDMS报告出来那天,我盯着看了半个多小时。136个存储过程,128个标了可自动转换,剩8个要人工------全是动态SQL拼EXEC的,工具确实啃不动,手工搬。1.4TB全量过下来,比预想的顺。
后来给领导汇报,工作量汇成这么一张表:
| 对象类型 | 总数 | 自动转换 | 人工处理 | 备注 |
|---|---|---|---|---|
| 表 | 423 | 423 | 0 | 含6张分区表 |
| 存储过程/函数 | 160 | 152 | 8 | 动态SQL手工搬 |
| 视图 | 58 | 57 | 1 | 有个TOP 100 PERCENT的写法 |
| 索引 | 310 | 296 | 14 | 顺手清了批无效索引 |
| 自增列 | 46 | 46 | 要手工重置序列 | 埋了坑,后面细说 |
表打出来我自己先看了一遍,别的行都挺体面,就最后一行那个备注,写的时候心里就咯噔了一下。你们猜怎么着,还真就它炸了。
四、踩坑实录
自增列断号,功能测试第一天就给我下马威
全量数据迁完,功能测试第一天下午,下单接口报主键冲突,duplicate key。
我第一反应是迁移工具丢数据或者搬重了,拉着同事核行数核了快半小时,一条不差。那冲突哪来的?
接着看报错那条数据。插入的订单ID是10583万,表里最大ID是10582万,下一个号就该是10583万,没毛病啊?再往下抠才发现,问题不在表里,在序列上。兼容模式下自增列是走序列发号的,序列的当前值还停在初始位置,压根不知道表里已经发到一亿多了。数据是数据,序列是序列,工具把数据搬过来了,序列不会自己跟着涨。
修起来不难,拿表内最大值把序列顶上去就行:
sql
-- 先看序列当前值,果然还在1附近
SELECT last_value FROM t_orders_order_id_seq;
-- 用表内最大值重置序列,false表示下一个号从max+1开始发
SELECT setval('t_orders_order_id_seq',
(SELECT COALESCE(MAX(order_id), 1) + 1 FROM t_orders),
false);
46张带自增列的表,我写了个脚本遍历跑完,前后十来分钟。但这事教训挺深:校验别光核行数和checksum,序列、默认值这类元数据也得进核对清单。后来我把它写进我们组的迁移检查单了,排第七条。
统计信息这坑,差点让全组心态崩了
第二个坑藏得深,杀伤力也大。
切换前做100并发压测,第一轮TPS跑出来141。基准多少?SQL Server上是235。六成。会议室里安静了几秒,后排有人小声来了句"果然不行"。说实话我血压也上来了,但骂库没用,拉执行计划吧。
EXPLAIN一出来就看到问题了,一条核心关联查询的索引走错了,明明有现成的组合索引,优化器偏偏选了另一个。看了半天没想通,下午四点多打电话给原厂支持,对方听完就问了一句:统计信息收集过吗?
......对啊,收集过吗?没有。
新库刚建好,统计信息要么是空的要么抽样极粗,优化器跟蒙着眼选路没区别。赶紧补:
sql
-- 全库收集统计信息
ANALYZE;
-- 重点大表单独再来一遍,让优化器看清数据分布
ANALYZE t_orders;
ANALYZE t_deal_price;
ANALYZE t_member;
再压,TPS从141直接拉到302。一行代码没改,就是让优化器把数据看清楚了。这课我记到现在:换了库先ANALYZE,再谈性能,顺序不能反。
标量子查询,真正的刺客藏在SELECT列表里
统计信息补完,整体达标了,但离官方说的"100并发复杂查询TPS提升60%"还差一截。我们卡在330上下,对235的基准,提升也就四成。
卡在哪?就是前面说的那条含标量子查询的多表关联查询。报表SQL大概长这样,简化过的:
sql
SELECT o.order_id,
o.customer_id,
o.amount,
-- 每行都去查一次"该会员最近成交价",行数一大就是灾难
(SELECT TOP 1 p.deal_price
FROM t_deal_price p
WHERE p.customer_id = o.customer_id
ORDER BY p.deal_time DESC) AS last_price
FROM t_orders o
WHERE o.order_date >= '2026-06-01'
AND o.status = 2;
这种写法数据量小的时候真没毛病,涨起来之后等于每行订单都单独跑一次子查询。SQL Server当年也慢,靠索引硬扛;换了引擎,同样的坑换了个姿势疼。
这次帮上大忙的是金仓内置的QueryMapping。它能做模糊匹配,把长得像的SQL归到一起比执行差异。不查不知道,"查会员最近成交价"这一个逻辑,各分支团队写出了十几个变体,散在七八张报表里,各有各的慢法。工具直接给了改写建议,统一改成LEFT JOIN派生表加窗口函数:
sql
SELECT o.order_id, o.customer_id, o.amount, t.last_price
FROM t_orders o
LEFT JOIN (
SELECT customer_id, deal_price,
ROW_NUMBER() OVER (PARTITION BY customer_id
ORDER BY deal_time DESC) AS rn
FROM t_deal_price
) t ON t.customer_id = o.customer_id AND t.rn = 1
WHERE o.order_date >= '2026-06-01'
AND o.status = 2;
再补两个索引:
sql
CREATE INDEX idx_dealprice_cust_time ON t_deal_price (customer_id, deal_time DESC);
CREATE INDEX idx_orders_cust_date ON t_orders (customer_id, order_date);
这一批改完,TPS稳在376,对SQL Server基准正好提升60%;平均响应从4.2秒压到0.42秒,十分之一。跟官方发布的实测数据基本咬上了,我心里那块石头才算落地。
五、性能数据
切换后跑完一个完整月底高峰,跟切换前的压测放一起:
| 场景 | SQL Server 2016 | 金仓V9R4C019 | 变化 |
|---|---|---|---|
| 100并发复杂查询TPS(含标量子查询) | 235 | 376 | +60% |
| 同场景平均响应时间 | 4.2s | 0.42s | 约1/10 |
| 同场景P95响应时间 | 9.8s | 1.1s | 降了约89% |
| 月度销售汇总报表(单条大SQL) | 8分36秒 | 4.2秒 | 122倍左右 |
| 月底结账整体窗口 | 5.5小时 | 2.1小时 | 缩短62% |
这表看着提气,但我得说两句实话,不然你们真以为换库跟点外卖一样简单。
首先,"报表秒出"不是迁完自动就有的。刚迁完没调优那阵子,那张月度汇总报表要跑3分多钟,比SQL Server还慢。是补统计信息、清无效索引、改标量子查询三板斧全下去,才降到4秒出头。性能是调出来的,不是迁出来的,这话我现在跟谁都说。
另外业务那边的感受最直接。以前月底出报表,同事点完就去泡咖啡,回来还在转;上个月有个小姑娘点完报表愣了一下,跑来问我是不是缓存了旧数据。月底结账窗口从5个半小时压到2小时出头,财务经理专门跑IT这边道了个谢。干DBA八年,头一回。
六、兼容性这事,版本差很多
复盘的时候我反复跟同事念叨,这次顺,有一半要记在版本上。V9R4C019是SQL Server兼容版的一个大节点,好几个能力刚好在这版补齐。
我们日结对账的存储过程重度依赖MERGE,出库单模块用OUTPUT捕获变更写审计日志,这两块在老版本里得人工改写,C019原生支持,存量T-SQL连上去就跑,一行没动。
还有个差点闹笑话的。搜索功能里有句LIKE '[0-9]%',字符集通配符,评估阶段在旧版本上跑报错,当时差点被我当成"兼容性硬伤"写进报告。结果C019直接支持了。所以POC一定拿目标版本测,别拿旧版本下结论,冤枉了产品也耽误自己。
窗口函数、PIVOT/UNPIVOT这些也是这版全面支持的,报表组的同事基本没来找过我的麻烦,这在以前不敢想。
运维侧也有惊喜。内置的故障收集分析工具能自动抓日志、出诊断报告,上线后出过一次连接数异常,从定位到处置5分钟。块级增量备份加PITR最优路径恢复,备份窗口从小时级压到分钟级。这种窗口,原来在SQL Server上我们想都不敢想。
七、最后聊几句
整个项目从KDMS评估到正式切换,现场三周。正式切换是周六晚上,KFS增量追平加数据校验,总停机25分钟。到现在跑过三个月底高峰,稳的。
要说过来的经验,有这么几条,说给后面要做SQL Server数据迁移的同行。
评估一定用对版本。兼容性结论跟版本强相关,C019这种节点版本,可能直接把你改造清单砍掉一大半。
校验清单里加上序列和默认值。行数对上了不代表能写入,自增列断号这种坑,要到第一次插入才爆。
换库后先ANALYZE,再谈性能。统计信息这种看不见的东西,最容易背锅。
标量子查询趁早改。它在哪家库上都是慢性病,趁迁移这个机会拿QueryMapping这类工具全扫一遍,一次把债还了。
给性能对比留够调优时间。裸迁即巅峰这种事不存在,先调完再下结论,对自己对产品都公平。
反正现在谁再问我SQL Server数据迁移能不能搞,我都说能搞。但把坑当成项目的一部分去排,别当成意外。亲眼看TPS从235干到376、响应压到十分之一之后,这话我说得有底气。