
1. 为什么我劝你别再依赖故障转移集群实例Always On可用性组的定位先讲个我踩过的坑。前几年接手一套老系统数据库用的还是传统的故障转移集群实例Failover Cluster Instance简称FCI架构是一台共享存储阵列加两台数据库节点。表面上看起来是标准的高可用方案但真正出问题的时候你会发现它远没有想象中那么高可用。有一次存储阵列的控制器固件需要升级整个数据库服务被迫停机两个小时业务方电话都快打爆了。后来我下决心把核心库迁移到Always On可用性组架构上才算是把这种存储单点问题彻底解决掉。如果你正在规划Windows Server环境下的SQL Server高可用方案我建议你优先考虑Always On可用性组Always On Availability Groups而不是传统的故障转移集群实例。原因很简单Always On不需要共享存储它基于SQL Server内部的日志传输机制实现数据同步底层用的是Windows Server故障转移集群WSFC来做节点状态探测和自动故障转移。只要你有两块还过得去的本地磁盘就能搭出一套真正意义上的高可用数据库环境。这篇文章不是从零开始的入门科普我预设你已经有Windows Server和SQL Server的基本操作经验但对Always On的理解还停留在听说过、没实操的阶段。我会用一次真实的搭建过程把从环境准备到集群上线的完整链路讲清楚包括那些官方文档里不会写、但实际动手一定会遇到的细节问题。我用的环境是两台Windows Server 2019虚拟机加一台域控服务器SQL Server版本是2019 Enterprise。为什么要强调版本因为Always On可用性组在SQL Server 2012才开始引入但只有Enterprise版才支持只读路由和备份优先这类高级特性。如果你的业务实在买不起Enterprise授权Standard版也能用但可用性组只支持单个数据库不能把多个业务库打包进同一个可用性组。这个限制在规划阶段就要考虑清楚。2. 搭建前的环境检查清单域、账号、防火墙、网络这四关很多人搭Always On失败不是后面操作有问题而是前期环境没准备好。我见过最离谱的一次对方两台机器连域都没加就想着直接开搭结果Windows故障转移集群功能死活装不上折腾了一整天才发现是工作组环境导致的。所以我先把环境准备这部分单独拎出来讲每一项都别跳过。2.1 域环境和DNS解析Always On的地基所在Always On可用性组必须运行在Windows Server故障转移集群之上而Windows故障转移集群要求所有节点必须属于同一个Active Directory域。这是Windows平台高可用方案的硬性要求不是SQL Server层面的选择而是WSFC本身的设计约束。你要准备的东西包括一台域控服务器Windows Server 2012 R2及以上版本都可以不需要和生产环境同样的版本两台或以上的SQL Server节点服务器全部加入域每台服务器的DNS指向必须配置为域控服务器的IP不能是公网DNS也不能是自己的IPDNS这个点特别容易被忽略。我遇到过一台节点加域之后DNS还是指向路由器地址结果集群验证时节点间通信时好时坏表现为集群服务能启动但节点频繁掉线又自动恢复。后来排查到DNS解析问题改成域控地址后一切正常。WSFC节点之间通过主机名通信如果DNS解析不稳定集群健康检测就会判定节点失联触发不必要的故障转移。2.2 域账号和服务账号的规划一处想清楚后面少踩坑接下来规划账号。你至少需要两类账号千万别图省事全都用同一个第一类是域管理员账号用来执行加域操作、安装故障转移集群功能、创建集群。这类账号权限足够大就行不需要特别设计。第二类是SQL Server服务账号。两个节点的SQL Server服务账号必须是同一个域账号这是Always On同步的一个前提条件。我习惯单独创建一个域账号比如DOMAIN\sqlsvc只赋予它作为服务登录的权限不加入任何管理组最小权限原则嘛。这样做的好处是安全风险小而且不会因为某个管理员改密码导致SQL服务起不来。有个细节需要特别注意如果SQL Server服务账号更改了密码你需要用SQL Server配置管理器去更新服务登录凭据而不是直接在Windows服务管理器里改。SQL Server配置管理器会同步更新启动账户相关的权限设置比如在WSFC资源组中注册的权限。我早期直接用Windows服务管理器改密码结果Always On的集群资源一直报错折腾了半天才找到原因。2.3 Windows防火墙和SQL Server端口放行Always On可用性组的数据同步走的是SQL Server默认端口1433但集群节点间通信还涉及其他端口。如果生产环境防火墙策略比较严格建议提前规划好下列端口用途端口/协议备注SQL Server数据库引擎1433/TCP所有节点都要互相放行可用性组数据库镜像端点5022/TCP默认也可以自定义端口但两个节点必须一致集群节点间通信3343/TCPUDPWSFC心跳通信集群共享卷/文件共享445/TCP仲裁见证若用文件共享则需要动态RPC端口49152-65535/TCP集群其他RPC通信其实最省事的做法是直接在Windows防火墙里放行故障转移集群和SQL Server这两个预定义规则。在服务器管理器里打开高级安全Windows Defender防火墙入站规则里找到这两个预定义规则并启用即可。但如果你有第三方防火墙比如安全组策略统一管控就必须按上面的端口列表逐项放行否则后面集群验证会报错。3. 五步实操拆解从系统功能到可用性组正式上线环境准备好之后就可以开始正式搭建了。整个流程我总结成五个核心步骤每完成一步记得做对应的验证不要一口气全做完再检查那样出问题了不好定位。3.1 第一步启用Windows故障转移集群功能在每台SQL Server节点上用服务器管理器打开添加角色和功能找到故障转移集群功能并安装。这一步没什么技术含量但要注意两点一是所有节点都要装不只是两台数据库节点。如果以后要加第三个副本新节点也要装上。二是在域控制器上不要装WSFC功能。我见过有人把所有服务器都装上故障转移集群功能包括域控结果域控自身不稳定导致集群仲裁频繁抖动。域控服务器就让它安安静静地做认证服务别掺和进集群里。安装完成后用PowerShell命令验证一下功能是否全部就位Get-WindowsFeature Failover-Clustering | Select-Object InstallState如果输出结果是Installed说明这一步完成。3.2 第二步配置Windows故障转移集群这一步是整个搭建过程中最容易出问题的地方。打开故障转移集群管理器右键选择创建集群。这里要输入两个节点的计算机名然后指定集群名称和IP地址。关于集群名称和IP有几个细节值得展开说集群名称会在AD里注册一个计算机对象Computer Object相当于一个虚拟服务器身份。集群IP地址建议使用静态IP如果是DHCP分配后面的稳定性会打折扣。我用的地址段是10.0.0.0/24两个节点分别是10.0.0.11和10.0.0.12集群IP设为10.0.0.10。创建向导会先运行一组验证测试。如果验证不通过不要强行创建。验证报告里每一项都有详细说明最常见的问题是存储验证失败——因为Always On不需要共享存储所以本地磁盘的验证项会失败这属于预期情况可以在验证选项中排除存储部分。但网络方面的验证必须全部通过特别是网络绑定和网络通信这两项如果失败说明网络配置有问题先解决再继续。集群创建完成后在故障转移集群管理器里应该能看到两个节点都处于运行中状态。如果某个节点显示暂停或已停止检查一下该节点的集群服务是否正常启动以及Windows防火墙是否拦截了节点间通信。3.3 第三步配置SQL Server服务并启用Always On两个节点安装SQL Server时的一致性太重要了。我建议在安装时就把两个节点配置成完全一样的实例名一般都用默认实例MSSQLSERVER、一样的排序规则、一样的实例功能组件。尤其是排序规则如果两个节点不一致后面可用性组创建时大概率报错。安装完成后需要做三件事第一件事启用Always On可用性组功能。打开SQL Server配置管理器在SQL Server服务里找到数据库引擎服务右键选择属性切换到Always On高可用性选项卡勾选启用Always On可用性组然后重启SQL Server服务。这一步很多人会忘记或者在集群创建之前就急着开启。官方要求是创建Windows故障转移集群后才能启用Always On但实际上不先创建集群也能勾选只是后续操作会失败。所以一定要按照先有集群再启用Always On的顺序来。第二件事确保两个节点的SQL Server配置一致。不只是版本一致SQL Server配置也要一致。我整理了一个检查清单照着一项项核对配置项建议值/操作检查原因实例名称两边都使用默认实例可用性组不支持不同实例名跨节点SQL Server语言/排序规则两边一致不一致会导致数据库同步后排序规则冲突数据库引擎端口一致性检查生产环境可用1433测试环境可用其他端口SQL Server服务账号同一个域账号跨节点同步的权限基础数据库文件路径两边建议相同副本创建后文件位置一致性更佳第三件事创建数据库镜像端点。Always On可用性组的数据同步依赖数据库镜像端点Database Mirroring Endpoint默认端口5022。SQL Server 2019在启用Always On功能后会尝试自动创建端点但有时候会失败所以我习惯手动创建确保万无一失。CREATE ENDPOINT [Hadr_endpoint] STATE STARTED AS TCP (LISTENER_PORT 5022, LISTENER_IP ALL) FOR DATABASE_MIRRORING (ROLE ALL, ENCRYPTION REQUIRED ALGORITHM AES); GO执行完这条语句后用下面的查询验证端点状态SELECT endpoint_id, name, state_desc, type_desc FROM sys.endpoints WHERE type 4 AND state 0;如果返回的记录里state_desc是STARTED说明端点已就绪。注意在副节点上也要执行同样的创建语句。3.4 第四步配置集群仲裁见证这一步不是可选项是必选。Always On可用性组依赖WSFC的仲裁机制来决定集群是否健康运行。节点数为偶数的集群必须配置见证Witness否则一旦某个节点宕机集群会因为无法达到多数派而自动离线——数据库服务直接不可用这跟高可用的初衷完全相悖。常见的见证类型有两种文件共享见证File Share Witness和磁盘见证Disk Witness。磁盘见证需要额外的共享存储在云环境或虚拟化环境里不太现实。我强烈推荐文件共享见证一台单独的服务器可以是域控也可以是任何一台空闲的Windows Server上开辟一个共享文件夹给集群做第三票。配置方法很直接在故障转移集群管理器里右键集群名称选择更多操作-配置集群仲裁设置然后按向导选择添加或更改仲裁见证-选择文件共享见证输入共享路径即可。在虚拟机环境里搭过Always On的兄弟应该知道文件共享见证放在域控上虽然能用但微软并不推荐在生产环境这么做因为域控本身也可能挂。最佳实践是找一台跟集群节点不在同一物理位置的服务器部署文件共享见证。虚拟化平台的话可以放在第三台独立虚机上。3.5 第五步创建可用性组并接入侦听器前面的准备都做完了现在进入最核心的一步——创建可用性组。我推荐用SSMS的向导来做但对于生产环境我更习惯用T-SQL脚本可重复执行、可版本化、出问题了也好排查。CREATE AVAILABILITY GROUP [AG-SalesDB] WITH AUTOMATIC_FAILOVER ON, DB_FAILOVER ON, DT_CLUSTER ON, REQUIRED_SYNCHRONIZED_SECONDARIES_TO_COMMIT 1 FOR DATABASE [SalesDB] REPLICA ON NSERVER01 WITH ( ENDPOINT_URL NTCP://SERVER01:5022, AVAILABILITY_MODE SYNCHRONOUS_COMMIT, FAILOVER_MODE AUTOMATIC, SEEDING_MODE AUTOMATIC ), NSERVER02 WITH ( ENDPOINT_URL NTCP://SERVER02:5022, AVAILABILITY_MODE SYNCHRONOUS_COMMIT, FAILOVER_MODE AUTOMATIC, SEEDING_MODE AUTOMATIC ) LISTENER NAG-Listener ( WITH IP ((10.0.0.20, 255.255.255.0)), PORT 1433 ); GO这里有几个参数值得拆开讲REQUIRED_SYNCHRONIZED_SECONDARIES_TO_COMMIT 1这个参数意味着主副本必须至少等到一个同步提交副本确认写入才算事务提交成功。这样配置可以保证即使主节点突然宕机同步副本上也不会丢失已提交的事务。但代价是事务响应时间会变长一些如果两个节点都在同城机房网络延迟在1-2毫秒以内这个影响可以忽略。SEEDING_MODE AUTOMATIC是SQL Server 2016以后引入的自动种子初始化功能它会自动把主副本的数据库文件复制到副副本不需要手动做备份还原初始化。注意自动种子化要求数据库文件路径在两边完全一致否则会报错。如果路径不一致就得先手动备份并还原到副节点然后改用SEEDING_MODE MANUAL。创建好可用性组后把副节点上的数据库加入到可用性组中。右键数据库-Always On高可用性-加入可用性组按向导操作。加入完成后在Always On可用性组目录下应该能看到SalesDB (主)和SalesDB (辅助已同步)这样的状态展示。4. 故障转移实测与常见踩坑定位事情只能出在预期之外不要以为可用性组创建成功就万事大吉了。搭建是一回事稳定性是另一回事。我每次部署完Always On都会做一次主动故障转移演练如果这一步没做等到真正发生故障时才发现转移不过去那可就是事故级别的问题了。4.1 手动故障转移的正确姿势用SSMS连接到主副本或任意可写副本在Always On高可用性节点下右键可用性组选择故障转移向导里选择目标副本和故障转移方式。注意这里有个选项叫计划内的手动故障转移数据不丢失和强制故障转移可能丢失数据。日常演练一定要选计划内的强制执行只在主副本完全不可用、数据丢失可以接受的极端场景下用。另一种更快捷的方式是PowerShell# 连接到主副本 SQLPS Switch-SqlAvailabilityGroup -Path SQLSERVER:\Sql\SERVER01\Default\AvailabilityGroups\AG-SalesDB -TargetSecondaryReplica SERVER02执行时会检测目标副本是否处于已同步状态未同步会直接拒绝这是保护机制在起作用。演练完之后观察几项关键指标检查项正常状态可用性组状态两个节点均显示运行中数据同步状态已同步新主副本的可写性可以正常写入侦听器连接用AG-Listener连接可以自动路由到新主副本注意侦听器这个环节连接字符串里的Server地址必须写成AG-Listener,1433不能用节点IP否则故障转移后你连不上数据库。开发组那边如果写死了节点IP你得提前跟他们对齐统一改成侦听器名称。4.2 同步提交复制在慢网络下的性能陷阱同步提交模式有一个隐性问题值得单独说明如果两个节点的网络延迟偏大每一次事务提交都要等副节点确认这会让主数据库的写入性能出现可感知的下降。我在一个跨机房的场景里测过网络延迟大约8毫秒事务提交响应时间直接翻倍从平均3毫秒涨到6毫秒左右。如果你遇到类似情况需要评估业务接受程度。如果延迟大到影响业务有几个思路把关键业务库保留在同步提交模式普通业务库改用异步提交模式。但你要接受异步模式下可能丢失数据的风险。把两个节点尽量放在同一个二层网络内避免跨交换机路由带来的延迟。调整事务提交方式如使用快照隔离级别减少阻塞但这是应用层优化的范畴了。4.3 自动故障转移失败的常见原因排查链路有一次客户环境里的主副本病毒导致系统崩溃Windows集群自动把可用性组转移到了副节点。看起来是成功的但业务方反馈从故障发生到服务恢复整整花了10分钟远超预期。排查下来发现问题出在三个环节叠加第一WSFC检测到节点离线需要时间默认是30秒的心跳间隔加上重试次数大约需要60-120秒才能确认节点失联。第二节点失联后WSFC需要重新评估仲裁如果见证服务器那边也有资源波动仲裁建立又花了额外的时间。第三SQL Server在副节点上重新启动数据库并恢复连接需要时间数据库越大恢复越慢。这个链路里唯一能优化的是第一步你可以把集群心跳的超时时间调短一些但不要追求极端值。我见过有人把心跳间隔改为5秒、超时改为20秒结果网络稍微抖动一下就触发故障转移把正常的负载均衡操作搞成了灾难。10秒间隔加60秒超时是比较合理的配置兼顾了故障切换速度和网络容错。另外自动故障转移还有一个必须满足的条件可用性组内部的所有副本都必须是同步提交模式自动故障转移才有意义。哪怕你设了自动故障转移只要有一个副本是异步模式这个可用性组就不会启用自动故障转移。这是很多人踩过的坑。4.4 副本数据初始化失败的经典报错与处理用自动种子部署可用性组时最常遇到的报错是The data on the secondary replica is not up to date或者Msg 35250, Level 16, State 7: The connection to the primary replica is not active第一次遇到的时候我差点以为配置没做对后来查了日志才发现是端点IP配置问题。CREATE ENDPOINT语句里我写的是LISTENER_IP ALL按道理应该没问题但其实在某些网络配置下SQL Server会解析出错误的IP地址去连副节点。解决办法是先跑一下端点验证SELECT endpoint_id, name, ip_address, port FROM sys.database_mirroring_endpoints;确认两边端点的IP地址是可达的再用Test-NetConnection测一下5022端口Test-NetConnection SERVER02 -Port 5022如果报错是Msg 35250还有一种常见情况是两个节点的SQL Server服务账号虽然相同但这个账号在SQL Server登录中不存在或者权限不足。检查一下每个节点的sys.endpoints和sys.tcp_endpoints的权限确保该账号在每个节点上都有CONNECT权限。5. 上线后的日常维护见证人、备份、补丁这些事别等出事了再想Always On搭完并不代表工作结束了恰恰相反维护工作才刚刚开始。我梳理了几个上线后一定会遇到的问题提前给你打预防针。5.1 文件共享见证的权限和路径问题文件共享见证的共享文件夹不需要放任何数据库数据它只存储一个大约几KB的文本文件来记录集群状态。但它对权限要求比较严格共享权限和NTFS权限都要给集群名称计算机对象Cluster Name Object做好配置这里指的不是某个域用户而是集群在AD里注册的那个虚拟账户。NT AUTHORITY\SYSTEM或者域\集群名$账号要有完全控制权限。别用域管理员去凑数等到AD安全策略收紧的那天你会后悔的。还要注意一点文件共享见证不要跟任何业务文件混放在同一个磁盘上。当共享文件夹所在服务器磁盘满了集群仲裁会变得不稳定我已经见到过至少两起因为共享盘写满导致集群整个离线的事故。5.2 备份策略的设计逻辑很多人在Always On环境下纠结备份到底该在哪边做。我给出的建议是事务日志备份优先放在副副本上做这样能减轻主副本的磁盘和IO压力。全量备份可以放在主副本也可以放在副副本看你实际的IO负载。如果副副本机器性能与主副本相当放副副本更稳妥。备份文件不能存放在同一块物理磁盘上否则副本损坏时数据也一起丢了直接违背了高可用的初衷。在可用性组里配置备份偏好很简单右键可用性组属性在备份首选项里选择辅助副本优先。这样SQL Server会自动把备份任务路由到副副本。但有一点要注意这个路由机制对第三方备份工具比如Commvault、NBU不一定有效需要你到备份客户端里手动指定副副本节点来备份。5.3 SQL Server补丁和Windows补丁的安装顺序这是运维里最容易踩雷的地方。不要直接在两个节点上同时打补丁也不要只给一个节点打补丁而另一个节点不打。正确的做法是先手动故障转移让业务跑到副节点上在原来的主节点上安装补丁此时它是空闲的等待补丁安装完成并重启系统确认数据库引擎能正常启动再次故障转移让业务回到已升级的节点对另一个节点执行同样的补丁安装操作顺序如果反了轻则可用性组状态显示不同步重则集群仲裁把两个节点都踢出去。SQL Server版本不一致的问题尤其隐蔽它不会让集群立刻就挂但会在某次自动故障转移后暴露为数据库无法恢复。5.4 健康监控项清单最后分享一份健康检查清单我在每周巡检时会定期执行这些检查检查内容执行方式/查询语句关注点可用性组同步状态SELECT * FROM sys.dm_hadr_availability_group_states所有副本primary/secondary状态健康数据库同步延迟SELECT * FROM sys.dm_hadr_database_replica_statessynchronization_health_desc为 HEALTHY集群节点在线状态故障转移集群管理器所有节点显示运行中仲裁见证可用性Get-ClusterQuorum见证状态为Online日志发送队列大小SELECT * FROM sys.dm_hadr_database_replica_stateslog_send_queue_size应为0或接近0活动事务数SELECT * FROM sys.dmv_hadr_database_replica_states注意长时间未结束的事务这些检查不用每天做但建议设一个周度循环任务有问题早发现早处理。高可用方案的价值在于该切的时候一定能切而不是平时风平浪静、关键时刻掉链子。6. 最后再分享一个实操小技巧故障转移后应用连接池必须注意的事如果你这套系统后面要接入应用有个细节特别容易被踩我多嘴提醒一句应用端的数据库连接池要设置合理的Connection Timeout和重试机制。故障转移期间连接池里的旧连接会被断开应用如果拿不到新连接就会直接报错。在Java的HikariCP或者.NET的SqlConnection里把Connection Timeout设置到15秒以上同时做一次连接重试能很大程度减少故障转移对业务的影响。还有Always On可用性组的侦听器支持多子网故障转移如果你的节点跨了多个网段需要在连接字符串里设置MultiSubnetFailoverTrue否则故障转移后客户端可能会卡在TCP连接超时上表现为能ping通但数据库连接不上去。Always On这套方案我线上跑了三年多整体稳定性确实没得说。但说句实在话再稳定的架构也需要运维人员对它有深刻的理解。别只停留在会搭的层面多跑几次故障转移演练把每个节点的角色切换和同步行为都摸透真正出故障的时候你才会心里有底。