前言:迁移不是搬家,是做手术
很多人把数据库迁移类比成搬家------把东西打包、运过去、再 unpack。但真正做过生产环境 SQL Server 迁移的人都知道,这个比喻害死人。搬家可以接受短暂的秩序混乱,可以忍受几天找不到东西。数据库迁移不一样,生产库一旦出问题,业务就是停摆,损失按分钟计。
我参与过大大小小十几次 SQL Server 迁移,从 2008 升级到 2016、跨版本的数据迁移、主从架构调整、甚至是实例层面的 OS 级别迁移。每一次都踩过不同的坑,也因此对「迁移到底要注意什么」有了比文档更实在的理解。这篇文章,就是把这些经验摊开来,不求面面俱到,但求说到点上。
一、迁移前的评估:问题在动笔之前就已经埋下了
1.1 为什么要迁?目标要非常清楚
很多人迁移是因为「版本太旧了」或者「领导说要升」。这个理由不充分。迁移是一项高风险操作,如果没有明确的目标,就像没有靶心就开枪,打得再准也没意义。
常见的迁移动机应该是可量化的:比如当前版本在 xxxx 年终止支持、现有硬件无法支撑业务峰值、某个新版本特性是业务急需的(比如 Always Encrypted、查询存储等)、或者审计要求必须满足特定合规级别。只有目标清晰,迁移方案的设计才有取舍的依据。
1.2 兼容性审计是第一步,也是最重要的一步
这一点怎么强调都不为过。我的建议是:在动手之前,先跑 Microsoft Data Migration Assistant (DMA)。这个工具会扫描你的源数据库,识别出迁移到目标版本时会遇到的兼容性问题,包括:
- 不支持的语法(比如旧版的一些 SET 选项)
- 被废弃的功能(deprecated features)
- 功能行为变化可能带来的影响
但我必须提醒的是:DMA 的报告要仔细看,但不要盲目全信。它会报出一些「警告」,有些实际上是 benign 的。比如某些查询在兼容级别降低时会触发警告,但你的业务逻辑根本不会触发那些路径。我见过有人被 DMA 报告吓得不敢动,最后花了两周去「修复」一堆根本不是问题的警告。拿到报告后,先按严重程度分类,和开发团队一起 review 每个警告的业务影响,再决定要不要处理。
1.3 数据量的真实评估
我第一次做大表迁移时,低估了数据量的影响。当时觉得 200GB 数据量不大,结果由于没有考虑表的具体分布------一张分区表占了 180GB,索引碎片率超过 85%------实际迁移时间超出了预估的三倍,差点影响业务恢复窗口。
正确的做法是:
- 用 sys.dm_db_partition_stats 拿到每张表精确的 row count 和占用空间
- 识别大表(超过 10GB 或行数超过千万级的) 并单独评估迁移策略
- 检查索引碎片率 ------高碎片率会导致大批量 INSERT 时性能急剧下降
- 了解业务高峰时间窗口------迁移窗口最好避开业务高峰
二、迁移方案的选择:没有银弹,只有权衡
2.1 备份/还原 vs. 复制迁移
SQL Server 迁移最常见的两条路:备份还原 和复制迁移(通过 Log Shipping、Always On、或者第三方工具)。各有各的适用场景。
备份还原的优势是简单可靠,适合数据量不太大(100GB 以内)、可以接受较长停机窗口的场景。但它有几个坑:
- 如果使用了 TDE(透明数据加密),还原前要确保目标实例有正确的证书和密钥,否则数据无法访问
- 备份集跨版本时存在限制:比如 SQL Server 2012 的备份不能直接还原到 SQL Server 2008,即使版本相近也要注意兼容级别
- 压缩备份在某些情况下会掩盖数据页损坏,还原后才发现问题。建议在还原后跑一次 DBCC CHECKDB,养成习惯,不要偷懒。
复制迁移(Log Shipping / Always On)是生产环境推荐的方式,优势是停机窗口可以做到极短(几分钟级别),且可以在真正切换前做多次演练和验证。但它对网络带宽有要求------如果源库和目标库之间的网络延迟高或者带宽低,同步延迟会成为噩梦。实践中我发现,有些团队低估了生产环境的网络复杂度,特别是在混合云或者跨机房的场景下。
2.2 异机迁移 vs. 原地升级
异机迁移(也就是在新的服务器实例上重新搭建)是目前的主流推荐,原因是:
- 可以并行运行新旧系统,有充分的验证时间
- 出现问题可以快速回滚
- 趁迁移机会清理掉历史累积的垃圾数据和不合理架构
原地升级 (In-place upgrade)看起来最省事,但我在吃过亏之后基本不推荐。原因很简单:一旦升级失败,你没有可以快速回退的环境,升级过程本身不可逆。如果非要做原地升级,强烈建议先在虚拟机上完整模拟一次升级过程,包括所有补丁、配置变更,然后再在生产环境操作。
三、Schema 迁移:最容易出问题的灰色地带
3.1 兼容级别(Compatibility Level)不是小事
很多团队迁移后直接让数据库以默认兼容级别运行,这没问题------但要清楚知道你在用的是多少。SQL Server 2016 开始默认兼容级别是 130(对应 SQL Server 2016),而如果你的应用设计上针对的是 110(SQL Server 2012)的行为,混用可能会导致查询结果不一致。比如:
- 字符串排序规则在某些语言设置下行为不同
- 基数估算(Cardinality Estimation)模型差异可能导致执行计划迥异
- 某些聚合查询在 CE 模型切换后返回结果顺序不同(虽然标准上说 ORDER BY 是必须的,但实际代码里不写 ORDER BY 的场景太多了)
建议迁移后先用兼容级别保持原状跑一段时间,让应用充分验证,确认无误后再逐步提升兼容级别,而不是一次性拉满。
3.2 链接服务器(Linked Server)和权限的重新映射
如果源环境里有大量的 Linked Server 配置,这些信息不会随着数据库迁移自动生效。你需要:
- 重建 Linked Server 配置
- 特别处理使用 SQL Server 认证的 Linked Server------密码存在源实例的 registry 里,明文导出要小心安全风险
- 验证远程连接权限是否正确
还有一点容易被忽略:数据库所有者(owner)变更后,某些 stored procedure 的权限可能出问题 。比如某个 SP 用 EXECUTE AS 写了特定用户,迁移后这个用户可能不存在了,导致 SP 编译失败或运行时权限错误。建议迁移后执行一次 EXEC sp_change_users_login 'Report' 来检查孤立用户。
四、性能回归:迁移后才是真正的考验
4.1 执行计划是最大的不确定因素
SQL Server 的查询优化器在不同版本间会有变化,同样一条 SQL,在旧版本用得好好的执行计划,迁移后可能变成全表扫描。这不是 Bug,是正常行为,但如果没有主动检测,线上会突然变慢。
我的做法是:迁移前,用 Query Store(2016 及以上版本)或第三方工具(如 Redgate SQL Monitor)抓取一批高频查询作为基准。迁移后,拿同一批查询的实际执行计划做对比。如果某条 SQL 迁移后的执行计划明显变差,先不要急着加 hint,先分析背后的原因------是因为统计信息过期?是因为索引丢失?还是基数估算器模型变化?找到根因再动手。
4.2 统计信息迁移的特殊性
这里有一个很多人不知道的点:SQL Server 的统计信息(statistics)是绑定在数据库级别的,理论上迁移数据库时统计信息会一起过来。但长时间运行的库,统计信息的采样可能已经高度偏斜 ,迁移后优化器拿到的可能还是「旧照片」。建议迁移完成后,在关键大表上手动 UPDATE STATISTICS 一次(带 FULLSCAN 选项,如果窗口允许的话)。
4.3 参数化嗅探(Parameter Sniffing)问题
这个问题在迁移后特别容易爆发。很多存储过程依赖参数嗅探来生成高效执行计划,但一旦数据分布发生变化(比如表在迁移前做了一次大批量导入),同样的参数可能产生完全不同的计划。经典表现就是:迁移后某些 Stored Procedure 在某些参数值下性能极差,而其他值下正常。如果你的业务有这种特征,迁移后要做压力测试,而不是只跑功能验证。
五、切换窗口的管理:计划赶不上变化
5.1 切换前的最后一公里
即便前面的准备工作都做得很充分,切换窗口那段时间依然需要极度专注。我的经验是:
- 提前写好切换 checklist,包括每一步的操作人和预计时间
- 建立回滚标准:什么情况下必须回滚?这个标准在开始前就要明确,不能等到出了问题再现场讨论
- 在切换窗口前做一次「模拟切换」------把整个流程在没有生产压力的情况下跑一遍,特别是对那些有 cron job、定时任务的系统,定时任务在切换期间的触发逻辑必须提前验证
- 保留迁移前最后的 log backup,切换完成后,立即做一个 tail log backup,防止切换过程中产生的数据丢失
5.2 切换后的验证:不要相信「应该没问题」
切换完成后,业务团队的验证不能只停留在「页面能打开」。至少要验证:
- 核心写入流程:从应用写入一笔数据,能读回来
- 报表和聚合查询:结果和迁移前一致
- 连接池和应用池:应用服务器能正常建立连接(有些应用在连接字符串改变后有缓存问题)
- 定时任务和批处理:在业务窗口内触发一次确认能正常执行
六、迁移到国产数据库------以金仓数据库为例
这部分值得单独说一说,因为国内现在有大量 SQL Server 迁移项目,实际方向不是跨版本升级,而是异构迁移到国产数据库,金仓数据库(KingbaseES)是其中比较常见的选择之一。
为什么会出现这种迁移需求
政策层面,信创要求(信息技术应用创新)是一个重要推动力。很多政府单位、金融机构、大型企业被要求在限定时间内完成核心系统的国产化替代。技术层面,金仓等国产数据库这些年成熟度提升明显,兼容 Oracle 和 SQL Server 的能力也在增强,不再是从零起步。
但现实情况是:这种迁移的难度远高于同类型数据库的版本迁移。SQL Server 到 SQL Server 迁移,底层存储引擎是一致的,兼容性好很多。而 SQL Server 到国产数据库,在 SQL 语法细节、数据类型精度、存储过程语法、事务隔离级别实现等方面都有差异。
迁移到国产数据库时的几个特殊注意点
语法适配是最耗时的工作。 SQL Server 的 T-SQL 扩展非常丰富,大量业务代码用了 proprietary 的语法,比如 PIVOT/UNPIVOT、CTE、IDENTITY 的实现方式、sequence 的差异、临时表和表变量的行为差异等。
建议的做法是:先用 如金仓的KDMS 之类的迁移评估工具做一轮自动扫描,它能把一部分语法做自动转换,但剩余的手动工作量往往比预期大。我的经验是,自动化工具大概能覆盖 60%~70% 的语法转换,剩下的 30%~40% 需要人工介入。这部分工作量在项目排期时很容易被低估。
数据类型差异要特别关注。 SQL Server 的 datetime2、time、datetimeoffset 等数据类型在金仓里的对应关系要搞清楚,特别是精度问题。另外,SQL Server 的 uniqueidentifier(GUID)在金仓里如何映射,以及排序规则(Collation)的处理,都是需要注意的点。
存储过程和函数的改写是另一块硬骨头。 如果业务逻辑重度依赖 SQL Server 的 stored procedure,改写工作量不亚于重写。这里没有捷径,只有老老实实逐个审查。
说一句实在话
信创迁移的时间窗口通常非常紧,但越是这种时候越要清醒。很多信创迁移项目的失败,不是在技术上做不到,而是因为时间压力导致质量把控不到位。 语法改写不完整就上线、测试覆盖不足就切正式库------这些问题在信创项目里我见过不少。
如果你正在规划或参与这样的迁移,建议在项目前期就把「兼容性改写」和「充分测试」的时间单独列出来,不要让这两块被其他工作挤占。迁移工具能加速,但替代不了人仔细审核每一条业务逻辑。
七、总结:迁移是系统性工程,不是技术活
SQL Server 迁移表面上是一项数据库技术工作,实际上是一项系统性工程。它考验的不只是 DBA 的技术能力,更考验项目管理能力、跨团队沟通能力,以及对业务系统整体的理解。
最后说一句:很多坑不是技术问题,是人的问题。迁移方案定了,开发团队说「我们没问题」,测试团队说「我们测完了」,结果上线后问题一堆------不是因为技术不过关,而是因为没有人真正负责端到端的验证。找一个明确的 migration owner,这个人要对整个迁移负责到底。这是所有注意事项里,最务实的一条。