2. 版本与功能兼容性评估

大型数据库迁移升级中,最容易被低估的风险不是硬件或网络,而是“版本语义差异”。同一句 T-SQL 在 SQL Server 2012 上返回正确结果,在 SQL Server 2022 上可能因优化器行为变更而变慢,甚至因废弃功能被移除而直接报错。因此,版本与功能兼容性评估的目标不是简单比对版本号,而是回答三个问题:哪些功能已废弃或移除、哪些行为发生了静默变化、当前数据库兼容性级别是否掩盖了这些差异。

一、版本差异的三个层次

评估时应把差异拆成三个层次,避免混为一谈。

  • 引擎能力差异:新版本引入的新特性,如 SQL Server 2016 的 Query Store、2017 的图形数据库与自适应查询处理、2019 的智能查询处理与加速数据库恢复、2022 的 Parameter Sensitive Plan Optimization。这些是“可选收益”,不升级也能运行。
  • 废弃与移除差异:微软按“已弃用(Deprecated)→ 不再支持(Discontinued)”路径推进。例如 Database Mirroring 在 2012 起弃用、后续版本移除;全文检索中的旧式目录、旧版备份还原格式、部分系统存储过程(如 sp_dboption)均已不可用。
  • 行为变更差异:功能仍存在,但默认行为改变。典型如 SQL Server 2016 起 STRING_SPLIT 的兼容性级别依赖、2017 起 IDENTITY 缓存默认开启导致重启后跳号、2022 起基数估计模型默认使用 120 以上级别。

这三类差异的处置策略完全不同:能力差异可延后采用,废弃差异必须改造,行为差异必须通过测试验证。

二、兼容性级别:不是“开关”,而是“语义锚点”

数据库兼容性级别(Compatibility Level,简称 CL)决定查询优化器采用哪一代基数估计模型和部分语法语义。常见对应关系为:CL 100 对应 SQL Server 2008,CL 110 对应 2012,CL 120 对应 2014,CL 130 对应 2016,CL 140 对应 2017,CL 150 对应 2019,CL 160 对应 2022。

关键认知是:升级实例版本不等于升级兼容性级别。将数据库从 2012 实例还原到 2022 实例后,CL 仍保持 110,此时新优化器的大部分改进不会生效,但部分引擎级行为(如废弃功能移除)仍会立即生效。这会造成“看起来没变、实际已变”的错觉。

兼容性级别只保护部分查询优化行为,不能保护已移除的语法、已停用的系统对象和引擎级默认值变更。不要把它当作升级安全网。

三、升级阻断点识别方法

推荐使用 SQL Server 自带的升级顾问(Data Migration Assistant / Upgrade Advisor)做静态扫描,再结合动态验证。静态扫描可识别废弃语法、跨数据库引用、链接服务器、CLR 程序集、加密对象等。动态验证则需在目标版本上执行真实负载。

下面是一段用于盘点当前数据库兼容性级别与关键行为开关的查询示例:

-- 盘点数据库兼容性级别与状态
SELECT name, compatibility_level, collation_name, is_read_committed_snapshot_on
FROM sys.databases
WHERE database_id > 4;

-- 查找可能受基数估计模型影响的查询(示例:按 CL 判断)
SELECT DB_NAME() AS db_name,
       compatibility_level,
       CASE
         WHEN compatibility_level < 120 THEN '旧基数估计模型'
         WHEN compatibility_level < 160 THEN '中间模型,需回归测试'
         ELSE '新模型,关注 PSP 与 CE 反馈'
       END AS ce_model_hint
FROM sys.databases
WHERE database_id = DB_ID();

-- 检查已弃用功能使用痕迹(示例:旧式全文目录)
SELECT name, is_default
FROM sys.fulltext_catalogs;

执行后应形成一张“阻断点清单”,按严重程度分级:P0 为直接报错或数据不一致,如已移除语法、跨版本备份还原失败;P1 为性能显著退化,如基数估计变化导致执行计划翻转;P2 为行为差异但可接受,如标识列跳号、日期格式默认值变化。

四、典型阻断点与处置对照

阻断点常见版本表现处置建议
Database Mirroring2012 起弃用,后续移除相关 DDL 报错迁移到 Always On 可用性组
旧式全文目录2012 起弃用目录创建失败改用新版全文目录并重建索引
基数估计模型切换2014 起 CL 120+执行计划翻转、CPU 升高用 Query Store 对比计划,必要时用 USE HINT 或计划指南
IDENTITY 缓存2017 起默认开启重启后跳号业务若依赖连续编号,需显式关闭或改用序列
STRING_SPLIT 兼容性2016 起低 CL 下不可用提升 CL 或改用自定义拆分函数

五、评估流程与判断依据

建议按“盘点—扫描—验证—定级—决策”推进。盘点阶段收集实例版本、数据库 CL、排序规则、依赖对象;扫描阶段用 DMA 与脚本识别废弃项;验证阶段在目标版本还原副本并回放负载;定级阶段按 P0/P1/P2 分类;决策阶段确定是“直接升级、改造后升级、还是暂缓”。

判断是否可升级的核心依据有三条:是否存在 P0 阻断点且无替代方案;P1 性能退化是否在可接受阈值内;CL 提升后关键查询计划是否稳定。只有三条同时满足,才建议推进正式升级。

版本与功能兼容性评估:从差异识别到升级阻断点定级 三类版本差异 引擎能力差异(可选收益) 废弃/移除差异(必须改造) 行为变更差异(需回归测试) 兼容性级别(CL)语义锚点 CL 100 / 110 CL 120 / 130 CL 140 / 150 CL 160(2022) 升级实例 ≠ 升级 CL CL 只保护部分优化行为 不保护已移除语法与引擎默认值 阻断点定级 P0:直接报错 / 数据不一致 P1:性能显著退化 P2:行为差异但可接受 评估流程与决策依据 ① 盘点 版本 / CL / 排序规则 ② 扫描 DMA / 废弃项脚本 ③ 验证 副本还原 + 负载回放 ④ 定级 P0 / P1 / P2 ⑤ 决策 升级 / 改造 / 暂缓 可升级判定三条件(需同时满足) 无 P0 阻断点且无替代方案 | P1 性能退化在阈值内 | CL 提升后关键查询计划稳定 任一不满足 → 先改造或暂缓,不进入正式升级窗口

最后强调一点:兼容性评估的产出不应只是一份报告,而应是一张可执行的“阻断点—处置—验证”对照表。每个 P0 项都要有明确的替代方案和验证用例,每个 P1 项都要有性能基线和回退手段。只有把版本差异转化为可验证的工程动作,升级才具备可控性。