13_VMware 安装 SQL Server 双节点 AlwaysOn 可用性组教程 ------ WSFC、AG 与侦听器完整部署(系列核心篇)
系列定位 :《SQL Server 数据库全栈运维实战系列》第 13 篇,与《MySQL 数据库全栈运维实战系列》同构对标(对标 MySQL 13 主从与 InnoDB Cluster 双节点教程,是全系列的重头戏)。适用版本 :SQL Server 2019/2022(2017 及以下差异随文标注)。官方文档:Microsoft Learn SQL Server 2019/2022 文档、Always On 可用性组、WSFC 与侦听器章节。
〇、本篇要做什么(一张总图)
本篇在 VMware Workstation 上,从零搭出一套 Windows 体系的双节点高可用环境:
- 第一层:Windows Server 故障转移群集(WSFC)------先把"谁是活节点、故障谁来判定"的集群底座搭好;
- 第二层:Always On 可用性组(AG)+ 数据库镜像端点 + AG 侦听器------把数据库升级成"主库写、备库读、节点故障自动切换、应用只认一个侦听器地址"的高可用集群。

图 1
| 概念/对象 | 说明 | 类比 MySQL |
|---|---|---|
| WSFC 故障转移群集 | Windows 集群服务,管投票仲裁、节点健康、资源组 | Group Replication 的 Paxos 选主 |
| 数据库镜像端点 | TCP 端点,主副本把事务日志块发往次要副本 | 复制 IO 线程的 binlog 通道 |
| Always On 可用性组 AG | 一组数据库作为整体同步与故障转移单元 | MySQL 主从复制、Group Replication |
| AG 侦听器 | 固定虚拟网络名与 IP,应用统一入口 | MySQL Router 6446 读写端口、VIP |
| 同步提交 | 主库提交等至少一个副本硬化日志才返回 | 半同步复制 |
| 异步提交 | 主库不等副本确认直接返回 | 普通异步复制 |
先读这一句再往下做 :AlwaysOn 的"自动故障转移"依赖 WSFC 多数仲裁投票。两个数据库节点加一个文件共享见证共 3 张票,挂掉任意一个节点,存活侧仍握有 2 张票(本机票 + 见证票),群集继续服务。如果两台 DB 节点都挂、或"节点+见证"所在机器同时挂,存活票数不足,群集按保护原则停服务。这个仲裁语义会贯穿全文,是 AlwaysOn 最重要的认知前提。
一、环境准备(VMware 虚机三台)
1.1 资源规划
| 角色 | 主机名 | IP | vCPU/内存/磁盘 | 说明 |
|---|---|---|---|---|
| 域控兼文件见证 | DC | 192.168.10.10 | 2C / 4G / 60G | Windows Server 2019、AD DS、共享文件夹 witness |
| 节点 1 主 | SQLNode1 | 192.168.10.11 | 4C / 8G / 80G | Windows Server 2019、SQL Server 2019 |
| 节点 2 备 | SQLNode2 | 192.168.10.12 | 4C / 8G / 80G | Windows Server 2019、SQL Server 2019 |
为什么要先有域控?WSFC 的所有节点必须加入同一个 Active Directory 域,群集虚拟对象、节点身份、SQL 服务账户都依赖域认证。生产环境这台 DC 是现有域,教学环境单独建一台。
1.2 VMware 注意事项(本篇相关,逐条照做)
| 事项 | 为什么 / 怎么做 |
|---|---|
| 网络模式 | 三台虚机用 NAT 或仅主机(Host-Only),网段与宿主机打通,方便宿主机用 SSMS 连入;不要桥接到公网 |
| 完整克隆与新 MAC | 克隆 SQLNode1 之前先做完整克隆,再在虚机设置里生成新 MAC,否则 SQLNode2 会与 SQLNode1 IP/MAC 冲突,WSFC 心跳时断时续 |
| 时间同步 | 三台虚机都开 VMware Tools 时间同步,域认证与 Kerberos 对时间敏感,偏差超过 5 分钟直接认证失败 |
| 内存预留 | SQL 节点 8G 内存里留给缓冲池 ~6G,虚机设置里预留全部客户机内存,避免 balloon 回收导致同步延迟抖动 |
| 磁盘控制器 | 数据盘用 NVMe 或准虚拟化 PVSCSI,别用 IDE;事务日志是顺序写,控制器不对会人为放大同步延迟 |
| 快照纪律 | 每完成一个大阶段(装好 SQL、建好域、建好群集、建好 AG)打一个命名快照,失败可整台回滚;注意 AlwaysOn 建好之后不要再挂快照,否则 VSS 与日志链会出问题 |
| CPU 虚拟化 | 教学单机部署不需要;若要在虚机里再跑嵌套,需勾选"虚拟化 Intel VT-x/EPT" |
| 主机名解析 | 三台必须互相能按主机名解析,DNS 由 DC 负责,hosts 兜底 |
1.3 系统基础配置(DC、SQLNode1、SQLNode2 都做)
# ① 改主机名(解释:三台分别执行对应那一条,改完重启)
Rename-Computer -NewName "DC"; Restart-Computer -Force
Rename-Computer -NewName "SQLNode1"; Restart-Computer -Force
Rename-Computer -NewName "SQLNode2"; Restart-Computer -Force
# ② 固定 IP 与 DNS(解释:把 DNS 指向 DC,域解析靠它;三台按上表改)
# 查看网卡名后执行(示例接口名 Ethernet0)
New-NetIPAddress -InterfaceAlias "Ethernet0" -IPAddress 192.168.10.11 `
-PrefixLength 24 -DefaultGateway 192.168.10.254
Set-DnsClientServerAddress -InterfaceAlias "Ethernet0" -ServerAddresses 192.168.10.10
# ③ 关闭防火墙或精准放行(解释:实验环境简化做法;生产按 WSFC 与 AlwaysOn 端口清单放行)
Set-NetFirewallProfile -Profile Domain,Public,Private -Enabled False
1.4 建域控(DC 上执行一次)
# ① 安装 AD 域服务(解释:corp.local 为本篇实验域名,生产用既有域)
Install-WindowsFeature AD-Domain-Services -IncludeManagementTools
# ② 提升为域控(解释:目录服务还原模式密码按企业规范设置,这里写演示值)
Import-Module ADDSDeployment
Install-ADDSForest -DomainName "corp.local" -SafeModeAdministratorPassword `
(ConvertTo-SecureString "Dc@2026!" -AsPlainText -Force) -Force
# 提升完成后自动重启,DC 成为 corp.local 的第一台域控
# ③ 在 DC 上建文件共享见证(解释:WSFC 仲裁用,域内成员只读即可)
New-SmbShare -Name "witness" -Path "C:\witness" -ChangeAccess "corp\Cluster$"
New-Item -ItemType Directory -Path "C:\witness" -Force
# ④ 两台 SQL 节点加域(解释:在 SQLNode1、SQLNode2 上分别执行,重启后用 corp\admin 登录)
Add-Computer -DomainName "corp.local" -Credential "corp\Administrator" -Restart
二、SQL Server 2019 安装(SQLNode1、SQLNode2 都做)
2.1 安装前域账户准备(在 DC 上做一次)
WSFC 与 AlwaysOn 的标准做法是用域托管服务账户 gMSA 跑 SQL 服务,避免手工改密码过期。教学环境也可先用域普通账号,但密码策略要配"永不过期"。
# 在 DC 上:启用 gMSA 根 KDS 并创建 SQL 服务账户(解释:SQL 服务账户不绑死单机,改密码域自动管)
Add-KdsRootKey -EffectiveImmediately
New-ADServiceAccount -Name "sqlsvc" -DNSHostName "sqlsvc.corp.local" `
-PrincipalsAllowedToRetrieveManagedPassword "SQLNode1$","SQLNode2$"
2.2 安装数据库引擎与 SSMS
# ① 挂载 ISO 后无人值守安装数据库引擎(解释:实例名默认 MSSQLSERVER,服务账户用上面的域账号思路)
# setup.exe 常见参数(在 SQLNode1、SQLNode2 各执行一次,结构相同)
# setup.exe /Q /ACTION=INSTALL /FEATURES=SQLENGINE,Conn /INSTANCENAME=MSSQLSERVER `
# /SQLSVCACCOUNT="corp\sqlsvc" /AGTSVCACCOUNT="corp\sqlsvc" `
# /SQLSYSADMINACCOUNTS="corp\Administrator" /UPDATEENABLED=1
# ② 两台都装 SSMS(解释:SSMS 19/20 是独立下载的 SSMS-Setup-ENU.exe,不在安装介质里)
Start-Process "C:\temp\SSMS-Setup-ENU.exe" -ArgumentList "/quiet /norestart" -Wait
-- ③ 验证实例就绪(解释:两台都用 sqlcmd 或 SSMS 各连本机,能登录才算装完)
sqlcmd -S SQLNode1 -E -Q "SELECT @@VERSION, @@SERVERNAME;"
sqlcmd -S SQLNode2 -E -Q "SELECT @@VERSION, @@SERVERNAME;"
2019 vs 2022:本篇命令两版通用。差异点:2022 的智能查询处理增强、2019 引入的行存储批处理与 UCM(受控备份);AlwaysOn 端点语法、WSFC 依赖两版完全一致。
三、第一部分:Windows Server 故障转移群集 WSFC 配置
3.1 安装群集功能(两台 SQL 节点都做)
# 安装故障转移群集功能与管理工具(解释:RSAT 群集 PowerShell 模块一并装上)
Install-WindowsFeature Failover-Clustering -IncludeManagementTools
3.2 群集验证(强烈建议先跑,不过别硬建)
# 群集验证向导等价命令(解释:自动测网络、存储、AD 权限;失败项要逐条解决再建群)
Test-Cluster -Node SQLNode1, SQLNode2 -Verbose
# 输出报告 .mht 在 C:\Users\Administrator 下,先看 Errors 再决定是否建
3.3 创建群集 CLB1
# 创建两节点群集(解释:群集名 CLB1、虚拟 IP 192.168.10.100;实验环境不带共享磁盘,加 -NoStorage)
New-Cluster -Name "CLB1" -Node SQLNode1, SQLNode2 `
-StaticAddress 192.168.10.100 -NoStorage -Verbose
# 把文件共享见证挂到群集上(解释:两节点 + 见证 = 3 张票,后面 3.5 详解)
Set-ClusterQuorum -Cluster CLB1 -NodeAndFileShareMajority "\\DC\witness"
# 验证群集在线(解释:看节点状态、群集资源、仲裁配置)
Get-ClusterNode -Cluster CLB1
Get-ClusterResource -Cluster CLB1
Get-ClusterQuorum -Cluster CLB1
| 验证项 | 预期 |
|---|---|
Get-ClusterNode |
SQLNode1、SQLNode2 均 Up |
| 群集 IP 资源 | 192.168.10.100 Online |
| 仲裁模型 | Node and File Share Majority |
3.4 WSFC 心跳网络
WSFC 靠网络心跳判定节点死活:默认每分钟检测 3~5 次,连续失败超过阈值才判死,避免网络抖动误切换。实验环境两台节点同宿主机,心跳天然稳定。
# 查看群集网络角色(解释:私有网络做心跳,公用网络对外服务)
Get-ClusterNetwork -Cluster CLB1
Get-ClusterNetworkInterface -Cluster CLB1
3.5 仲裁投票:3 张票的语义(本篇认知核心)

图 2
与 MySQL 侧对照:MySQL InnoDB Cluster 双节点挂一台就只读保护,因为 Paxos 要多数派;AlwaysOn 双节点之所以能"挂一台还能自动切",就是因为我们额外引入了文件共享见证这张票,把 2 票变成 3 票。这就是 SQL Server 体系"双节点可生产用"而 MySQL 双节点只能教学的本质差别。
四、第二部分:AlwaysOn 可用性组原理(官方:Always On 可用性组概述)

图 3
| 组件 | 作用 | 类比 MySQL |
|---|---|---|
| 可用性副本 | 一份数据库的主或次要实例 | 主库、从库实例 |
| 数据库镜像端点 | 副本间日志传输通道,TCP 5022 | 复制通道、复制账号权限 |
| 可用性组资源 | WSFC 里一个可漂移的资源组 | MGR 组成员身份 |
| AG 侦听器 | 虚拟网络名 + IP,应用只认它 | MySQL Router 路由入口、VIP |
| 只读路由 | 把读连接定向到次要副本 | Router 6447 RO 端口 |

图 4
与 MySQL 的关键差别:AG 不是"一条复制链路",而是"一组数据库作为整体"。AG 内的数据库必须同生同死:要么整体在主副本可写,要么整体在次要副本待命。不能像 MySQL 那样一部分库复制、一部分不复制。
五、启用 AlwaysOn 并建镜像端点(两台 SQL 节点都做)
5.1 开启实例级 AlwaysOn
# 在 SQLNode1、SQLNode2 分别启用(解释:启用后自动重启 SQL 服务,注意窗口)
Enable-SqlAlwaysOn -ServerInstance SQLNode1
Enable-SqlAlwaysOn -ServerInstance SQLNode2
# 或在 SSMS:实例属性 Always On 高可用 勾选启用 Always On 可用性组
5.2 创建数据库镜像端点
-- 在 SQLNode1 执行(解释:端点名 db_hadr、TCP 5022、证书或 Windows 认证,域内用 Windows 认证即可)
CREATE ENDPOINT [db_hadr]
STATE = STARTED
AS TCP (LISTENER_PORT = 5022)
FOR DATABASE_MIRRORING (ROLE = ALL);
GO
-- 在 SQLNode2 上执行同样的语句,两台端点互为通道
-- 端点防火墙放行 5022(解释:PowerShell 实验环境已整体关防火墙,生产补规则)
-- New-NetFirewallRule -DisplayName "HADR 5022" -Direction Inbound -LocalPort 5022 -Protocol TCP -Action Allow
-- 验证端点在线(解释:state_desc 为 STARTED 才算好)
SELECT name, state_desc, protocol_desc
FROM sys.endpoints
WHERE type_desc = 'DATABASE_MIRRORING';
六、数据库准备与创建可用性组
6.1 备库基线:完整恢复模型 + 一次完整备份
AG 里的数据库有硬性前置:必须完整恢复模型,且至少做过一次完整备份(日志链起点)。
-- 在 SQLNode1(主)准备库(解释:教学库 MyApp;生产直接拿既有库做)
CREATE DATABASE MyApp;
GO
ALTER DATABASE MyApp SET RECOVERY FULL;
GO
-- 一次完整备份 + 一次日志备份(解释:日志链的起点,不做这步 AG 建不起来)
BACKUP DATABASE MyApp TO DISK = N'D:\bak\MyApp_full.bak' WITH INIT, COMPRESSION;
BACKUP LOG MyApp TO DISK = N'D:\bak\MyApp_log.trn' WITH INIT;
6.2 创建可用性组 MyAG(同步提交、自动故障转移)
-- 在 SQLNode1 执行(解释:两个副本都 SYNCHRONOUS_COMMIT,辅助副本设自动故障转移、可读)
CREATE AVAILABILITY GROUP [MyAG]
WITH (AUTOMATED_BACKUP_PREFERENCY = PRIMARY)
FOR DATABASE [MyApp]
REPLICA ON
N'SQLNode1' WITH (
ENDPOINT_URL = N'TCP://SQLNode1.corp.local:5022',
AVAILABILITY_MODE = SYNCHRONOUS_COMMIT,
FAILOVER_MODE = AUTOMATIC,
BACKUP_PRIORITY = 50),
N'SQLNode2' WITH (
ENDPOINT_URL = N'TCP://SQLNode2.corp.local:5022',
AVAILABILITY_MODE = SYNCHRONOUS_COMMIT,
FAILOVER_MODE = AUTOMATIC,
BACKUP_PRIORITY = 50,
SECONDARY_ROLE (ALLOW_CONNECTIONS = ALL, READ_ONLY_ROUTING_URL = N'TCP://SQLNode2.corp.local:1433'));
GO
-- 在 SQLNode2 上把自己加入 AG(解释:与主节点握手,准备接收数据)
ALTER AVAILABILITY GROUP [MyAG] JOIN;
GO
| 验证项 | 预期 |
|---|---|
| SSMS Always On 高可用 可用性组 | 出现 MyAG |
主副本 sys.availability_groups |
MyAG 已定义 |
| 次要副本 Join 后 | 进入"正在同步"状态 |
前置条件自查清单(报错时逐条核对):实例级 AlwaysOn 已开启并重启服务、两台镜像端点已 STARTED、域服务账号对端点有 CONNECT 权限、数据库完整恢复模型且做过完整备份、Windows 防火墙放行 5022。这五条缺一条,CREATE AVAILABILITY GROUP 或 JOIN 就会在对应环节报错。
七、次要副本初始数据同步

图 5
7.1 主副本备份流还原到次要副本
# 把主库备份文件拷到 SQLNode2(解释:\\SQLNode1\D$\bak 共享访问,域账号有读权限)
Copy-Item "\\SQLNode1\D$\bak\MyApp_full.bak" "D:\bak\"
Copy-Item "\\SQLNode1\D$\bak\MyApp_log.trn" "D:\bak\"
-- 在 SQLNode2 上还原(解释:必须 NORECOVERY,让库停在"准备接收日志"的还原中间态)
RESTORE DATABASE MyApp FROM DISK = N'D:\bak\MyApp_full.bak'
WITH NORECOVERY, REPLACE, STATS = 10;
RESTORE LOG MyApp FROM DISK = N'D:\bak\MyApp_log.trn'
WITH NORECOVERY;
-- 在 SQLNode2 上把还原好的库联入 AG(解释:之后日志由端点自动持续推送)
ALTER DATABASE MyApp SET HADR AVAILABILITY GROUP = MyAG;
SSMS 向导更省事:主副本右键 可用性组 使用新建可用性组向导,勾上"完全数据同步",向导会自动做备份拷贝还原。本篇用 T-SQL 写是为了看清每一步;生产两者等价。
7.2 验证同步状态
-- ① 在主副本看副本与库级同步状态(解释:副本级 DMV 看健康,队列列只属于库级 DMV)
SELECT ar.replica_server_name,
rs.role_desc,
rs.operational_state_desc,
rs.connected_state_desc,
rs.synchronization_health_desc,
drs.synchronization_state_desc,
drs.log_send_queue_size,
drs.redo_queue_size
FROM sys.availability_replicas ar
JOIN sys.dm_hadr_availability_replica_states rs
ON rs.replica_id = ar.replica_id
JOIN sys.dm_hadr_database_replica_states drs
ON drs.replica_id = ar.replica_id;
-- ② 端到端实测(解释:主副本写一行,次要副本可读应立刻看到)
-- 在 SQLNode1:
CREATE TABLE MyApp.dbo.t_hadr(id int primary key, msg nvarchar(50));
INSERT INTO MyApp.dbo.t_hadr VALUES (1, N'from primary SQLNode1');
-- 在 SQLNode2 上以只读连接查(解释:同步提交模式下主库提交即硬化,备库立刻可读)
SELECT * FROM MyApp.dbo.t_hadr;
八、配置 AG 侦听器(应用入口)
-- 在主副本执行(解释:侦听器名 aglistener、虚拟 IP 192.168.10.110、端口 1433)
ALTER AVAILABILITY GROUP [MyAG]
ADD LISTENER N'aglistener' (
WITH IP ((N'192.168.10.110', N'255.255.255.0')),
PORT = 1433);
GO
# 验证侦听器注册(解释:DNS 自动登记 aglistener.corp.local;客户端用侦听器名即可)
Test-NetConnection -ComputerName aglistener -Port 1433
nslookup aglistener
# 应用连接串就写侦听器(解释:后续主副本漂移,应用连接串一个字都不用改)
# Server=aglistener; Database=MyApp; User Id=sa; Password=Sql@2026;

图 6
与 MySQL Router 对照:MySQL 要先起 Router 进程再 bootstrap;SQL Server 这边没有独立进程,侦听器是 WSFC 管的一个网络名资源,主副本切换时 WSFC 自动把资源组漂到新主,IP 不变。应用侧语义完全一致------永远连入口,不连具体机器。
九、故障转移演练
9.1 计划内手动故障转移(主副本正常)
# 在主副本用 PowerShell 优雅转移(解释:必须在同步提交模式下执行;先停写再切)
Switch-SqlAvailabilityGroup `
-Path "SQLSERVER:\SQL\SQLNode1\DEFAULT\AvailabilityGroups\MyAG"
# 切换后 SQLNode2 变主、可写
-- 验证新主(解释:现在在 SQLNode2 上可写了)
INSERT INTO MyApp.dbo.t_hadr VALUES (2, N'after manual failover');
9.2 模拟主副本宕机:自动故障转移
# ① 在 SQLNode2 上看群集节点状态(解释:先把 SQLNode1 的群集节点资源停掉,模拟整机宕机)
Stop-ClusterNode -Cluster CLB1 -Name SQLNode1
# ② WSFC 判定 SQLNode1 失联后,自动把 AG 资源组漂到 SQLNode2
Get-ClusterGroup -Cluster CLB1

