数据库基础体系 · 第 49/139 篇。文章以各产品官方稳定版本的公开语义为准;示例会明确引擎、事务与部署边界。
SQL Server 高可用与运维:Backup、Always On、监控和故障恢复
SQL Server 的高可用与运维不能只理解为“配置一个副本,再定期执行 BACKUP DATABASE”。一个可恢复的系统至少要回答四个问题:
- 数据如何被持久化和复制?
- 故障发生时,客户端如何切换到可用副本?
- 如何知道副本、日志和备份是否真的健康?
- 发生误删、逻辑错误、存储损坏或整个站点故障时,如何恢复到目标时间点?
本文以现代受支持版本的 SQL Server Database Engine 公开语义为基础,重点讨论:
- 数据库备份、事务日志备份与恢复链;
- 全量、差异、PITR、RPO/RTO 和恢复演练;
- Always On Availability Groups(可用性组)的组件、数据流、同步与故障转移;
- SQL Server Failover Cluster Instance(FCI)、Log Shipping 与 Availability Groups 的边界;
- 备份、Always On、事务日志和监控之间的关系;
- 故障诊断、计划内切换、强制故障转移和灾难恢复。
一、先建立三个边界:备份、复制和故障转移不是同一件事
1. 备份解决“回到过去”
备份是某个时刻数据库状态或事务日志的持久化副本。它主要用于:
- 误删数据后的时间点恢复;
- 数据库文件或存储损坏后的恢复;
- 逻辑错误传播后的回滚;
- 迁移、测试和恢复演练;
- 灾难后在新服务器重建数据库。
备份通常不能让正在运行的连接无感知地切换到另一台服务器。
2. 复制解决“把变化送到另一处”
Always On Availability Groups、数据库镜像和 Log Shipping 都会把数据变化传送到其他副本或服务器,但复制行为不同:
- Always On AG 复制事务日志记录;
- Log Shipping 周期性备份、复制并还原事务日志;
- FCI 通常由多个节点共享同一组存储,实例在节点之间切换,而不是维护独立数据库副本。
复制延迟、存储损坏、误操作和逻辑错误都可能被带到副本上。因此:
Always On 不能替代备份,备份也不能替代高可用。
3. 故障转移解决“改变服务入口”
故障转移把客户端从故障实例切换到另一个可服务实例。它解决的是服务连续性问题,但是否丢数据取决于:
- 事务日志是否已经传到目标副本;
- 目标副本是否已经将日志硬化到持久存储;
- 是计划内切换还是强制切换;
- 是否允许在不确定数据完整性的情况下接管服务。
二、必须先理解 SQL Server 的事务日志
1. 事务日志记录什么
SQL Server 使用事务日志记录数据库修改,例如:
- 行插入、更新和删除;
- 页面分配和释放;
- 索引操作;
- 事务提交和回滚;
- 数据库恢复所需的内部操作。
日志记录按逻辑顺序分配 Log Sequence Number(LSN)。LSN 可以看作日志流中的单调位置。事务提交时,提交相关的日志记录必须先写入日志文件并满足持久性要求,SQL Server 才能向客户端确认提交成功。
数据库数据页可以晚于事务提交写入数据文件,因为崩溃恢复时可以利用日志重做或回滚操作。
2. 恢复模型决定日志能否连续备份
SQL Server 有三种恢复模型:
| 恢复模型 | 是否支持事务日志备份 | 能否通常进行时间点恢复 |
|---|---|---|
| Simple | 否 | 否 |
| Full | 是 | 是 |
| Bulk-logged | 是 | 有条件 |
FULL 恢复模型并不是“自动生成日志备份”。如果长期不执行日志备份,日志截断可能无法推进,日志文件会不断增长。
可以先检查恢复模型和日志复用等待原因:
SELECT
name,
recovery_model_desc,
log_reuse_wait_desc
FROM sys.databases
WHERE name = N'Inventory';
典型结果可能是:
name recovery_model_desc log_reuse_wait_desc
--------- -------------------- -------------------
Inventory FULL LOG_BACKUP
这表示数据库处于完整恢复模型,但缺少事务日志备份。解决方法不是盲目扩大日志文件,而是建立日志备份计划,并调查为什么日志无法截断。
在 Always On 环境中,还可能看到:
AVAILABILITY_REPLICA
这表示某个可用性副本仍需要日志,主库不能仅因为本地已经完成检查点就截断相关日志。
3. LSN 是恢复链的核心
一次典型的恢复链如下:
全量备份 F0
└── 差异备份 D1
└── 日志备份 L1 ── L2 ── L3
差异备份记录的是自某个全量备份以来发生变化的数据页。日志备份记录的是日志序列中的连续区间。
如果恢复了 F0,可以选择:
- 直接恢复
F0; - 恢复
F0后再恢复D1,跳过L1之前已经包含在差异中的日志; - 恢复
F0,再依次恢复L1、L2、L3。
不能在恢复 L1 后再恢复 D1,因为 D1 的内容已经基于 F0 形成了另一条恢复路径。
三、Backup:全量、差异、日志和 Copy-only
1. 全量备份
全量备份包含恢复数据库所需的完整数据内容,并包含恢复所需的日志信息。它是恢复链的基础。
BACKUP DATABASE [Inventory]
TO DISK = N'/var/opt/mssql/backup/Inventory_full.bak'
WITH
INIT,
COMPRESSION,
CHECKSUM,
STATS = 10;
这里假定:
- SQL Server 运行在 Linux;
- SQL Server 服务账户能够写入
/var/opt/mssql/backup; - 该目录已经存在;
- 目标磁盘有足够空间。
Windows 示例可以使用:
BACKUP DATABASE [Inventory]
TO DISK = N'C:\SqlBackups\Inventory_full.bak'
WITH
INIT,
COMPRESSION,
CHECKSUM,
STATS = 10;
关键选项含义:
INIT:覆盖备份介质中的现有备份集。生产环境必须确认不会误覆盖仍需保留的备份。COMPRESSION:请求备份压缩。实际收益取决于数据可压缩性和 CPU。CHECKSUM:在备份过程中进行校验,并在还原时可以继续校验。STATS = 10:每完成约 10% 输出进度。
CHECKSUM 不是绝对防止介质损坏的保证。它能发现一部分读取或写入过程中的问题,但仍需要把备份复制到独立存储,并执行实际还原验证。
2. 差异备份
差异备份记录自某个全量备份以来发生变化的数据页。它不是“上一次备份以来的差异”,而是“自差异基准全量备份以来的差异”。
BACKUP DATABASE [Inventory]
TO DISK = N'/var/opt/mssql/backup/Inventory_diff.bak'
WITH
DIFFERENTIAL,
COMPRESSION,
CHECKSUM,
STATS = 10;
如果每天执行一次全量、每小时执行一次差异,那么随着时间推移,差异备份可能越来越大,直到下一次全量备份重置差异基准。
差异备份的恢复过程是:
恢复全量 F0 WITH NORECOVERY
恢复最近的差异 Dn WITH NORECOVERY
恢复 Dn 之后的日志备份
最后一个日志使用 WITH RECOVERY
3. 事务日志备份
完整恢复模型下,日志备份形成连续日志链:
BACKUP LOG [Inventory]
TO DISK = N'/var/opt/mssql/backup/Inventory_log_20250308_1200.trn'
WITH
COMPRESSION,
CHECKSUM,
STATS = 10;
日志备份能够:
- 使日志截断具备条件;
- 支持时间点恢复;
- 降低 RPO;
- 将事务日志传送到其他服务器或副本。
但日志备份不等于“每条事务提交后立即复制”。备份任务是一个独立的调度过程,其频率决定了仅依靠备份时的潜在数据丢失窗口。
4. Copy-only 备份
Copy-only 备份用于临时复制,不改变常规备份链的正常语义。
BACKUP DATABASE [Inventory]
TO DISK = N'/var/opt/mssql/backup/Inventory_copy_only.bak'
WITH
COPY_ONLY,
COMPRESSION,
CHECKSUM;
典型用途:
- 临时创建测试环境;
- 在不影响差异备份基准的情况下获取一个全量副本;
- 对生产数据库做一次独立取样备份。
Copy-only 全量备份可以用于独立恢复,但不会成为后续差异备份的差异基准。Copy-only 日志备份则不会改变普通日志备份链的截断行为。
5. 备份文件检查与备份历史
可以先检查备份介质中的备份集:
RESTORE HEADERONLY
FROM DISK = N'/var/opt/mssql/backup/Inventory_full.bak';
该命令不会恢复数据库,而是返回备份集元数据,例如:
BackupType;FirstLSN、LastLSN;DatabaseBackupLSN;BackupStartDate、BackupFinishDate;- 是否包含校验信息。
查看最近的备份历史:
SELECT TOP (20)
bs.database_name,
bs.backup_start_date,
bs.backup_finish_date,
CASE bs.type
WHEN 'D' THEN 'Full'
WHEN 'I' THEN 'Differential'
WHEN 'L' THEN 'Log'
ELSE bs.type
END AS backup_type,
bs.first_lsn,
bs.last_lsn,
bs.database_backup_lsn,
bs.is_copy_only,
bs.has_backup_checksums,
bmf.physical_device_name
FROM msdb.dbo.backupset AS bs
LEFT JOIN msdb.dbo.backupmediafamily AS bmf
ON bs.media_set_id = bmf.media_set_id
WHERE bs.database_name = N'Inventory'
ORDER BY bs.backup_finish_date DESC;
msdb 中的历史记录很重要,但它只存在于某个 SQL Server 实例中。若实例丢失,备份文件仍然存在,并不意味着备份目录、保留策略和元数据也完整。因此应同时保存备份文件、备份清单和恢复说明。
四、RPO、RTO 与 PITR
1. RPO 与 RTO 的定义
RPO(Recovery Point Objective) 是允许丢失的数据时间范围。
如果最近一次可用日志备份在 12:00,而故障发生在 12:07,且没有其他副本已经保存 12:00 之后的事务,那么基于该备份方案的 RPO 至少接近 7 分钟。
RTO(Recovery Time Objective) 是从故障发生到业务恢复的允许时间。它包括:
- 找到正确备份;
- 创建或准备目标实例;
- 复制备份文件;
- 执行还原;
- 回滚未完成事务;
- 应用配置和权限;
- 验证数据;
- 切换客户端;
- 解除应用层错误状态。
因此,RPO 是“最多丢多少数据”,RTO 是“最多停多久”。
2. 时间点恢复的形式化条件
设:
F0是一个有效全量备份;L1, L2, ..., Ln是从F0之后连续的日志备份;T是需要恢复到的目标时间;Lk是包含时间T的日志备份。
要进行时间点恢复,必须满足:
F0可读且属于目标数据库;- 如果使用差异备份,差异备份必须以
F0为基准; L1到Lk的日志区间连续;- 所有中间备份均能成功还原;
T落在Lk覆盖的事务日志范围内;- 如果希望恢复到故障前最后状态,还需要获得故障前的尾日志。
用恢复顺序表示:
RESTORE DATABASE Inventory FROM F0 WITH NORECOVERY;
RESTORE DATABASE Inventory FROM Dn WITH NORECOVERY; -- 可选
RESTORE LOG Inventory FROM L1 WITH NORECOVERY;
...
RESTORE LOG Inventory FROM Lk
WITH STOPAT = '2025-03-08T12:05:00', RECOVERY;
NORECOVERY 表示还会继续应用备份,数据库保持还原状态;最后一步使用 RECOVERY,表示完成恢复并打开数据库。
3. 一个完整的恢复示例
假设已有:
/var/opt/mssql/backup/Inventory_full.bak/var/opt/mssql/backup/Inventory_diff.bak/var/opt/mssql/backup/Inventory_log_1200.trn/var/opt/mssql/backup/Inventory_log_1230.trn
先查看备份内容:
RESTORE FILELISTONLY
FROM DISK = N'/var/opt/mssql/backup/Inventory_full.bak';
如果目标服务器上的数据文件路径不同,需要根据该结果使用 MOVE:
RESTORE DATABASE [Inventory]
FROM DISK = N'/var/opt/mssql/backup/Inventory_full.bak'
WITH
MOVE N'Inventory' TO N'/var/opt/mssql/data/Inventory.mdf',
MOVE N'Inventory_log' TO N'/var/opt/mssql/data/Inventory_log.ldf',
NORECOVERY,
CHECKSUM,
STATS = 10;
然后恢复差异备份:
RESTORE DATABASE [Inventory]
FROM DISK = N'/var/opt/mssql/backup/Inventory_diff.bak'
WITH
NORECOVERY,
CHECKSUM,
STATS = 10;
再按日志顺序恢复:
RESTORE LOG [Inventory]
FROM DISK = N'/var/opt/mssql/backup/Inventory_log_1200.trn'
WITH
NORECOVERY,
CHECKSUM,
STATS = 10;
RESTORE LOG [Inventory]
FROM DISK = N'/var/opt/mssql/backup/Inventory_log_1230.trn'
WITH
STOPAT = '2025-03-08T12:25:00',
RECOVERY,
CHECKSUM,
STATS = 10;
这里恢复顺序成立的原因是:
- 全量备份提供数据库基线;
- 差异备份提供自该全量以来变化的页面;
- 12:00 的日志备份补充差异备份之后的日志;
- 12:30 的日志备份包含 12:25 的目标时间;
STOPAT让恢复过程在目标时间停止,而不是应用该备份中的全部日志。
如果恢复的是新数据库,MOVE 中的逻辑文件名必须来自 RESTORE FILELISTONLY 的实际结果,不能把物理文件名或任意字符串当作逻辑文件名。
4. 尾日志备份
如果数据库仍然可以访问,但数据文件或实例即将损坏,故障前最后一段日志可能尚未进入常规日志备份。这时可以尝试尾日志备份:
BACKUP LOG [Inventory]
TO DISK = N'/var/opt/mssql/backup/Inventory_tail.trn'
WITH
NO_TRUNCATE,
NORECOVERY,
CHECKSUM,
STATS = 10;
NORECOVERY 会让源数据库进入还原状态,阻止普通连接继续使用它,因此必须确认这是恢复路径的一部分。
NO_TRUNCATE 不是“总能备份尾日志”的保证。数据库状态、文件可访问性和损坏程度都会影响结果。如果数据库无法读取所需日志,尾日志备份也可能失败。
五、备份验证不等于“备份命令成功”
1. 三种不同层次的验证
第一层:备份任务成功
SQL Agent、调度器或自动化系统报告命令返回成功。这只能说明本次操作没有报告错误。
第二层:备份介质可读
RESTORE VERIFYONLY
FROM DISK = N'/var/opt/mssql/backup/Inventory_full.bak'
WITH CHECKSUM;
VERIFYONLY 检查备份是否看起来完整、可用于还原,以及备份集是否满足基本格式要求。它不会创建数据库,也不会验证业务数据是否正确。
第三层:真正还原
最可靠的验证是在隔离环境中实际执行 RESTORE DATABASE,然后:
DBCC CHECKDB (N'Inventory') WITH NO_INFOMSGS;
DBCC CHECKDB 检查数据库结构和一致性,但它不能证明业务逻辑正确,也不能证明应用程序会正常工作。恢复演练还应验证:
- 登录名、用户和权限;
- Agent Job、凭据和外部依赖;
- 加密密钥和证书;
- 应用连接字符串;
- 数据库兼容级别和配置;
- 关键查询和写入流程。
2. 常见失败:只做备份,不做恢复演练
以下方案看起来合理,但不可靠:
每天全量备份;
每小时日志备份;
备份任务没有报错。
它仍可能在故障时失败,因为:
- 备份文件未复制到灾备位置;
- 恢复服务器磁盘路径不同;
- 缺少中间日志文件;
- 备份文件被覆盖;
- 加密证书没有保存;
- 数据库用户与实例登录名映射不正确;
- 恢复所需时间超过 RTO;
- 备份已经过保留期;
- 逻辑错误已经传播到所有在线副本。
六、Always On Availability Groups 的组件与数据流
1. Availability Group 的基本对象
Always On Availability Groups,简称 AG,是 SQL Server 的数据库级高可用和灾难恢复技术。其核心对象包括:
- Availability Group:包含一组需要一起提供高可用能力的用户数据库;
- Primary Replica:承担读写请求并产生事务日志;
- Secondary Replica:接收主副本发送的日志并应用;
- Availability Database:加入 AG 的具体数据库;
- Availability Group Listener:对应用提供稳定网络入口;
- WSFC:Windows Server Failover Clustering,负责集群成员、健康状态和仲裁;
- Database Mirroring Endpoint:副本之间传输数据库镜像/AG 数据流的端点。
AG 中的每个副本通常是一个 SQL Server 实例。数据库副本不共享同一份数据文件,而是各自拥有自己的数据库文件和日志文件。
2. 日志数据流
主副本上的事务通常经历如下路径:
客户端提交
↓
主副本生成日志记录
↓
写入主副本日志文件并硬化
↓
日志捕获与发送
↓
次副本接收日志块
↓
次副本硬化日志
↓
次副本 redo 线程重做日志
↓
次副本数据库状态前进
这里有三个容易混淆的时间点:
- 发送:日志已经离开主副本;
- 硬化:日志已经写入次副本持久存储;
- 重做:次副本已经把日志应用到数据页。
同步提交等待的是目标副本的日志硬化,而不是目标副本完成所有 redo。故障转移后,次副本可以先通过恢复流程处理已硬化但尚未重做的日志。
3. 同步提交与异步提交
同步提交
主副本提交事务时,需要等待配置的同步提交次副本确认日志已硬化。
特点:
- 正常条件下可以实现较低的数据丢失风险;
- 跨远距离部署时,提交延迟会受到网络往返时间和次副本存储延迟影响;
- “同步提交”不表示次副本已经完成查询可见的数据重做;
- “同步提交”也不等于任何强制故障转移都绝对零丢失。
异步提交
主副本不等待次副本硬化后再向客户端确认提交。
特点:
- 主库提交延迟较低;
- 次副本可能有发送队列或重做队列;
- 发生灾难时可能丢失尚未送达或尚未硬化的日志;
- 更适合远距离灾备,而不是把它当作无损本地高可用。
可以用一个简化的潜在数据丢失模型理解异步副本:
潜在丢失日志 ≈ 主副本已生成的日志
- 次副本已硬化的日志
这不是直接用于精确计算的 SQL 公式,但它说明了为什么“副本在线”不代表“副本已经包含全部提交”。
七、AG 的状态、故障转移和客户端连接
1. 副本状态不是只有“在线”和“离线”
常见状态包括:
ONLINE:副本实例可用;SYNCHRONIZING:次副本正在追赶;SYNCHRONIZED:同步提交关系中,数据库已达到同步状态;NOT SYNCHRONIZING:数据移动未运行或无法保持同步;SUSPENDED:数据库级数据移动被暂停;RESOLVING:集群正在处理主角色归属;FAILED_NO_QUORUM:集群缺少仲裁,无法安全决定角色。
数据库同步状态和副本连接状态必须结合判断。实例能 ping 通,不代表数据库日志传输正常。
2. 自动故障转移的必要条件
自动故障转移不是“主库进程退出就一定切换”。通常需要同时满足:
- AG 配置允许自动故障转移;
- 目标副本配置为同步提交;
- 目标数据库处于可同步状态;
- WSFC 仍能形成有效仲裁;
- 集群和 SQL Server 健康检测判定主副本不可用;
- 目标副本满足自动切换所需的角色和状态条件。
具体版本、部署拓扑和故障条件会影响判定。自动故障转移追求的是在安全条件下自动接管,而不是任何故障都强制接管。
3. 计划内手动切换
在主副本健康、目标副本同步完成时,可以执行计划内故障转移。命令应在目标次副本上执行:
ALTER AVAILABILITY GROUP [AG_Inventory] FAILOVER;
该操作的前置条件包括:
- 目标副本已经配置为同步提交;
- 目标数据库显示为同步状态;
- 应用已准备好处理连接中断和重连;
- 运维人员确认切换窗口;
- 客户端通过 Listener 连接,而不是硬编码当前主机名。
计划内切换的关键是先让数据状态收敛,再改变主角色。它不同于强制故障转移。
4. 强制故障转移
当主副本不可用、WSFC 或网络隔离导致无法完成正常协调时,可能需要在次副本上执行:
ALTER AVAILABILITY GROUP [AG_Inventory]
FORCE_FAILOVER_ALLOW_DATA_LOSS;
这个命令明确包含 ALLOW_DATA_LOSS,因为 SQL Server 无法证明次副本拥有主副本已经提交的全部事务。
强制切换后可能出现两类问题:
- 次副本成为新的主副本,但旧主副本后来恢复并仍认为自己可写;
- 旧主副本包含新主副本没有的数据,重新加入 AG 时产生分叉或需要重新同步。
因此强制切换后的流程不是“命令成功就结束”,还必须:
- 隔离旧主副本,避免双主写入;
- 检查应用连接是否已转移;
- 评估丢失的事务;
- 处理旧副本的恢复、重新加入或重新初始化;
- 对受影响业务进行数据校验。
5. Listener 与读写路由
应用应连接 AG Listener,而不是直接连接某个副本实例。Listener 提供:
- 稳定的 DNS 名称;
- 虚拟网络名和端口;
- 当前主副本定位;
- 在配置正确时的只读路由能力。
只读路由需要同时满足多个条件:
- 客户端连接意图为
ReadOnly; - AG 配置了只读路由 URL;
- 当前副本允许读取;
- 应用驱动支持相关连接属性;
- 连接字符串使用正确的 Listener 和端口。
例如,应用连接字符串通常需要包含类似 ApplicationIntent=ReadOnly 的客户端属性,但具体语法取决于驱动。仅仅连接到次副本并不能自动保证查询被路由到期望节点。
八、AG 配置前置条件与生命周期
1. 配置不是单条 CREATE 语句
建立 AG 前,需要完成一组实例级准备:
- 实例使用支持 AG 的版本和 Edition;
- 所有副本实例的版本兼容;
- 配置 WSFC 和集群节点;
- 启用 Always On;
- 为每个实例配置数据库镜像端点;
- 配置端点认证和网络访问;
- 为次副本准备数据库;
- 使数据库满足加入 AG 的条件;
- 创建 AG;
- 将次副本数据库加入并开始数据同步;
- 创建 Listener;
- 用实际客户端验证读写、切换和重连。
SQL Server Configuration Manager 通常用于启用实例的 Always On 功能和管理服务。端点可以通过 T-SQL 创建,例如:
CREATE ENDPOINT [Hadr_endpoint]
STATE = STARTED
AS TCP (LISTENER_PORT = 5022)
FOR DATABASE_MIRRORING
(
ROLE = ALL,
AUTHENTICATION = WINDOWS NEGOTIATE,
ENCRYPTION = REQUIRED ALGORITHM AES
);
这条语句并不意味着 AG 已经可用。前置条件仍包括:
- 端口 5022 在副本之间可达;
- 运行 SQL Server 服务的账户在对端点执行连接时具有所需权限;
- 端点名称和端口没有冲突;
- 防火墙和网络设备允许双向通信;
- 所有实例的端点状态为
STARTED。
生产环境不应直接复制端点配置而忽略认证、服务账户和证书要求。Windows 身份验证、证书身份验证和网络拓扑必须与实际部署一致。
2. 数据库如何加入 AG
常见方式是:
- 在主副本创建数据库;
- 切换到完整恢复模型;
- 创建至少一个全量备份;
- 将全量备份和连续日志备份还原到次副本,使用
NORECOVERY; - 在 AG 中添加数据库;
- 在次副本执行加入数据库操作;
- 等待同步状态收敛。
示例:
ALTER DATABASE [Inventory]
SET RECOVERY FULL;
GO
BACKUP DATABASE [Inventory]
TO DISK = N'/var/opt/mssql/backup/Inventory_ag_seed_full.bak'
WITH COPY_ONLY, COMPRESSION, CHECKSUM;
GO
在次副本:
RESTORE DATABASE [Inventory]
FROM DISK = N'/var/opt/mssql/backup/Inventory_ag_seed_full.bak'
WITH NORECOVERY, CHECKSUM;
GO
如果全量备份之后又产生了日志,需要继续按 LSN 顺序还原日志:
RESTORE LOG [Inventory]
FROM DISK = N'/var/opt/mssql/backup/Inventory_ag_seed_log.trn'
WITH NORECOVERY, CHECKSUM;
GO
之后才可以将数据库加入 AG。若次副本数据库已经恢复并打开,或者缺少中间日志,加入过程可能失败,或者需要重新初始化。
九、FCI、AG 和 Log Shipping 的取舍
1. FCI:实例级切换,共享存储
Failover Cluster Instance 是一个 SQL Server 实例资源组,多个节点访问同一套共享存储。故障时实例资源转移到另一个节点。
优势:
- 对应用通常表现为同一个实例;
- 不需要为每个数据库维护独立副本;
- 实例级对象和数据库一起切换。
边界:
- 共享存储本身可能成为故障域;
- 不能天然解决跨站点存储灾难;
- 节点切换期间实例需要重新启动或恢复;
- 它不是独立存储副本。
2. AG:数据库级副本
AG 每个副本有独立存储,通过日志传输保持一致。
优势:
- 支持同步和异步提交;
- 可以跨机房部署;
- 支持读扩展和备份卸载等能力;
- 故障转移粒度是数据库组。
边界:
- 需要管理副本、端点、WSFC、Listener 和数据库状态;
- AG 只保护加入 AG 的用户数据库;
master、msdb等系统数据库不会因为 AG 自动同步;- 登录名、Agent Job、Linked Server、凭据等实例级对象需要单独同步。
3. Log Shipping:基于备份的简单灾备
Log Shipping 通过定时任务完成:
主服务器备份日志
↓
复制 .trn 文件
↓
灾备服务器还原日志
它通常具有较大的切换延迟,但结构简单、跨机房适应性较好,也可以作为比 AG 更低成本的灾备方案。
它的限制是:
- 不是实时副本;
- 切换通常需要人工或额外自动化;
- 目标库通常处于
STANDBY或NORECOVERY; - 应用切换、登录同步和回切需要额外设计。
十、监控:不要只监控 SQL Server 进程
高可用监控至少要覆盖四层:
业务连接层
↓
Listener、DNS、端口和驱动重连
数据库副本层
↓
角色、同步状态、挂起状态、发送与重做队列
实例资源层
↓
CPU、内存、I/O、Worker、锁和等待
备份恢复层
↓
最近成功备份、日志链、备份可读性、恢复演练
1. 数据库状态与日志复用
SELECT
name,
state_desc,
user_access_desc,
recovery_model_desc,
log_reuse_wait_desc
FROM sys.databases
ORDER BY name;
重点观察:
- 数据库是否为
ONLINE; - 是否意外变为
SUSPECT、RECOVERY_PENDING或EMERGENCY; - 生产数据库是否错误地处于
SIMPLE; log_reuse_wait_desc是否长期为LOG_BACKUP、AVAILABILITY_REPLICA、ACTIVE_TRANSACTION或其他原因。
ACTIVE_TRANSACTION 需要调查长事务;AVAILABILITY_REPLICA 需要调查副本数据移动和次副本存储,而不是简单收缩日志。
2. 日志空间
在较新的受支持版本中,可以使用:
USE [Inventory];
GO
SELECT
total_log_size_in_bytes / 1024.0 / 1024.0 AS total_log_size_mb,
used_log_space_in_percent,
used_log_space_in_bytes / 1024.0 / 1024.0 AS used_log_space_mb
FROM sys.dm_db_log_space_usage;
这个 DMV 反映当前数据库日志空间使用情况。日志文件增长时,应同时回答:
- 业务是否产生了异常写入量;
- 日志备份是否停止;
- 是否存在长事务;
- AG 次副本是否落后;
- 是否正在进行大事务、索引操作或批量导入。
直接执行 DBCC SHRINKFILE 只能缩小已经不再需要的尾部空间,不能修复持续增长原因。频繁收缩还会造成文件反复增长和物理碎片。
3. AG 副本状态
副本级状态:
SELECT
ar.replica_server_name,
ars.role_desc,
ars.connected_state_desc,
ars.operational_state_desc,
ars.recovery_health_desc,
ars.synchronization_health_desc
FROM sys.availability_replicas AS ar
JOIN sys.dm_hadr_availability_replica_states AS ars
ON ar.replica_id = ars.replica_id;
数据库级状态:
SELECT
DB_NAME(drs.database_id) AS database_name,
ar.replica_server_name,
ars.role_desc,
drs.synchronization_state_desc,
drs.synchronization_health_desc,
drs.database_state_desc,
drs.is_suspended,
drs.suspend_reason_desc,
drs.log_send_queue_size,
drs.log_send_rate,
drs.redo_queue_size,
drs.redo_rate,
drs.last_commit_time
FROM sys.dm_hadr_database_replica_states AS drs
JOIN sys.availability_replicas AS ar
ON drs.replica_id = ar.replica_id
JOIN sys.dm_hadr_availability_replica_states AS ars
ON drs.replica_id = ars.replica_id
AND drs.group_id = ars.group_id
WHERE drs.is_local = 1;
这些字段需要结合解释:
log_send_queue_size:主副本尚未发送到次副本的日志量,通常以 KB 表示;log_send_rate:日志发送速率,通常以 KB/s 表示;redo_queue_size:次副本已收到但尚未重做的日志量;redo_rate:次副本重做速率;is_suspended:该数据库的数据移动是否被暂停;suspend_reason_desc:暂停原因;last_commit_time:副本上最近提交相关状态的时间。
在速率稳定且不为零时,可以粗略估算追赶时间:
发送追赶时间 ≈ log_send_queue_size / log_send_rate
重做追赶时间 ≈ redo_queue_size / redo_rate
但这是诊断估算,不是 SLA 保证。速率会受到磁盘、网络、日志生成速率、锁和查询负载影响。若速率为零,不能用除法得出“无限大”之外的有意义结论,而应先检查连接、端点、暂停状态和错误日志。
4. 集群和 Listener 不能漏监控
AG 依赖的健康范围还包括:
- WSFC 节点和仲裁;
- 集群网络名称资源;
- Listener IP 和 DNS;
- SQL Server 服务状态;
- HADR endpoint;
- 防火墙端口;
- 见证资源或云见证配置;
- 客户端驱动的多子网重连行为。
数据库副本显示 SYNCHRONIZED,但 Listener DNS 已失效,应用仍然无法连接。反过来,Listener 可以解析,但数据库已经停止数据移动,也不代表目标副本可安全接管。
十一、从故障现象反推原因
1. 日志文件持续增长
可能原因:
- 未执行日志备份;
- 长事务未结束;
- AG 次副本落后;
- 数据库镜像或复制相关消费者未推进;
- 大型事务或批处理正在运行。
诊断顺序:
SELECT
name,
recovery_model_desc,
log_reuse_wait_desc
FROM sys.databases
WHERE name = N'Inventory';
然后检查:
- SQL Agent 日志备份 Job;
msdb.dbo.backupset的最近日志备份;- AG 发送和重做队列;
- 活跃事务;
- SQL Server 错误日志。
不要在不了解原因时切换到 SIMPLE、删除日志文件或反复收缩日志。这样可能破坏原本需要的日志链,且无法解决 AG 或长事务导致的等待。
2. 次副本“在线”但不断落后
需要先区分是发送队列还是重做队列:
log_send_queue_size大:主库到次库的网络、端点或次库接收能力有问题;redo_queue_size大:日志已到达次库,但次库重做能力不足;- 两者都小但状态异常:检查连接状态、暂停状态、数据库错误和集群状态。
常见根因包括:
- 次副本存储延迟高;
- 次副本 CPU 或内存不足;
- 网络抖动;
- HADR endpoint 未启动;
- 数据库数据移动被暂停;
- 次副本长时间执行阻塞或资源密集型操作。
3. AG 自动切换没有发生
不要直接得出“AG 失效”的结论。应验证:
- 当前主副本和目标副本的角色;
- 目标副本是否同步提交;
- 数据库是否为
SYNCHRONIZED; - 副本是否配置自动故障转移;
- WSFC 是否有仲裁;
- 是否发生网络分区;
- SQL Server 错误日志和集群日志中的故障检测结果;
- 自动故障转移是否被维护操作、暂停或不支持的状态阻止。
自动切换的保守行为是设计的一部分:在无法证明安全时不切换,比同时出现两个可写主库更安全。
4. 故障转移后应用仍报错
可能原因:
- 应用连接的是实例主机名而不是 Listener;
- DNS 缓存未刷新;
- 客户端驱动不支持或未启用多子网重连;
- 登录名只在旧实例存在;
- 数据库用户与新实例登录名 SID 不匹配;
- 应用连接池保留了旧连接;
- 防火墙未允许新路径;
- 应用将只读连接错误地发送到主副本,或反过来。
AG 不会自动同步实例级对象。登录名、Job、Linked Server、凭据、数据库邮件、服务器级配置和证书都要纳入部署与灾备管理。
十二、故障恢复路径
路径 A:主副本仍可访问,计划内切换
这是风险最低的切换类型:
- 确认目标副本健康;
- 确认目标数据库为同步状态;
- 停止或排空会产生新写入的维护任务;
- 执行计划内故障转移;
- 通过 Listener 验证写入;
- 验证新主副本继续执行日志备份;
- 检查旧主副本是否转为次副本;
- 验证应用连接池和后台任务。
路径 B:主副本不可用,但同步副本可接管
如果集群仍可协调,优先使用正常故障转移。若无法完成正常切换但确认业务必须恢复,可以考虑强制故障转移。
关键风险是:同步配置降低了数据丢失概率,但强制切换时如果存在未确认的提交、网络隔离或集群状态不完整,仍不能把“同步”解释为无条件零丢失。
路径 C:强制切换后旧主副本恢复
必须首先防止旧主副本重新对外提供写服务。然后:
- 确认新主副本的业务状态;
- 记录可能丢失或分叉的数据范围;
- 检查旧副本是否还能作为次副本加入;
- 必要时从新主副本重新初始化旧副本;
- 对应用关键数据进行对账;
- 重新建立备份与监控链路。
最危险的操作不是“暂时没有副本”,而是两个实例都被应用认为是主库并同时接受写入。
路径 D:整组副本和存储均不可用
这时进入备份恢复流程:
- 找到最近可用全量备份;
- 找到对应差异备份;
- 按 LSN 顺序收集连续日志备份;
- 如果源服务器仍可访问,尝试尾日志备份;
- 在目标实例恢复数据库;
- 执行
DBCC CHECKDB; - 恢复登录名、权限、Job、证书和应用配置;
- 用业务查询验证数据;
- 切换连接入口;
- 记录恢复耗时和实际数据丢失点。
若目标时间是逻辑错误发生之前,而不是服务器宕机时间,应使用 PITR。例如,误删在 12:05 发生,不能简单恢复到 12:30;应恢复到 12:04:59 附近,然后从业务日志或其他来源重放合法操作。
十三、备份与 AG 的组合设计
一种常见的组合如下:
本地同步副本
└── 应对节点或实例故障,缩短 RTO
异地异步副本
└── 应对机房级故障,接受一定 RPO
独立备份存储
└── 应对误删、逻辑错误、全副本损坏和时间点恢复
备份任务可以按照 AG 的备份偏好选择副本执行,但“首选备份副本”是调度建议,不是安全边界。必须验证:
- 实际执行备份的是哪个副本;
- 备份文件写到了哪里;
- 日志备份是否仍保持连续链;
- 备用副本切换后备份任务是否继续工作;
- 备份历史是否能正确记录;
- 备份文件是否被复制到与所有副本不同的故障域。
独立备份存储至少应避免与数据库数据文件、同一台虚拟机和同一存储阵列共享单一故障点。更高要求的环境还要考虑删除保护、不可变存储、访问控制和加密密钥保管。
十四、恢复演练应该验证什么
一次完整演练不应只执行:
RESTORE VERIFYONLY ...
而应至少验证以下流程:
- 从备份清单找到目标全量备份;
- 找到匹配的差异备份;
- 找到连续的日志备份;
- 在隔离 SQL Server 实例还原;
- 执行
DBCC CHECKDB; - 检查关键表的行数、校验和或业务汇总;
- 测试登录和权限;
- 测试应用连接;
- 测试 Listener 或灾备入口;
- 测量从开始恢复到业务可用的时间;
- 记录实际可恢复到的时间点;
- 模拟备份缺失、磁盘路径不同和凭据失效等错误。
演练的输出应是可验证的事实:
最近可恢复时间:2025-03-08 12:25:00
恢复开始时间:13:00:00
数据库打开时间:13:18:00
应用验证完成:13:27:00
实际 RPO:约 5 分钟
实际 RTO:27 分钟
这些结果比“配置了高可用”“每天都有备份”更能说明系统是否满足业务目标。
十五、几个必须纠正的误解
误解一:同步提交等于永不丢数据
同步提交表示事务提交需要等待同步副本的日志硬化,但强制故障转移、网络隔离、集群判定和未完成协调都可能改变结果。正常计划内切换与强制切换必须分开讨论。
误解二:AG 副本可以替代备份
逻辑删除、错误更新、恶意操作会被复制。副本能提供另一个服务实例,却不能提供任意历史时间点。
误解三:全量备份后就不需要日志备份
在 FULL 或 BULK_LOGGED 模型下,没有持续日志备份就没有完整的时间点恢复链,也可能导致日志无法截断。
误解四:Listener 可连接就说明数据库同步正常
Listener 只说明网络入口可能可达。必须分别检查数据库同步状态、日志发送队列、重做队列和数据移动是否暂停。
误解五:RESTORE VERIFYONLY 通过就完成了恢复验证
它不能替代实际还原、数据库一致性检查和应用验证。只有真正恢复并测试,才能知道备份链、目标路径、权限和依赖是否可用。
误解六:把日志文件收缩到很小可以解决日志问题
收缩只能处理暂时不再需要的尾部空间,不能解决长事务、缺少日志备份、AG 副本落后或持续高写入。错误收缩还会让日志反复增长,增加 I/O 和碎片问题。
结语
SQL Server 高可用的核心不是某个单独功能,而是一条完整的数据与故障路径:
事务日志产生
→ 日志硬化
→ 备份或副本传输
→ 目标副本硬化与重做
→ 状态监控
→ 计划内或强制切换
→ 必要时基于备份进行时间点恢复
Backup 提供历史版本和灾难后的恢复能力;Always On 提供数据库级副本和快速切换;FCI 提供实例级共享存储切换;Log Shipping 提供相对简单的异地日志灾备。只有把它们与 LSN、恢复模型、Listener、WSFC、监控和恢复演练结合起来,RPO 与 RTO 才具有可验证的含义。
系列导航与关联阅读
- 系列入口:数据库完整学习路线:从关系模型、事务索引到分布式与向量检索
- 上一篇:SQL Server 数据库引擎:存储、事务日志、锁、索引和执行计划
- 下一篇:Cassandra 分布式数据模型:Partition Key、Clustering 与查询驱动设计
- 延伸:数据库备份与恢复:全量、增量、PITR、RPO/RTO 和恢复演练
官方资料
本文依据数据库官方文档重新梳理;正文、示例与生产检查清单由 WR BLOG 编写。

评论
0 条讨论