如何在香港服务器上通过Windows Failover Cluster配置SQL Server AlwaysOn,实现高可用数据库架构?

第一次去香港 MEGA 机房接这套库的时候,是个周五凌晨 1 点。冷通道风像刀子一样,我把手伸进机柜理网线时,耳边只有风扇的嘶嘶声和 KVM 小屏上“Press any key to continue”的跳动。运营同学在门口补充登记,我这边把两台主机的 iDRAC/IPMI 接好、贴好资产标签,拨通运维群里的语音,开始执行我们的计划:在同城机房内搭一套 Windows Server Failover Cluster(WSFC),承载 SQL Server AlwaysOn 可用性组,做到 RPO≈0、RTO<30s 的高可用。
那杯打开就忘在机柜顶上的咖啡,等我想起来,已经是切换演练结束之后了。
目标与边界
架构目标:同城双节点 + 文件共享见证(File Share Witness)的 WSFC,承载 SQL Server AlwaysOn 可用性组(多数据库,同步提交,自动故障转移),对外用 Listener 暴露一个固定服务名。
业务目标:主备实时同步、备库可读(报表/只读查询),计划内切换<10s,计划外自动切换<30s。
约束与选择:
- Windows Server 2022 Datacenter(包含 WSFC)
- SQL Server 2019/2022 Enterprise(AlwaysOn 完整特性;如果是 Standard 只能用 Basic AG,功能限制较多)
- 域环境使用 Active Directory(生产推荐;工作组模式也能做,但维护与排障成本更高)
- 同一机房同一子网(单子网 Listener,收敛复杂度)
硬件与网络清单(实际参数)
| 角色 | 型号/CPU | 内存 | 系统盘 | 数据盘 | 网卡 | OS / SQL |
|---|---|---|---|---|---|---|
| HK-SQL01 | Dell R7525 / AMD EPYC 7313P | 256 GB | 2×480 GB SATA SSD (RAID1) | 4×1.92 TB NVMe(Data/Log/TempDB 分盘) | 2×10GbE(Bond/Team) + 1×1GbE 管理 | Win Server 2022 Datacenter / SQL Server 2019 Ent CU 现行 |
| HK-SQL02 | 同上 | 256 GB | 同上 | 同上 | 同上 | 同上 |
| HK-DC01(域控+见证) | 轻配 | 64 GB | 2×480 GB | 2×1 TB SSD | 2×1GbE | Win Server 2022 / AD DS |
机房网络:单子网 10.10.20.0/24;管理网(Out-of-band)独立;到广州骨干 < 20ms(测得 16–18ms),同子网内 0.2–0.3ms。
IP 规划
| 名称 | IP | 备注 |
|---|---|---|
| HK-SQL01 | 10.10.20.11 | 生产网 |
| HK-SQL02 | 10.10.20.12 | 生产网 |
| HK-DC01 | 10.10.20.10 | 域控 + 文件共享见证 |
| HK-DB-CL1(集群名) | 10.10.20.40 | WSFC VIP |
| AGLISTENER(AG 监听器) | 10.10.20.50 | SQL 客户端连接入口 |
端口白名单(防火墙/ACL 必开)
| 组件 | 端口 | 用途 |
|---|---|---|
| WSFC | 3343/UDP | 心跳 |
| RPC/Cluster | 135/TCP + 49152-65535/TCP | 集群/远程管理 |
| SMB | 445/TCP | 文件共享见证 |
| SQL | 1433/TCP | 客户端连接 |
| HADR Endpoint | 5022/TCP | 可用性组数据通道 |
系统准备与基线优化
1)固件与 BIOS
BIOS 调整为 Performance(禁 C-States),内存 profile 固定,确保延迟与抖动最小。
NVMe 开启命名空间对齐;控制器固件更新到机房白名单版本。
2)Windows 基线
# 功能安装(含WSFC)
Install-WindowsFeature Failover-Clustering, RSAT-AD-PowerShell -IncludeManagementTools
# 时钟同步,域控为权威时钟(也可配置独立NTP)
w32tm /config /manualpeerlist:"time.windows.com,0x8" /syncfromflags:manual /reliable:yes /update
w32tm /resync
3)磁盘与文件系统
数据盘、日志盘、TempDB 物理分离,64K 分配单元,禁用压缩。
Get-Disk | Where PartitionStyle -Eq 'RAW' | Initialize-Disk -PartitionStyle GPT
New-Partition -DiskNumber 3 -DriveLetter D -UseMaximumSize | Format-Volume -FileSystem NTFS -AllocationUnitSize 65536 -NewFileSystemLabel "DATA"
New-Partition -DiskNumber 4 -DriveLetter L -UseMaximumSize | Format-Volume -FileSystem NTFS -AllocationUnitSize 65536 -NewFileSystemLabel "LOG"
New-Partition -DiskNumber 5 -DriveLetter T -UseMaximumSize | Format-Volume -FileSystem NTFS -AllocationUnitSize 65536 -NewFileSystemLabel "TEMPDB"
4)SQL Server 安装与补丁
使用域服务账号 HKDOM\sqlsvc 作为 SQL 服务账户(最小权限,非管理员)。
安装后立即打到当前 CU。
基线参数:
-- 最大内存(预留 OS 与备端工具),按256G主机预留 ~32G
EXEC sp_configure 'show advanced options', 1; RECONFIGURE;
EXEC sp_configure 'max server memory (MB)', 215040; RECONFIGURE;
-- MAXDOP:按NUMA和核心布局(EPYC 7313P 16c,单路),OLTP 取 8
EXEC sp_configure 'max degree of parallelism', 8; RECONFIGURE;
-- TempDB:8~12 个数据文件,等大小,独立盘 T:\
搭建域与文件共享见证
在 HK-DC01 上部署 AD DS,建域 hkdom.local,把两台 SQL 节点加域。
创建文件共享 \\HK-DC01\WSFCWitness,仅集群计算机对象 HK-DB-CL1$ 与节点计算机对象有 读写。
创建 Windows Failover Cluster(WSFC)
1)节点验证(必须做,失败别硬上)
Test-Cluster -Node HK-SQL01,HK-SQL02 -Include "Storage","Inventory","Network","System Configuration"
常见报错:网络多路径命名不一致、心跳网络被打上公用标记等;按报表修即可。
2)创建集群
New-Cluster -Name "HK-DB-CL1" -Node "HK-SQL01","HK-SQL02" -StaticAddress "10.10.20.40" -NoStorage
3)设置仲裁(文件共享见证)
Set-ClusterQuorum -Cluster "HK-DB-CL1" -NodeAndFileShareMajority "\\HK-DC01\WSFCWitness"
4)可选:心跳阈值(同子网通常默认就好)
(Get-Cluster).SameSubnetDelay = 1000 # ms
(Get-Cluster).SameSubnetThreshold = 5 # 次
启用 AlwaysOn 与 HADR Endpoint
1)启用 AlwaysOn(两节点都要)
在“SQL Server 配置管理器”里勾选“可用性组”,或用注册表/PowerShell。启用后重启 SQL 服务。
2)创建 HADR Endpoint(端口 5022)
采用 Windows 身份验证,授予服务账号连接权限。
-- 两个节点都执行
CREATE ENDPOINT [Hadr_endpoint]
STATE = STARTED
AS TCP (LISTENER_PORT = 5022)
FOR DATA_MIRRORING (ROLE = ALL, ENCRYPTION = REQUIRED, ALGORITHM = AES);
-- 在每个实例授予服务账号连接端点权限
CREATE LOGIN [HKDOM\sqlsvc] FROM WINDOWS;
GRANT CONNECT ON ENDPOINT::[Hadr_endpoint] TO [HKDOM\sqlsvc];
3)准备数据库
主库做一次 FULL + LOG 备份;检查数据库为 FULL 恢复模式。
确保两端 排序规则一致、兼容级别一致。
创建可用性组(两种方式:自动播种 vs 传统备份还原)
方案 A:自动播种(推荐,干净、快)
在主库(HK-SQL01)执行:
-- 1) 创建 AG(自动播种,自动故障转移,同步提交,可读次要)
CREATE AVAILABILITY GROUP [AG_HK_CORE]
WITH (AUTOMATED_BACKUP_PREFERENCE = SECONDARY, DB_FAILOVER = ON, SEEDING_MODE = AUTOMATIC)
FOR REPLICA ON
N'HK-SQL01' WITH (
ENDPOINT_URL = N'TCP://HK-SQL01.hkdom.local:5022',
FAILOVER_MODE = AUTOMATIC,
AVAILABILITY_MODE = SYNCHRONOUS_COMMIT,
BACKUP_PRIORITY = 60,
SECONDARY_ROLE(ALLOW_CONNECTIONS = READ_ONLY)),
N'HK-SQL02' WITH (
ENDPOINT_URL = N'TCP://HK-SQL02.hkdom.local:5022',
FAILOVER_MODE = AUTOMATIC,
AVAILABILITY_MODE = SYNCHRONOUS_COMMIT,
BACKUP_PRIORITY = 50,
SECONDARY_ROLE(ALLOW_CONNECTIONS = READ_ONLY));
GO
-- 2) 将数据库加入 AG(自动播种会在备端自动创建)
ALTER AVAILABILITY GROUP [AG_HK_CORE] ADD DATABASE [CoreDB], [CRMDB];
GO
在备库(HK-SQL02)执行:
ALTER AVAILABILITY GROUP [AG_HK_CORE] JOIN;
GO
ALTER DATABASE [CoreDB] SET HADR AVAILABILITY GROUP = [AG_HK_CORE];
ALTER DATABASE [CRMDB] SET HADR AVAILABILITY GROUP = [AG_HK_CORE];
注意:自动播种要求两端有足够权限写 MDF/LDF,磁盘路径需一致或确保 SQL 能创建目录。
方案 B:传统备份/还原(在高带宽不稳定时更踏实)
主库:
BACKUP DATABASE [CoreDB] TO DISK='\\HK-SQL01\backup\CoreDB_full.bak' WITH COPY_ONLY, COMPRESSION;
BACKUP LOG [CoreDB] TO DISK='\\HK-SQL01\backup\CoreDB_log.trn' WITH COMPRESSION;
备库(NORECOVERY 还原):
RESTORE DATABASE [CoreDB] FROM DISK='\\HK-SQL01\backup\CoreDB_full.bak' WITH NORECOVERY, REPLACE;
RESTORE LOG [CoreDB] FROM DISK='\\HK-SQL01\backup\CoreDB_log.trn' WITH NORECOVERY;
加入 AG:
ALTER AVAILABILITY GROUP [AG_HK_CORE] JOIN;
ALTER DATABASE [CoreDB] SET HADR AVAILABILITY GROUP = [AG_HK_CORE];
创建 Listener(客户端的唯一入口)
单子网场景(本次部署):
ALTER AVAILABILITY GROUP [AG_HK_CORE]
ADD LISTENER N'AGLISTENER' (
WITH IP ((N'10.10.20.50', N'255.255.255.0')),
PORT=1433);
SPN(确保 Kerberos,减少双跳问题)
在域控上(或有 RSAT 的终端):
setspn -S MSSQLSvc/AGLISTENER.hkdom.local:1433 HKDOM\sqlsvc
setspn -S MSSQLSvc/AGLISTENER:1433 HKDOM\sqlsvc
客户端连接串(.NET/ODBC)
Server=AGLISTENER.hkdom.local;Database=CoreDB;Integrated Security=True;MultiSubnetFailover=True;ApplicationIntent=ReadOnly
单子网也建议 MultiSubnetFailover=True,驱动会优化连接握手;只读流量可通过 ApplicationIntent=ReadOnly 自动打到可读副本。
备份与维护(尊重 AG 偏好)
备份偏好:在 AG 层设置 “Prefer Secondary”。作业里调用函数判断本机是否应执行。
作业脚本示例(通用全/差/日志,自动判断)
IF sys.fn_hadr_backup_is_preferred_replica(DB_NAME()) = 1
BEGIN
DECLARE @dt nvarchar(20)=CONVERT(nvarchar(20),GETDATE(),112)+'_'+REPLACE(CONVERT(nvarchar(8),GETDATE(),108),':','');
BACKUP DATABASE [CoreDB] TO DISK = N'\\backupshare\CoreDB_FULL_'+@dt+'.bak' WITH COPY_ONLY, COMPRESSION, CHECKSUM;
END
同理加差异与日志备份作业。二次校验 msdb.dbo.backupset,并用 RESTORE VERIFYONLY。
维护与作业漂移:
- 需要在两端都创建相同的 SQL Agent 作业,但靠脚本判断是否执行。
- 索引/统计维护尽量在主副本(避免同步带来的额外日志量),并选择业务低峰。
监控与告警
关键 DMV 查询
-- 同步健康与延迟
SELECT ag.name, ar.replica_server_name, drs.synchronization_state_desc, drs.log_send_queue_size, drs.redo_queue_size, drs.end_of_log_lsn, drs.redo_rate
FROM sys.dm_hadr_database_replica_states drs
JOIN sys.availability_replicas ar ON drs.replica_id=ar.replica_id
JOIN sys.availability_groups ag ON ar.group_id=ag.group_id;
-- 会话状态与可读二级副本连接数
SELECT * FROM sys.dm_hadr_availability_replica_states;
集群日志(排障利器)
Get-ClusterLog -UseLocalTime -Destination C:\ClusterLogs -TimeSpan 5
性能计数器:SQLServer:Availability Replica / Database Replica;物理磁盘延迟;网络吞吐与重传。
切换演练(计划内 & 计划外)
计划内切换(零丢失)
在当前主库(HK-SQL01):
ALTER AVAILABILITY GROUP [AG_HK_CORE] FAILOVER;
预期:几秒内 Listener 指向 HK-SQL02,业务短暂抖动,连接自动重连。
计划外强制切换(允许丢失)
仅灾难演练用,确保业务明确知情:
ALTER AVAILABILITY GROUP [AG_HK_CORE] FORCE_FAILOVER_ALLOW_DATA_LOSS;
我们真实踩过的坑 & 现场修复
端点 5022 被防火墙拦截
现象:AG 加入卡住,The connection to the primary replica is not active。
解决:Windows 防火墙入站规则显式放行 5022/TCP,机房 ACL 同步开白。
排查技巧:Test-NetConnection HK-SQL02 -Port 5022。
SPN 缺失导致 Kerberos 降级为 NTLM
现象:连接延迟偶发飙高(特别是 AD 压力大时)。
解决:为 Listener 绑定 MSSQLSvc/AGLISTENER... 到服务账号,并清理重复 SPN;klist purge 刷票测试。
自动播种失败(磁盘路径不存在)
现象:AG 里的 DB 状态 NOT_SYNCHRONIZING,错误里有 Operating system error 3(The system cannot find the path specified.)。
解决:在备端预创建目录结构(例如 D:\MSSQL\Data / L:\MSSQL\Log / T:\TempDB),重试播种。
WSFC 验证报告网络告警
现象:Validate Network Communication 黄色告警,标记了多网卡。
解决:在 Failover Cluster Manager → Networks 中把管理/带外网卡标记为 Do not allow cluster network communication。
备库只读负载把 REDO 压趴
现象:报表在副本跑得猛,redo_queue_size 飙升,同步延迟上升。
调整:给副本加内存保留,控制报表并发;必要时把报表迁到延时副本或额外只读副本(异步)。
TempDB 没分盘导致日志同步被拖累
现象:日志刷盘等于总盘延迟,AG 同步队列飙升。
解决:TempDB 独占 NVMe,预分配文件、固定增长(避免频繁扩展)。
跨域名解析偶发抖动
现象:客户端偶发解析到旧 IP。
解决:确保 Listener 的 DNS TTL 合理(默认 120s 可调),并在关键应用侧启用连接重试策略。
验收与指标(我们上线当晚的真实数据)
| 项 | 指标 |
|---|---|
| 计划内切换(主→备) | 6–8s(应用自动重连,平均 6.4s) |
| 计划外断电演练(主节点失联) | 20–25s 自动故障转移(含检测阈值) |
| 同步延迟(业务低峰) | < 5ms,log_send_queue_size≈0 |
| 只读副本吞吐(报表) | 峰值 ~ 18k Batch Requests/sec |
| 数据盘延迟(P99) | 1.2–1.8ms(NVMe) |
运维清单(上线之后每天/每周要做的事)
每天:巡检 DMV、SQL Agent 作业执行、备份可恢复性抽检(RESTORE VERIFYONLY)。
每周:下载并归档 ClusterLog、Windows 事件日志;只读副本压力测试,确认不会影响 REDO。
每月:打补丁窗口(先备后主,滚动升级),AG 切换演练一次;容量与增长评估。
突发:监控告警(同步队列、心跳抖动、磁盘 P99),第一时间抓取现场信息。
附:端到端部署流程速查(可当 Runbook)
- 机房上架 → 布线 → iDRAC → 固件/BIOS 基线
- 安装 Windows 2022 → 打补丁 → 加域
- 盘符规划(D/L/T)→ NTFS 64K → SQL 安装(域服务账号)→ CU
- DC 上创建文件共享见证
- 节点跑 Test-Cluster → New-Cluster → Set-ClusterQuorum
- SQL 启用 AlwaysOn → 创建 HADR Endpoint(5022)
- 选自动播种或备份/还原 → 创建/加入 AG
- 创建 Listener(10.10.20.50:1433)→ 配置 SPN
- 放通端口/防火墙 → 客户端连通测试(MultiSubnetFailover=True)
- 备份&维护作业部署(尊重 AG 偏好)→ 监控上报
- 计划内/计划外切换演练 → 验收指标归档
结尾:3 点 17 分,我把那杯凉咖啡一口闷了
切完最后一次计划外演练时已经 3 点多。我看着监控面板上的绿色,redo_queue_size 归零,Listener 的连接数平稳落回主库。机柜门关上的一瞬间,冷通道的风声也像安静了下来。
我端起那杯已经凉透的咖啡,一口闷了,然后在微信群里发了上线确认:“香港库 AlwaysOn 高可用上线,RPO≈0、RTO<30s,演练通过。”
这套方案不是最华丽的,但它在真实的机房、真实的链路、真实的流量下跑得稳。这就是我们做运维想要的——一套能扛事、能落地、能复用的高可用架构。
可复制的配置片段(集中放一处,拿就能用)
PowerShell:WSFC
Install-WindowsFeature Failover-Clustering -IncludeManagementTools
Test-Cluster -Node HK-SQL01,HK-SQL02
New-Cluster -Name "HK-DB-CL1" -Node "HK-SQL01","HK-SQL02" -StaticAddress "10.10.20.40" -NoStorage
Set-ClusterQuorum -NodeAndFileShareMajority "\\HK-DC01\WSFCWitness"
T-SQL:HADR Endpoint + AG(自动播种)
CREATE ENDPOINT [Hadr_endpoint]
STATE = STARTED
AS TCP (LISTENER_PORT = 5022)
FOR DATA_MIRRORING (ROLE = ALL, ENCRYPTION = REQUIRED, ALGORITHM = AES);
CREATE LOGIN [HKDOM\sqlsvc] FROM WINDOWS;
GRANT CONNECT ON ENDPOINT::[Hadr_endpoint] TO [HKDOM\sqlsvc];
CREATE AVAILABILITY GROUP [AG_HK_CORE]
WITH (AUTOMATED_BACKUP_PREFERENCE = SECONDARY, DB_FAILOVER = ON, SEEDING_MODE = AUTOMATIC)
FOR REPLICA ON
N'HK-SQL01' WITH (ENDPOINT_URL=N'TCP://HK-SQL01.hkdom.local:5022', FAILOVER_MODE=AUTOMATIC, AVAILABILITY_MODE=SYNCHRONOUS_COMMIT, SECONDARY_ROLE(ALLOW_CONNECTIONS=READ_ONLY)),
N'HK-SQL02' WITH (ENDPOINT_URL=N'TCP://HK-SQL02.hkdom.local:5022', FAILOVER_MODE=AUTOMATIC, AVAILABILITY_MODE=SYNCHRONOUS_COMMIT, SECONDARY_ROLE(ALLOW_CONNECTIONS=READ_ONLY));
ALTER AVAILABILITY GROUP [AG_HK_CORE] ADD DATABASE [CoreDB], [CRMDB];
T-SQL:Listener
ALTER AVAILABILITY GROUP [AG_HK_CORE]
ADD LISTENER N'AGLISTENER' (WITH IP ((N'10.10.20.50', N'255.255.255.0')), PORT=1433);
Kerberos SPN
setspn -S MSSQLSvc/AGLISTENER.hkdom.local:1433 HKDOM\sqlsvc
setspn -S MSSQLSvc/AGLISTENER:1433 HKDOM\sqlsvc
连接串
Server=AGLISTENER.hkdom.local;Database=CoreDB;Integrated Security=True;MultiSubnetFailover=True;ApplicationIntent=ReadOnly
如果你也要在香港机房落地这套架构,把上面的 Runbook 按顺序做一遍,再结合“坑与修复”的清单,基本就能一次过。若你的环境是多子网/跨机房、多副本、或要做混合云延伸,我们也可以在这个基线之上扩展(MultiSubnet Listener、异步 DR 副本、Azure Cloud Witness 等),但那又是另一个凌晨、另一个故事了。