图 7
-- ③ 验证自动切换结果(解释:应用无感知重连即可;新主 SQLNode2 已持有可写副本)
SELECT ar.replica_server_name, rs.role_desc, rs.synchronization_health_desc
FROM sys.availability_replicas ar
JOIN sys.dm_hadr_availability_replica_states rs ON rs.replica_id = ar.replica_id;
# ④ 恢复 SQLNode1(解释:开机后 SQL 服务自动起,副本自动重新联入并追平日志)
Start-ClusterNode -Cluster CLB1 -Name SQLNode1
9.3 故障转移对照表
| 场景 | 模式 | 是否自动 | 应用是否需改连接串 |
|---|---|---|---|
| 主副本正常维护 | 手动 Switch | 否 | 否 |
| 主副本整机宕机、同步提交 | 自动 | 是 | 否 |
| 副本异步提交 | 手动或强制 | 否 | 否 |
| 双节点同时宕机 | 不可用 | 群集停服务 | 等恢复后按最后存活副本强制恢复 |
9.4 只读路由验证(读流量压到次要副本)
-- ① 在主副本为次要副本配置只读路由列表(解释:读连接打到侦听器后,引擎按列表把它重定向到次要副本)
ALTER AVAILABILITY GROUP [MyAG]
MODIFY REPLICA ON N'SQLNode1' WITH (
PRIMARY_ROLE (READ_ONLY_ROUTING_LIST = (N'SQLNode2')));
ALTER AVAILABILITY GROUP [MyAG]
MODIFY REPLICA ON N'SQLNode2' WITH (
PRIMARY_ROLE (READ_ONLY_ROUTING_LIST = (N'SQLNode1')));
-- ② 只读连接串加 ApplicationIntent=ReadOnly(解释:与写连接串同侦听器,靠这个参数分流)
-- 写连接串:Server=aglistener; Database=MyApp; ApplicationIntent=ReadWrite
-- 读连接串:Server=aglistener; Database=MyApp; ApplicationIntent=ReadOnly
SELECT @@SERVERNAME; -- 预期落到 SQLNode2,即只读副本
| 连接串参数 | 路由目标 |
|---|---|
| 默认 ReadWrite | 当前主副本,可写 |
| ApplicationIntent=ReadOnly | 次要副本,只读分流 |
十、WSFC 仲裁与投票调优(生产进阶)
# 实验环境是两节点 + 文件共享见证。生产常见变体:
# ① 三节点群集:默认奇数票,不需要见证
# ② 域控与 SQLNode1 同机:见证文件放独立文件服务器,避免"共享命运"
# ③ 手动关闭某节点投票(解释:跨数据中心部署时,把灾备中心节点票置 0,避免脑裂)
(Get-ClusterNode -Cluster CLB1 -Name SQLNode2).NodeWeight = 0
| 配置 | 总票数 | 可容忍故障数 | 说明 |
|---|---|---|---|
| 2 节点 + FSW | 3 | 挂任意 1 个 | 教学与小生产标准配法 |
| 3 节点 | 3 | 挂任意 1 个 | 生产推荐 |
| 2 节点无见证 | 2 | 0 | 挂任意 1 个群集停服务,不可用 |
十一、AG 监控常用 DMV
-- ① 副本健康与队列长度(解释:副本级看健康状态,队列列 JOIN 库级 DMV 取)
SELECT ar.replica_server_name,
rs.role_desc,
rs.operational_state_desc,
rs.synchronization_health_desc,
drs.synchronization_state_desc,
drs.log_send_queue_size,
drs.redo_queue_size
FROM sys.dm_hadr_availability_replica_states rs
JOIN sys.availability_replicas ar ON ar.replica_id = rs.replica_id
JOIN sys.dm_hadr_database_replica_states drs ON drs.replica_id = rs.replica_id;
-- ② 数据库级同步详情(解释:每个库一行,synchronization_state_desc 全 HEALTHY 才算健康)
SELECT database_name, synchronization_state_desc,
log_send_rate, redo_rate, last_hardened_time
FROM sys.dm_hadr_database_replica_states;
-- ③ 当前主副本是谁(解释:应用排查时先看主在哪台)
SELECT ag.name, ar.replica_server_name, rs.role_desc
FROM sys.availability_groups ag
JOIN sys.availability_replicas ar ON ar.group_id = ag.group_id
JOIN sys.dm_hadr_availability_replica_states rs ON rs.replica_id = ar.replica_id
WHERE rs.is_local = 1;
| 监控项 | 健康基线 |
|---|---|
| synchronization_health_desc | 全部 HEALTHY |
| redo_queue_size | 持续为 0,>100 页要查 |
| log_send_queue_size | 0;涨说明网络或副本磁盘慢;数据丢失量估算参考 PBM 的 @EstimatedDataLoss |
-- ④ AG 数据库故障准备状态(解释:is_failover_ready=1 表示该库已就绪、可参与故障转移;挂起状态去库级 DMV 取)
SELECT ag.name AS ag_name,
ar.replica_server_name,
adc.database_name,
cls.is_database_joined,
cls.is_failover_ready,
cls.is_pending_secondary_suspend
FROM sys.availability_databases_cluster adc
JOIN sys.availability_replicas ar ON ar.group_id = adc.group_id
JOIN sys.availability_groups ag ON ag.group_id = adc.group_id
JOIN sys.dm_hadr_database_replica_cluster_states cls
ON cls.group_database_id = adc.group_database_id
AND cls.replica_id = ar.replica_id;
# ⑤ PowerShell 一行巡检(解释:脚本化巡检脚本里可直接引用这段)
Get-SqlAvailabilityGroup -Path "SQLSERVER:\SQL\SQLNode1\DEFAULT" |
Get-SqlAvailabilityReplica |
Select-Object Name, Role, AvailabilityMode, ConnectionState, SynchronizationState
十二、端口与参数速查
| 端口 | 用途 |
|---|---|
| 1433 | 数据库引擎默认端口、侦听器对外端口 |
| 5022 | 数据库镜像端点、副本间日志传输 |
| 3343 | WSFC 节点间集群通信 |
| 135、445 | RPC 与 SMB、群集管理流量 |
| 概念/对象 | 说明 | 类比 MySQL |
|---|---|---|
| 镜像端点 5022 | 副本间专用通道 | 复制通道、复制账号权限 |
| 侦听器 1433 | 应用入口 VIP | Router 6446、VIP |
| SYNCHRONOUS_COMMIT | 主库等副本硬化 | 半同步插件 |
| ASYNCHRONOUS_COMMIT | 主库不等确认 | 普通异步复制 |
| FAILOVER_MODE AUTOMATIC | 自动故障转移 | MGR 自动选主 |
| FAILOVER_MODE MANUAL | 仅计划内手动 | 手工 CHANGE MASTER |
应用连接串永远写侦听器名。这和 MySQL 应用永远写 Router 地址是同一个道理------主副本漂移时,入口不变。
十三、扩展:异地容灾与读扩展
双节点同城高可用只是起点。把灾备中心挂成异步提交副本,就构成两地容灾:

