备份维护计划突然失效?一个 SQL Server 主机名引发的大坑

在配置 SQL Server AlwaysOn 高可用性组的维护计划时,我们通常会使用如下 T-SQL 逻辑判断当前实例是否是“首选备份副本”:

1
2
3
4
IF (SELECT master.sys.fn_hadr_backup_is_preferred_replica('MyDbName')) = 1
BEGIN
BACKUP LOG [MyDbName] TO DISK = 'D:\Backup\MyDbName.trn'
END

理论上,如果当前节点是备份首选副本(Preferred Replica),该函数会返回 1,否则返回 0。但我在某项目中遇到一个非常诡异的现象:始终返回 0,即使当前节点确实是主副本,导致维护计划中的备份逻辑无法触发。


问题分析

在排查过程中,我尝试以下方法:

  • QUOTENAME() 包裹数据库名,例如:SELECT fn_hadr_backup_is_preferred_replica('[MyDbName]'),结果返回 1;
  • 用普通字符串 SELECT fn_hadr_backup_is_preferred_replica('MyDbName'),始终返回 0;
  • 数据库确实位于主副本,AG 配置无误。

最终,我通过查询 @@SERVERNAME 发现,SQL Server 内部注册的服务器名与实际主机名不一致!

这是关键所在。


原因根源:SQL Server 安装时记录的本地服务器名未更新

SQL Server 在安装时会把当时的主机名注册进系统目录 sys.servers 中,并在内部函数中用于判断“本地实例”是否等于主副本。

但如果你之后更改了主机名,比如从 OLD-SERVER 改为 NEW-SERVER,系统主机名虽然变了,但 SQL Server 并不会自动更新它的注册信息

你可以用以下 SQL 验证这个不一致:

1
2
3
SELECT 
'系统主机名(实际)' = CAST(SERVERPROPERTY('ComputerNamePhysicalNetBIOS') AS nvarchar),
'SQL Server 注册名(@@SERVERNAME)' = @@SERVERNAME;

解决方法:重新注册本地服务器名

只需执行以下三步:

第一步:删除旧的注册名

1
EXEC sp_dropserver '旧服务器名';

其中 '旧服务器名' 替换为你当前 @@SERVERNAME 的返回值。

第二步:添加新的服务器名

如果你是默认实例:

1
EXEC sp_addserver '新主机名', 'local';

如果你是命名实例(如 NEW-SERVER\SQL2022):

1
EXEC sp_addserver '新主机名\实例名', 'local';

可通过如下方式获取当前信息:

1
2
3
SELECT 
CAST(SERVERPROPERTY('ComputerNamePhysicalNetBIOS') AS nvarchar) AS HostName,
CAST(SERVERPROPERTY('InstanceName') AS nvarchar) AS InstanceName;

第三步:重启 SQL Server 服务

只有重启 SQL Server 实例后,这些更改才会生效。可以通过服务管理器或以下 PowerShell 命令执行:

1
2
Restart-Service -Name 'MSSQLSERVER'  # 默认实例
Restart-Service -Name 'MSSQL$SQL2022' # 命名实例

验证结果

重新启动后,再次执行:

1
2
SELECT @@SERVERNAME;
SELECT master.sys.fn_hadr_backup_is_preferred_replica('YourDbName');

应该可以看到 @@SERVERNAME 与主机名一致,同时 fn_hadr_backup_is_preferred_replica() 正确返回 1。


总结

✅ 如果你在使用 fn_hadr_backup_is_preferred_replica 时发现它始终返回 0,而当前实例确实是主副本,很可能是由于主机名变更后没有同步 SQL Server 内部注册信息。

通过执行 sp_dropserver + sp_addserver + 重启 SQL Server,就可以修复这个问题,确保 AlwaysOn 的备份判断逻辑恢复正常。