图 8
-- 异地副本用异步提交(解释:跨城延迟大,同步会拖垮主库写性能)
ALTER AVAILABILITY GROUP [MyAG]
ADD REPLICA ON N'SQLNodeDR' WITH (
ENDPOINT_URL = N'TCP://SQLNodeDR.corp.local:5022',
AVAILABILITY_MODE = ASYNCHRONOUS_COMMIT,
FAILOVER_MODE = MANUAL);
| 部署形态 | 可自动故障转移 | 可容忍故障 | 适用场景 |
|---|---|---|---|
| 2 节点 + FSW | 是 | 同城挂 1 台 | 教学、小生产 |
| 3 节点同城 | 是 | 挂任意 1 台 | 生产推荐 |
| 同城 2 同步 + 异地 1 异步 | 同城自动、异地手动 | 同城挂 1 台 | 两地三中心金融容灾 |
十四、备份恢复与常见故障处理
14.1 在 AG 上做备份
官方推荐"自动备份首选次要副本",把备份 IO 从主副本卸走:
-- ① 备份作业在次要副本上执行(解释:AG 上下文里 BACKUP 只允许在主或配置允许的副本上做)
BACKUP DATABASE MyApp TO DISK = N'D:\bak\MyApp_secondary.bak' WITH INIT, COMPRESSION;
-- ② 还原单个 AG 库到新实例(解释:主副本完整备份 + 日志链,NORECOVERY 起手)
RESTORE DATABASE MyApp FROM DISK = N'D:\bak\MyApp_primary.bak' WITH NORECOVERY, REPLACE;
14.2 常见故障速查
| 故障 | 现象 | 处理 |
|---|---|---|
| 副本卡 SUSPENDED | redo_queue 只涨不落 | ALTER DATABASE MyApp SET HADR RESUME; |
| 同步 HEALTHY 但 redo 慢 | 大事务回放阻塞 | 查副本磁盘,必要时扩 tempdb 与日志 |
| 侦听器连不上 | DNS 没注册或防火墙挡 1433 | nslookup aglistener + 补防火墙规则 |
| 群集资源 Failed | 仲裁票不足或节点掉域 | Get-ClusterResource 看起因事件日志 |
| 副本反复断连 | 端点证书过期、网络抖动 | 检查 5022 连通性与端点状态 |
十五、踩坑记录(实测汇总)
| # | 坑 | 现象 | 解决 |
|---|---|---|---|
| 1 | 克隆虚机没生成新 MAC | SQLNode2 与 SQLNode1 网络时通时断,WSFC 建不起来 | 完整克隆 + 虚机设置里生成新 MAC |
| 2 | 三台虚机时间不同步 | 域控提升失败、加域报 Kerberos 错误 | 开 VMware Tools 时间同步,偏差控制在 5 分钟内 |
| 3 | 没加域就建 WSFC | New-Cluster 报权限或 AD 对象错误 | 两台 SQL 节点先加 corp.local 域 |
| 4 | 数据库还在 SIMPLE 恢复模型就建 AG | CREATE AVAILABILITY GROUP 报"未做过日志备份" | 先 ALTER 为 FULL,做一次完整备份 + 日志备份 |
| 5 | 备库还原用了 RECOVERY | 库进入可写态,联入 AG 报错 | 备库还原必须 NORECOVERY |
| 6 | 镜像端点没开 5022 防火墙 | 副本之间连不上,同步状态 SUSPENDED | 补入站规则放行 TCP 5022 |
| 7 | 两节点没加文件共享见证 | 挂一台节点后群集停服务,以为自动切换失效 | Set-ClusterQuorum 挂文件共享见证,凑 3 票 |
| 8 | 应用直连 SQLNode1 而非侦听器 | 故障转移后应用连接串要改、业务中断 | 连接串统一写 aglistener |
| 9 | 跨城副本误配同步提交 | 主库写延迟飙到秒级 | 跨城一律 ASYNCHRONOUS_COMMIT |
| 10 | 用域普通账号跑 SQL 服务且密码会过期 | 服务突然起不来,AG 资源 Failed | 改用 gMSA,密码域自动轮换 |
| 11 | 建 AG 后给数据库打快照做测试 | VSS 快照打断日志链,副本同步挂起 | AlwaysOn 建好后禁止对 AG 库打快照 |
| 12 | 直接改 SQLNode1 IP 没动 DNS | 侦听器与副本端点解析错乱 | IP 规划前置,DNS 由 DC 统一登记 |
| 13 | 在次要副本上执行写事务测试 | 报"只读副本不允许写" | 次要副本默认只读,写走侦听器 |
| 14 | 群集验证报错硬建群集 | 建完资源反复 Failed,日志一堆权限错误 | 先 Test-Cluster 逐条修 Errors 再建 |
| 15 | SQL 服务启动账户对 5022 端点无连接权限 | 副本 JOIN 报连接被拒 | 给域服务账号授予 CONNECT ON ENDPOINT |
十六、本文要点速记
- 部署顺序:三台虚机(DC + 两 SQL 节点)→ 建域、加域 → 装 SQL Server → 装群集功能建 WSFC → 启用 AlwaysOn、建端点 → 备库基线、建 AG → 配侦听器 → 故障转移演练。
- WSFC 是 AlwaysOn 的地基:没有群集就没有自动故障转移;两节点必须配文件共享见证凑 3 票。
- 备库铁律:数据库完整恢复模型、至少一次完整备份加日志备份;备库还原一律 NORECOVERY 再 JOIN。
- 应用只认侦听器 :
Server=aglistener,主副本漂移连接串不变。 - 同步提交保数据、异步提交扩异地:同城同步、跨城异步,是两地三中心的标准配法。
- 类比 MySQL:AG ≈ 主从加 Group Replication 的合体,WSFC ≈ Paxos 仲裁层,侦听器 ≈ MySQL Router 入口,同步提交 ≈ 半同步复制。
十七、参考资料(官方正式文档)
本文章属于《SQL Server 数据库全栈运维实战系列》高可用架构篇(系列核心篇)。欢迎收藏、交流。