备份恢复 & 迁移30 分钟阅读
SQL Server 备份恢复与日志链检查 100 条命令
SQL Server 做时间点恢复,要有可用的完整备份和连续的事务日志备份;若想利用差异备份缩短恢复时间,还得找对它的完整备份基。备份任务每天报成功,真正出事时却可能找不到文件、差异备份接错基,或者日志链中间缺一段。恢复前把这些关系对好,比临时打开一个 `.bak` 文件盲试更稳。
2026年9月16日阅读—点赞—收藏—
dba100sqlserverscenario
100 条命令系列文章专栏
SQL Server 做时间点恢复,要有可用的完整备份和连续的事务日志备份;若想利用差异备份缩短恢复时间,还得找对它的完整备份基。备份任务每天报成功,真正出事时却可能找不到文件、差异备份接错基,或者日志链中间缺一段。恢复前把这些关系对好,比临时打开一个 `.bak` 文件盲试更稳。
SQL Server 做时间点恢复,要有可用的完整备份和连续的事务日志备份;若想利用差异备份缩短恢复时间,还得找对它的完整备份基。备份任务每天报成功,真正出事时却可能找不到文件、差异备份接错基,或者日志链中间缺一段。恢复前把这些关系对好,比临时打开一个 .bak 文件盲试更稳。
本文先从 msdb 找备份清单和 LSN,再去读实际介质,最后在测试实例按恢复顺序演练。建议收藏,故障时可以照着一项项核对。
示例采用 SQL Server 2022、SalesDB 的 FULL 恢复模式和本地磁盘备份。文件路径、库名与时间按现场替换。msdb 历史只能说明 SQL Server 曾记录这些备份,不能证明文件还在;真正的 RESTORE DATABASE 留到隔离测试实例。
SQL Server 完整备份、差异备份与连续日志链的恢复顺序示意
图中采用“完整备份 + 差异备份 + 日志”的恢复路径。差异备份可不选;选用时必须匹配自己的完整备份基,日志备份要覆盖从选定数据备份到目标时间的连续区间。
1SELECT name, recovery_model_desc, state_desc2FROM sys.databases3WHERE name = N'SalesDB';FULL 才能按连续日志备份做通常的时间点恢复。当前模式是 FULL,也不能证明它过去一直如此;后面还要看每份备份记录时的恢复模式与日志链起点。
1SELECT backup_set_id, type, is_copy_only,2 backup_start_date, backup_finish_date,3 recovery_model, server_name4FROM msdb.dbo.backupset5WHERE database_name = N'SalesDB'6AND type IN ('D', 'I', 'L')7ORDER BY backup_finish_date DESC;D 是完整备份、I 是差异备份、L 是日志备份;is_copy_only=1 要单独认出来。这里先确定候选顺序,不能仅靠“最近的一份”决定恢复链,更不能把备份历史当成文件盘点。
1SELECT bs.backup_set_id, bs.type, bs.backup_finish_date,2 mf.family_sequence_number, mf.physical_device_name3FROM msdb.dbo.backupset AS bs4JOIN msdb.dbo.backupmediafamily AS mf5 ON mf.media_set_id = bs.media_set_id6WHERE bs.database_name = N'SalesDB'7ORDER BY bs.backup_finish_date DESC,8 mf.family_sequence_number;多条 family_sequence_number 可能是一份条带化备份的多个文件,恢复时要把这些文件一起提供。physical_device_name 是历史记录,文件可能已被移动或清理;拿它找文件后,还要读实际备份头。
1SELECT backup_set_id, backup_finish_date,2 checkpoint_lsn, database_backup_lsn,3 is_copy_only4FROM msdb.dbo.backupset5WHERE database_name = N'SalesDB'6AND type = 'D'7ORDER BY backup_finish_date DESC;完整备份是差异备份的基。COPY_ONLY 完整备份不会像普通完整备份那样重置差异基;看到最近一个 .bak 是 copy-only 时,不能把它直接当作后续差异备份的基。
1SELECT backup_set_id, backup_finish_date,2 differential_base_lsn, differential_base_guid,3 database_backup_lsn4FROM msdb.dbo.backupset5WHERE database_name = N'SalesDB'6AND type = 'I'7ORDER BY backup_finish_date DESC;单一差异基可从 differential_base_lsn、GUID 核对;若字段为 NULL,可能是多文件差异基,需要下探 msdb.dbo.backupfile,不能把空值当作“没有完整备份基”。实际恢复应读备份头再验证选定的完整与差异文件是否匹配。
1SELECT backup_set_id, backup_start_date,2 backup_finish_date, first_lsn, last_lsn,3 begins_log_chain, recovery_model4FROM msdb.dbo.backupset5WHERE database_name = N'SalesDB'6AND type = 'L'7ORDER BY backup_start_date, backup_set_id;last_lsn 指备份之后下一条日志记录的位置。按恢复目标挑日志段时,确认区间覆盖目标且没有断层;相邻日志备份可以重叠,不能硬要求上一段 last_lsn 与下一段 first_lsn 数值完全相等。
1SELECT backup_set_id, type,2 first_recovery_fork_guid,3 last_recovery_fork_guid, fork_point_lsn4FROM msdb.dbo.backupset5WHERE database_name = N'SalesDB'6AND type IN ('D', 'I', 'L')7ORDER BY backup_finish_date;曾经 RESTORE ... WITH RECOVERY 或发生别的恢复分叉后,时间上紧邻的两份日志备份也可能不在同一条可用恢复路径上。先核对 fork GUID 和转折点,再把目标时间映射到正确的备份链。
1-- 路径是 SQL Server 服务端可访问的实际文件;按现场替换2RESTORE HEADERONLY3FROM DISK = N'D:\SqlBackup\SalesDB_full_20260910.bak';一个介质里可能存着多个备份集,先从输出核对 Position、库名、备份类型、开始与完成时间、LSN。第 3 条的历史路径只用来找文件,实际可读性以这里的介质回读为准。
1-- FILE=1 必须与第 8 条选定的备份集 Position 对应2RESTORE FILELISTONLY3FROM DISK = N'D:\SqlBackup\SalesDB_full_20260910.bak'4WITH FILE = 1;LogicalName 是后续 RESTORE ... WITH MOVE 需要的逻辑文件名,PhysicalName 是备份时源库文件路径。恢复到测试实例时应显式映射目标文件,别让源路径在测试机上碰巧指向现有数据库文件。
1-- 文件及 Position 已核对;验证过程会读取备份介质2RESTORE VERIFYONLY3FROM DISK = N'D:\SqlBackup\SalesDB_full_20260910.bak'4WITH FILE = 1, CHECKSUM;VERIFYONLY 检查备份集完整、介质可读,不真正恢复数据库,也不验证数据结构。CHECKSUM 可利用备份校验信息;验证成功之后仍需在测试实例做一次实际恢复,才能证明文件映射、日志链和数据库打开流程可用。
1-- 差异备份是可选路径;文件与选定完整备份基先在 msdb 核对2RESTORE HEADERONLY3FROM DISK = N'D:\SqlBackup\SalesDB_diff_20260911.bak';输出里先确认这是数据库差异备份、库名和差异基,再与第 8 条的完整备份头核对。I 是 msdb.dbo.backupset.type 的记号,备份头里的 BackupType 使用另一套编码;差异文件较新,不代表一定能接在任意较老的完整备份后面。
1-- FILE 位置以第 11 条实际输出为准2RESTORE VERIFYONLY3FROM DISK = N'D:\SqlBackup\SalesDB_diff_20260911.bak'4WITH FILE = 1, CHECKSUM;文件能读是使用它的必要条件,不是差异基匹配或恢复成功的证明。差异介质若是条带化文件,必须列出同一备份集的所有条带,不能只验其中一个。
1-- 从第 6 条的候选日志链中选实际介质2RESTORE HEADERONLY3FROM DISK = N'D:\SqlBackup\SalesDB_log_20260911_0900.trn';从头部确认备份类型、FirstLSN、LastLSN、恢复分叉和 Position。msdb 记录可能已经清理或迁移;当历史与介质头不一致时,以实际文件为恢复方案重新排链。
1-- 对恢复链里每一份日志文件分别执行,Position 按头部调整2RESTORE VERIFYONLY3FROM DISK = N'D:\SqlBackup\SalesDB_log_20260911_0900.trn'4WITH FILE = 1, CHECKSUM;不能只验第一份和最后一份日志,中间少一个文件就会断链。验证通过后,还要在测试实例按顺序恢复到目标时间;VERIFYONLY 自身不执行日志回放。
1-- 仅在隔离测试实例;确认目标库不存在、FILE=1 和逻辑文件名2IF DB_ID(N'SalesDB_Rehearsal') IS NOT NULL3 THROW 50001, 'SalesDB_Rehearsal already exists; stop the restore', 1;45RESTORE DATABASE SalesDB_Rehearsal6FROM DISK = N'D:\SqlBackup\SalesDB_full_20260910.bak'7WITH FILE = 1, NORECOVERY,8 MOVE N'SalesDB' TO N'E:\Rehearsal\SalesDB_Rehearsal.mdf',9 MOVE N'SalesDB_log' TO N'E:\Rehearsal\SalesDB_Rehearsal.ldf',10 STATS = 5;NORECOVERY 留住继续应用差异和日志的入口。两个 MOVE 的逻辑名必须来自第 9 条的实际 FILELISTONLY;目标路径要位于隔离实例,不许套用源库原路径。执行前还要确认目标数据库名称不存在,避免覆盖现有库。
1-- 仅在隔离测试实例;已核对该差异备份对应第 15 条完整备份2RESTORE DATABASE SalesDB_Rehearsal3FROM DISK = N'D:\SqlBackup\SalesDB_diff_20260911.bak'4WITH FILE = 1, NORECOVERY, STATS = 5;没选差异备份就跳过这条,直接从完整备份之后的第一份所需日志开始。若差异基不匹配,不能用 WITH REPLACE 硬接;应重新选完整备份或差异备份。
1-- 仅在隔离测试实例;按实际日志链从第一份所需文件开始2RESTORE LOG SalesDB_Rehearsal3FROM DISK = N'D:\SqlBackup\SalesDB_log_20260911_0900.trn'4WITH FILE = 1, NORECOVERY,5 STOPAT = '2026-09-11T10:15:00', STATS = 5;时间点恢复时,每一份 RESTORE LOG 都用相同的 STOPAT,避免前面的日志已越过目标时间。第一份日志完成后继续保持 NORECOVERY;如果它已包含目标点,需按实际备份链调整最后一步。
1-- 仅在隔离测试实例;示例第二份日志确实覆盖 10:15 目标点2RESTORE LOG SalesDB_Rehearsal3FROM DISK = N'D:\SqlBackup\SalesDB_log_20260911_1100.trn'4WITH FILE = 1, STOPAT = '2026-09-11T10:15:00',5 RECOVERY, STATS = 5;这里只是示例顺序,目标时间必须落在该日志文件的可恢复范围内。若系统提示目标点超出日志范围而库仍未恢复,先核对实际文件和时间,不要临时改成 RECOVERY 打开一个错误时间点。测试库打开后,还要核对对象、关键数据和应用连接。
1-- 源库在线时执行;仅供判断尾日志规模2SELECT database_id, log_backup_time,3 log_since_last_log_backup_mb,4 log_truncation_holdup_reason5FROM sys.dm_db_log_stats(DB_ID(N'SalesDB'));这个数字说明最近一次日志备份之后又产生了多少日志,并不等于确定会丢多少事务。源库还能访问、且要恢复到最近备份之后的时间点时,应考虑尾日志备份;若目标时间已落在现有备份内,就没必要为凑流程多做一份。
1-- 仅在确定要恢复或替换源库时执行;会把源库置为 RESTORING2BACKUP LOG SalesDB3TO DISK = N'D:\SqlBackup\SalesDB_tail_20260911.trn'4WITH NORECOVERY, CHECKSUM, STATS = 5;NORECOVERY 阻止新的写入进入当前恢复链,也会改变源库状态。千万不要把它当成普通巡检命令。备份完成后按第 13、14 条检查尾日志介质,并把它排在其他日志备份之后。
1-- 源库已离线,日志文件仍可读;不要在健康在线库常规使用2BACKUP LOG SalesDB3TO DISK = N'D:\SqlBackup\SalesDB_tail_offline.trn'4WITH NO_TRUNCATE, CHECKSUM, STATS = 5;NO_TRUNCATE 用于离线或受损的特殊情形,相当于不截断日志的尾日志备份。文件损坏、状态不支持或包含 bulk-logged 更改时可能做不出来;失败后不能假称“已经保住最后一段事务”。
1-- 只在隔离测试实例的 msdb 执行2SELECT rh.restore_date, rh.restore_type,3 rh.destination_database_name, bs.database_name,4 bs.type AS backup_type, bs.first_lsn, bs.last_lsn5FROM msdb.dbo.restorehistory AS rh6JOIN msdb.dbo.backupset AS bs7 ON bs.backup_set_id = rh.backup_set_id8WHERE rh.destination_database_name = N'SalesDB_Rehearsal'9ORDER BY rh.restore_date, rh.restore_history_id;这能把完整、差异和日志恢复的执行顺序与备份集对起来。历史表证明 SQL Server 记录过恢复操作,不能代替测试库实际打开、关键业务数据核对和应用连接测试。
1-- 仅在隔离测试实例2SELECT name, state_desc, recovery_model_desc,3 create_date4FROM sys.databases5WHERE name = N'SalesDB_Rehearsal';ONLINE 是后续验收的起点。若库仍是 RESTORING,先核对计划中还有没有日志要应用;不要为让状态好看就执行 WITH RECOVERY,否则会关闭继续追加日志的入口。
1SELECT backup_set_id, type, backup_finish_date,2 has_backup_checksums, is_damaged,3 has_incomplete_metadata4FROM msdb.dbo.backupset5WHERE database_name = N'SalesDB'6ORDER BY backup_finish_date DESC;has_backup_checksums=1 说明备份时写入了备份校验和;is_damaged=1 表示备份期间检测到损坏却继续完成。尾日志备份若元数据不完整也要单独识别。三个标志都正常,仍需实际介质验证和隔离恢复。
1-- backup_set_id 取第 5 条选中的差异备份2SELECT logical_name, file_type,3 differential_base_lsn, differential_base_guid,4 is_present5FROM msdb.dbo.backupfile6WHERE backup_set_id = 123457ORDER BY logical_name;差异备份的 backupset.differential_base_lsn 为 NULL 时,要逐数据文件看基线。这里的 12345 必须换成实际备份集 ID;不能只挑一个文件的基线就断定整库差异备份可接。
1SELECT backup_set_id, backup_start_date,2 backup_finish_date, recovery_model,3 has_bulk_logged_data4FROM msdb.dbo.backupset5WHERE database_name = N'SalesDB'6 AND type = 'L'7ORDER BY backup_start_date;包含 bulk-logged 更改的日志备份可能不能停在其中任意时间点。准备 STOPAT 时先看覆盖目标时间的那份日志;该标记为 1 时应按实际操作和官方恢复限制重新确认方案。
1-- 仅当尾日志介质已经验证、且仍需继续追到目标点2RESTORE LOG SalesDB_Rehearsal3FROM DISK = N'D:\SqlBackup\SalesDB_tail_20260911.trn'4WITH FILE = 1, NORECOVERY,5 STOPAT = '2026-09-11T10:15:00', STATS = 5;这条是尾日志应用示例,不应在第 18 条已用 RECOVERY 打开测试库之后再执行。选择含尾日志的恢复链时,前面的各份日志也应保持 NORECOVERY,最后再统一打开库。
1-- 只在确认目标时间、最后一份日志和恢复顺序后执行2RESTORE DATABASE SalesDB_Rehearsal WITH RECOVERY;WITH RECOVERY 会执行回滚并关闭继续追加日志的入口。如果还缺一份日志,先别运行;打开库之后发现目标时间错了,必须从完整备份重新恢复一遍,不能再往已打开的库上补日志。
1-- 仅隔离测试实例;大库执行时间和空间提前评估2DBCC CHECKDB (N'SalesDB_Rehearsal') WITH NO_INFOMSGS;介质验证成功之后还要查数据库一致性。CHECKDB 发现错误时保存完整输出,先判断源备份、恢复过程和测试存储哪一环出了问题;不要在演练库上直接用 REPAIR_ALLOW_DATA_LOSS 把错误“修没”。
1-- dbo.Orders 是示例,按现场的核心业务表替换2SELECT OBJECT_ID(N'SalesDB_Rehearsal.dbo.Orders', N'U')3 AS orders_object_id;库处于 ONLINE 不代表业务对象齐全。先找关键表、视图和程序,再验证数据;NULL 可能是对象缺失,也可能是库名、模式名或对象类型写错。
1-- 表、键与预期值来自业务事故记录,不是固定模板2SELECT order_id, status, updated_at3FROM SalesDB_Rehearsal.dbo.Orders4WHERE order_id = 202609110015;时间点恢复真正要回答的是“这笔事务在不在”。拿应用流水、源库故障前的记录或事件日志给出预期值;单独查出一行,并不能证明恢复时间就是要求的那个时间点。
1-- 仅隔离测试实例2SELECT name, type_desc, physical_name, state_desc3FROM sys.master_files4WHERE database_id = DB_ID(N'SalesDB_Rehearsal')5ORDER BY file_id;检查 MOVE 后文件是否落在测试目录,数据库的所有文件是否存在。若源库不止第 15 条示例中的一个数据文件和一个日志文件,要按 FILELISTONLY 为每个文件安排目标路径。
1SELECT backup_set_id, type, backup_finish_date,2 software_major_version, software_minor_version,3 software_build_version4FROM msdb.dbo.backupset5WHERE database_name = N'SalesDB'6ORDER BY backup_finish_date;恢复目标实例不能低于备份源的兼容版本。这里先看历史记录,再由实际介质头复核;msdb 是哪台机器的历史,也要在跨实例演练时记清楚。
1SELECT backup_set_id, type, backup_finish_date,2 key_algorithm, encryptor_type, encryptor_thumbprint3FROM msdb.dbo.backupset4WHERE database_name = N'SalesDB'5ORDER BY backup_finish_date;备份加密后,目标测试实例还要具备相应的证书或非对称密钥。备份文件能复制过去,不代表新实例一定能解密;恢复演练时就该把密钥依赖测出来。
1-- 仅隔离测试实例;时间是恢复开始时间,不是持续时间2SELECT MIN(restore_date) AS first_restore_started,3 MAX(restore_date) AS last_restore_started,4 COUNT(*) AS restore_steps5FROM msdb.dbo.restorehistory6WHERE destination_database_name = N'SalesDB_Rehearsal';这只能粗略显示恢复步骤开始的时间跨度,不能当成实际 RTO。要算故障演练耗时,还需记录文件到位、每一步完成、CHECKDB 结束和业务验收的时间。
1SELECT backup_set_id, type, backup_start_date,2 backup_finish_date, recovery_model,3 begins_log_chain4FROM msdb.dbo.backupset5WHERE database_name = N'SalesDB'6ORDER BY backup_start_date, backup_set_id;库现在是 FULL,不能倒推昨天的日志备份就一定连续。若中间切到 SIMPLE 再切回来,要重新找日志链起点;begins_log_chain 只对日志备份判断有意义。
1-- 仅 SQL Server 2022 及以后;时间按恢复目标替换2SELECT TOP (10) backup_set_id, backup_start_date,3 backup_finish_date, first_lsn, last_lsn,4 last_valid_restore_time5FROM msdb.dbo.backupset6WHERE database_name = N'SalesDB'7 AND type = 'L'8 AND last_valid_restore_time > '2026-09-11T10:15:00'9ORDER BY backup_finish_date;last_valid_restore_time 是备份内带时间戳的最后一条日志记录时间,可帮助筛选含目标点的日志。它不是只靠一个时间列就能确定整条链连续;还要按 LSN、恢复分叉和实际介质排好前序日志。
1SELECT TOP (30) type, backup_finish_date,2 CAST(backup_size / 1048576.0 AS decimal(18,1)) AS backup_mb,3 CAST(compressed_backup_size / 1048576.0 AS decimal(18,1))4 AS stored_mb5FROM msdb.dbo.backupset6WHERE database_name = N'SalesDB'7ORDER BY backup_finish_date DESC;估算恢复时要复制多少文件、测试盘需要多少空间。压缩后的备份体积不是恢复后数据文件大小;compressed_backup_size 为 NULL 时也不要当作零字节文件。
1-- backup_set_id 按选定完整备份替换2SELECT bs.backup_set_id, mf.mirror,3 mf.family_sequence_number,4 mf.physical_device_name5FROM msdb.dbo.backupset AS bs6JOIN msdb.dbo.backupmediafamily AS mf7 ON mf.media_set_id = bs.media_set_id8WHERE bs.backup_set_id = 123459ORDER BY mf.mirror, mf.family_sequence_number;同一镜像内每个家族都要在恢复时提供;镜像介质还会给相同家族另留一组文件。不能把多行简单计作“有好几份完整备份”,应按 mirror 和 family_sequence_number 分组。
1SELECT TOP (20) bs.backup_set_id, bs.type,2 bs.backup_finish_date, bms.software_name,3 bms.media_uuid4FROM msdb.dbo.backupset AS bs5JOIN msdb.dbo.backupmediaset AS bms6 ON bms.media_set_id = bs.media_set_id7WHERE bs.database_name = N'SalesDB'8ORDER BY bs.backup_finish_date DESC;当 msdb 历史来自第三方备份或 VSS 快照时,恢复介质的获取方式可能和本地 .bak 不同。先找到实际系统与介质,再决定用原生 RESTORE 还是供应商恢复流程。
1SELECT TOP (5) backup_set_id, backup_finish_date,2 is_copy_only, checkpoint_lsn,3 database_backup_lsn4FROM msdb.dbo.backupset5WHERE database_name = N'SalesDB'6 AND type = 'D'7ORDER BY backup_finish_date DESC;COPY_ONLY 全备可以单独恢复,却不会改变差异备份基。计划使用差异备份时,应找与它相配的普通全备,不能只看文件修改时间或“最新全备”四个字。
1SELECT backup_set_id, backup_start_date,2 backup_finish_date, server_name,3 machine_name, first_lsn, last_lsn4FROM msdb.dbo.backupset5WHERE database_name = N'SalesDB'6 AND type = 'L'7ORDER BY backup_start_date;故障切换后,日志备份可能换节点执行。不同 server_name 不是断链证明;仍要按 LSN 与恢复分叉核对,必要时到各节点取介质,不能漏掉切换窗口的备份。
1-- 仅隔离测试实例2SELECT restore_date, restore_type,3 recovery, destination_database_name4FROM msdb.dbo.restorehistory5WHERE destination_database_name = N'SalesDB_Rehearsal'6ORDER BY restore_date, restore_history_id;recovery=1 表示该恢复步骤指定了 RECOVERY。若后面原计划还有日志却提前打开,不能直接追加恢复;应从完整备份重新跑,并把误用位置记入演练记录。
1-- 作业名称按现场规范筛选;先确认真实作业名2SELECT job_id, name, enabled, description3FROM msdb.dbo.sysjobs4WHERE name LIKE N'%SalesDB%'5 OR name LIKE N'%Backup%'6ORDER BY name;这里只是发现入口。作业名未必包含库名,有些环境用一个作业备份所有库;找不到时再检查维护计划、第三方备份软件和作业步骤。
1-- 作业名按第 44 条确认2SELECT j.name AS job_name, s.step_id, s.step_name,3 s.subsystem, s.database_name, s.command4FROM msdb.dbo.sysjobs AS j5JOIN msdb.dbo.sysjobsteps AS s6 ON s.job_id = j.job_id7WHERE j.name = N'SalesDB Backup'8ORDER BY s.step_id;同名“备份作业”可能只做清理或调用外部程序。看 subsystem 与 command,才能知道它备份的是全库、差异还是日志,以及执行目标和输出目录。
1SELECT TOP (10) j.name, h.run_date, h.run_time,2 h.run_duration, h.run_status, h.message3FROM msdb.dbo.sysjobs AS j4JOIN msdb.dbo.sysjobhistory AS h5 ON h.job_id = j.job_id6WHERE j.name = N'SalesDB Backup'7 AND h.step_id = 08ORDER BY h.instance_id DESC;step_id=0 是作业总体结果,run_status=1 为成功、0 为失败。run_date 和 run_time 分别是整数日期与时间,run_duration 采用 HHmmss 记法;这条命令不把它误当成秒数。
1SELECT TOP (20) j.name, h.step_id, h.step_name,2 h.run_status, h.sql_message_id,3 h.retries_attempted, h.message4FROM msdb.dbo.sysjobs AS j5JOIN msdb.dbo.sysjobhistory AS h6 ON h.job_id = j.job_id7WHERE j.name = N'SalesDB Backup'8 AND h.step_id > 09ORDER BY h.instance_id DESC;整体失败时先看具体步骤的错误号与信息。反过来,整体成功也可能掩盖某个允许继续执行的步骤失败;要同时看作业步骤设计和 backupset 中实际新增的备份记录。
1SELECT name, enabled, date_created,2 date_modified3FROM msdb.dbo.sysjobs4WHERE name = N'SalesDB Backup';enabled=0 会使按计划执行的作业停下来。变更后核对修改时间,再找变更记录;禁用作业不一定等于没有任何手工或第三方备份。
1SELECT j.name, js.next_run_date,2 js.next_run_time3FROM msdb.dbo.sysjobs AS j4JOIN msdb.dbo.sysjobschedules AS js5 ON js.job_id = j.job_id6WHERE j.name = N'SalesDB Backup';sysjobschedules 的下一次时间可能有约 20 分钟的刷新延迟。发现下一次计划异常时,继续核对关联的 sysschedules 和 SQL Server Agent 服务;不要仅凭这里一行判断任务不会运行。
1SELECT j.name, a.start_execution_date,2 a.stop_execution_date, a.last_executed_step_id3FROM msdb.dbo.sysjobs AS j4JOIN msdb.dbo.sysjobactivity AS a5 ON a.job_id = j.job_id6WHERE j.name = N'SalesDB Backup'7 AND a.session_id =8 (SELECT MAX(session_id) FROM msdb.dbo.syssessions);当前 Agent 会话中开始时间有值、停止时间为空,表示作业仍在运行。若备份历史尚未出现新行,先看作业有没有结束;不要在它运行途中把“没有备份记录”判作失败。
1SELECT TOP (15) type, backup_start_date,2 backup_finish_date, server_name,3 has_backup_checksums4FROM msdb.dbo.backupset5WHERE database_name = N'SalesDB'6ORDER BY backup_finish_date DESC;把第 46 条的作业完成时间与这里的 backup_finish_date 对照。作业成功但没有预计类型的备份时,应回看步骤和筛选逻辑;历史行存在仍需读取实际文件。
1-- 阈值按现场日志备份周期调整,这里示例为 30 分钟2SELECT MAX(backup_finish_date) AS last_log_backup,3 DATEDIFF(minute, MAX(backup_finish_date), SYSDATETIME())4 AS minutes_since_last_log_backup5FROM msdb.dbo.backupset6WHERE database_name = N'SalesDB'7 AND type = 'L';日志备份周期是 RPO 的关键输入。last_log_backup 为 NULL 时可能是历史被清理、备份跑在别的节点,或日志备份确实未做;先查执行节点,再判断告警。
1SELECT j.name, s.step_id, s.step_name,2 s.on_success_action, s.on_fail_action,3 s.on_fail_step_id, s.retry_attempts4FROM msdb.dbo.sysjobs AS j5JOIN msdb.dbo.sysjobsteps AS s6 ON s.job_id = j.job_id7WHERE j.name = N'SalesDB Backup'8ORDER BY s.step_id;on_fail_action=1 代表失败后仍以作业成功退出,3 或 4 会继续其他步骤。若备份步骤失败却走了这些路径,作业整体可能亮绿灯;真正的备份结果还得回到 backupset 和介质检查。
1-- 作业名按现场替换2SELECT j.name, sc.name AS schedule_name,3 sc.enabled, sc.freq_type, sc.freq_interval,4 sc.freq_subday_type, sc.freq_subday_interval5FROM msdb.dbo.sysjobs AS j6JOIN msdb.dbo.sysjobschedules AS js7 ON js.job_id = j.job_id8JOIN msdb.dbo.sysschedules AS sc9 ON sc.schedule_id = js.schedule_id10WHERE j.name = N'SalesDB Log Backup';freq_subday_type 决定间隔单位,freq_subday_interval 才是数量;看到数字 15 不能直接说“每 15 分钟”。调度频率也不等于备份实际完成频率,第 55 条要用结果反查。
1WITH log_backups AS (2 SELECT backup_finish_date,3 LAG(backup_finish_date) OVER4 (ORDER BY backup_finish_date) AS previous_finish5 FROM msdb.dbo.backupset6 WHERE database_name = N'SalesDB'7 AND type = 'L'8 AND backup_finish_date >= DATEADD(day, -7, SYSDATETIME())9)10SELECT backup_finish_date, previous_finish,11 DATEDIFF(minute, previous_finish, backup_finish_date)12 AS minutes_between_backups13FROM log_backups14WHERE previous_finish IS NOT NULL15ORDER BY backup_finish_date DESC;看实际完成时间,比仅看调度设置更接近真实 RPO 风险。历史已清理或备份在另一节点执行时,这里的间隔可能被拉大;跨节点场景要先合并两边记录。
1SELECT TOP (30) backup_start_date,2 backup_finish_date,3 DATEDIFF(second, backup_start_date, backup_finish_date)4 AS elapsed_seconds,5 compressed_backup_size6FROM msdb.dbo.backupset7WHERE database_name = N'SalesDB'8 AND type = 'L'9ORDER BY backup_finish_date DESC;如果一份日志备份耗时已经接近甚至超过调度周期,就要看重叠、介质带宽与备份大小。时间突然变长也可能是异常批量操作产生大量日志;不能只按作业结果“成功”忽略它。
1SELECT name, notify_level_email,2 notify_email_operator_id,3 notify_level_eventlog4FROM msdb.dbo.sysjobs5WHERE name IN (N'SalesDB Backup', N'SalesDB Log Backup');作业失败但无人收到通知,值班时就容易只看到“昨天备份成功”。这些字段只说明作业配置;还要在现场核对 Database Mail、Operator 和实际通知通道。
1SELECT j.name, js.server_id,2 js.last_run_date, js.last_run_time,3 js.last_run_outcome4FROM msdb.dbo.sysjobs AS j5JOIN msdb.dbo.sysjobservers AS js6 ON js.job_id = j.job_id7WHERE j.name IN (N'SalesDB Backup', N'SalesDB Log Backup');sysjobservers 登记作业与目标服务器的关联及上次结果,不证明今天的备份确实在该节点产生。多服务器作业要将 server_id 与现场实例配置对上,再到目标实例查作业历史和实际介质。
1# 在存放备份文件的 Windows 主机执行,路径来自现场介质盘点2Test-Path -LiteralPath 'D:\SqlBackup\SalesDB_full_20260910.bak' -PathType Leaf返回 True 只表示当前账号能看到这个文件,不表示 SQL Server 服务账号能读,也不表示内容有效。msdb 的介质路径若已过期,应以现场实际文件位置为准。
1Get-Item -LiteralPath 'D:\SqlBackup\SalesDB_full_20260910.bak' |2 Select-Object FullName, Length, LastWriteTime零字节或大小异常值得马上排查;文件大小正常也不能代替 RESTORE VERIFYONLY。修改时间可能因为复制、归档或还原被改变,不能直接当作备份完成时间。
1Get-FileHash -LiteralPath 'D:\SqlBackup\SalesDB_full_20260910.bak' -Algorithm SHA256源文件和测试实例收到的副本分别算 SHA256,值相同才能确认传输后的字节一致。哈希能证明复制一致,不能证明源备份本身可以恢复;后面还要做介质验证和实际恢复。
1-- 在能够访问该文件的 SQL Server 实例执行2RESTORE LABELONLY3FROM DISK = N'D:\SqlBackup\SalesDB_full_20260910.bak';媒体标签用于识别媒体集,不是具体备份集清单。一个媒体集可能有多个备份集;选要恢复的 FILE 位置仍需看 RESTORE HEADERONLY。
1-- backup_set_id 换成已选定的完整备份2SELECT bs.backup_set_id, bms.media_family_count,3 bms.mirror_count, bms.is_encrypted,4 bms.is_compressed5FROM msdb.dbo.backupset AS bs6JOIN msdb.dbo.backupmediaset AS bms7 ON bms.media_set_id = bs.media_set_id8WHERE bs.backup_set_id = 12345;条带化备份要把同一镜像的所有家族找齐。media_family_count 是家族数量,mirror_count 是镜像数量;两者不要混成“有几份可独立恢复的备份”。
1-- 这里只做预览,不删除文件或历史2SELECT database_name, type, COUNT(*) AS history_rows,3 MIN(backup_finish_date) AS first_finish,4 MAX(backup_finish_date) AS last_finish5FROM msdb.dbo.backupset6WHERE backup_finish_date < '2026-08-01'7GROUP BY database_name, type;sp_delete_backuphistory 按日期清理全实例的历史,并非只影响 SalesDB,所以先看每个库会失去多少行。历史保留期限应覆盖备份保留期与恢复取证期;清掉后旧介质未必删除,但从 msdb 排恢复链会更难。
1-- 清理日期和目标测试库按现场替换2SELECT rh.destination_database_name, rh.restore_date,3 bs.backup_set_id, bs.type, bs.backup_finish_date4FROM msdb.dbo.restorehistory AS rh5JOIN msdb.dbo.backupset AS bs6 ON bs.backup_set_id = rh.backup_set_id7WHERE rh.destination_database_name = N'SalesDB_Rehearsal'8 AND bs.backup_finish_date < '2026-08-01'9ORDER BY rh.restore_date;清理历史以前,把仍需审计的演练链另存档。这里查的是 msdb 里的关联记录;介质本身的保留与删除属于另一套策略,不能靠这条 SQL 证明。
1-- 修改 msdb 元数据;先完成第 64、65 条并留存审计记录2EXEC msdb.dbo.sp_delete_backuphistory3 @oldest_date = '2026-08-01';此过程删除全实例指定日期以前的备份与恢复历史,不负责删除磁盘文件。例子中的日期不能照抄,必须由全实例保留政策和演练需求确定;执行后再核对历史行数与外部归档。
1# 在隔离测试主机执行;文件名应与源介质清单对应2Get-ChildItem -LiteralPath 'D:\SqlBackup' -File |3 Where-Object Name -Like 'SalesDB_*' |4 Select-Object Name, Length, LastWriteTime把这份清单与源端选定的全备、差异、日志文件逐个对照;条带化备份一个文件也不能少。只复制一份全备,不能证明目标时间可达。
1# 在隔离测试主机执行;与源端逐份比对,不只检查全备2Get-ChildItem -LiteralPath 'D:\SqlBackup' -File |3 Where-Object Name -Like 'SalesDB_*' |4 ForEach-Object { Get-FileHash -LiteralPath $_.FullName -Algorithm SHA256 }源端和目标端逐份哈希一致,说明这些文件复制后字节未变。条带化备份一个文件也不能漏;哈希检查不能代替目标实例的介质验证。
1Get-PSDrive -Name D |2 Select-Object Name, Root, Free, Used目标盘至少要容纳尚未复制的备份文件和演练期间新增的文件。Get-PSDrive 显示的是当前 PowerShell 会话看到的驱动器;SQL Server 服务账号访问网络盘还需单独验证。
1-- 在隔离测试实例执行;实际目标路径是第 15 条的 E: 盘2SELECT DISTINCT v.volume_mount_point,3 CAST(v.total_bytes / 1073741824.0 AS decimal(18,1)) AS total_gb,4 CAST(v.available_bytes / 1073741824.0 AS decimal(18,1)) AS free_gb5FROM sys.master_files AS mf6CROSS APPLY sys.dm_os_volume_stats(mf.database_id, mf.file_id) AS v7WHERE v.volume_mount_point LIKE N'E:%';这能查看隔离实例已使用的目标卷,不能凭一行就算出恢复所需空间。若 E: 盘尚无数据库文件或文件落在挂载目录,过滤条件要按现场改;还要从 FILELISTONLY 汇总源文件大小。
1-- 在源实例查询;backup_set_id 换成第 5 条选中的完整备份集2SELECT logical_name, file_type, is_present,3 CAST(file_size / 1073741824.0 AS decimal(18,1)) AS file_gb4FROM msdb.dbo.backupfile5WHERE backup_set_id = 123456ORDER BY logical_name;file_size 是备份时各文件的字节长度,可先做空间预算;is_present 要一起看。这只是源实例的历史记录,选定文件的实际清单仍以第 9 条 RESTORE FILELISTONLY 读取的介质为准。恢复后文件还可能增长。
1-- 在隔离测试实例执行;文件名换成目标时点对应的最后一份日志2RESTORE VERIFYONLY3FROM DISK = N'D:\SqlBackup\SalesDB_log_20260911_1100.trn'4WITH FILE = 1, CHECKSUM;这份日志是演练链的终点,目标实例能不能读到,要在目标实例验证。全备、差异以及其余日志也要逐份检查;这里只列出最后一份,不代表整条链已验证。若备份经过加密,目标实例还需具备对应证书或密钥。
1-- 另开连接,在隔离测试实例执行2SELECT session_id, command, start_time,3 percent_complete, wait_type,4 total_elapsed_time5FROM sys.dm_exec_requests6WHERE command LIKE N'RESTORE%';percent_complete 可观察正在运行的恢复阶段,等待类型有助于判断介质或磁盘是否拖慢进度。它不是总演练 RTO,也不保证剩余时间可以按百分比线性推算;完整耗时还包括取文件和业务验收。
1-- 隔离测试实例;与源库演练前记录比较2SELECT name, state_desc, compatibility_level,3 collation_name, is_read_only,4 user_access_desc5FROM sys.databases6WHERE name = N'SalesDB_Rehearsal';ONLINE 只是能打开库。跨版本恢复后还要核对兼容级别、排序规则和读写状态;如果要给应用做验收,测试环境的设置差异须写在演练记录里。
1-- 源实例执行;库名按现场替换2SELECT DB_NAME(database_id) AS database_name,3 encryption_state, encryptor_type,4 encryptor_thumbprint5FROM sys.dm_database_encryption_keys6WHERE database_id = DB_ID(N'SalesDB');encryption_state=3 表示数据库已加密。备份加密和 TDE 是两项不同的依赖;TDE 库移到另一实例前,保护数据库加密密钥的证书或非对称密钥必须能在目标实例使用。
1-- 目标实例 master;与第 75 条的 thumbprint 对照2SELECT name, thumbprint,3 pvt_key_encryption_type_desc,4 pvt_key_last_backup_date5FROM master.sys.certificates6WHERE thumbprint = 0x0123456789ABCDEF0123456789ABCDEF01234567;指纹是示例值,要换成源库第 75 条实际值。查到证书名称还不够,私钥也要可用;没有目标证书和私钥时,TDE 备份不能靠复制 .bak 文件解决。
1-- 在隔离测试实例执行;只查依赖实例认证的 SQL 用户2SELECT dp.name AS database_user, dp.sid3FROM SalesDB_Rehearsal.sys.database_principals AS dp4LEFT JOIN sys.server_principals AS sp5 ON sp.sid = dp.sid6WHERE dp.type = 'S'7 AND dp.authentication_type_desc = 'INSTANCE'8 AND sp.principal_id IS NULL;跨实例恢复常见“库在,应用登录不了”,要看用户 SID 与目标实例登录是否对应。此查询受当前账号的元数据可见性影响;返回行需要结合实际登录测试确认,不能直接批量改 SID。
1-- 示例业务表,按现场实际表名替换2SELECT COUNT_BIG(*) AS order_rows3FROM SalesDB_Rehearsal.dbo.Orders;这是验收用的一个数字,不是恢复正确的证明。应与目标时间点的业务快照或应用流水比较;大表执行精确计数会扫描数据,测试实例也要预留时间。
1-- 更新字段和时间窗口按事故记录替换2SELECT MIN(updated_at) AS first_update,3 MAX(updated_at) AS last_update,4 COUNT_BIG(*) AS matching_rows5FROM SalesDB_Rehearsal.dbo.Orders6WHERE updated_at >= '2026-09-11T10:00:00'7 AND updated_at < '2026-09-11T10:15:00';时间窗要与第 17、18 条的 STOPAT 和业务时间源对应。恢复前后的业务记录分布、关键交易是否存在,比只看 restorehistory 的成功行更能说明恢复点是否正确。
1-- 当前数据库必须切到恢复出的测试库2USE SalesDB_Rehearsal;3DBCC CHECKCONSTRAINTS (N'dbo.Orders') WITH ALL_CONSTRAINTS;CHECKDB 查物理与逻辑一致性,约束检查另看外键和 CHECK 约束中的数据违规。ALL_CONSTRAINTS 会连禁用约束也查;若输出违规,先与源库既有状态对照,不能直接说是恢复造成的。
1-- 只有业务依赖这些功能时才需纳入验收2SELECT name, is_broker_enabled,3 is_cdc_enabled4FROM sys.databases5WHERE name = N'SalesDB_Rehearsal';异地恢复后的库可能能读表,却不能继续消息处理或变更捕获。先查应用是否用到这些功能,再确认测试环境能否安全模拟;不要为让数值和生产一样就随手启用后台业务链路。
1-- 用测试应用相同连接串连上之后执行,不能先直连实例2SELECT @@SERVERNAME AS connected_instance,3 DB_NAME() AS connected_database,4 ORIGINAL_LOGIN() AS original_login;备份能恢复、库能打开之后,最后还得让应用真正连上。若返回的库不是 SalesDB_Rehearsal 或登录账号不对,检查连接串、默认库和登录映射;不要拿管理员直连成功替代业务连接验收。
1-- 源实例执行;备份前确认日志状态2SELECT name, recovery_model_desc,3 log_reuse_wait_desc4FROM sys.databases5WHERE name = N'SalesDB';LOG_BACKUP 说明日志备份尚未满足截断条件;ACTIVE_TRANSACTION、AVAILABILITY_REPLICA 等原因则要走各自的排查路径。不能为了缩小日志文件就改成 SIMPLE,那会影响原有日志备份链。
1-- 源实例执行;专门给隔离演练使用的独立文件2BACKUP DATABASE SalesDB3TO DISK = N'D:\SqlBackup\SalesDB_copyonly_20260916.bak'4WITH COPY_ONLY, CHECKSUM, COMPRESSION, STATS = 5;COPY_ONLY 全备可单独恢复,但不会成为后续差异备份的基。演练需要一份新的源库快照、又不想扰动常规差异链时适用;备份完成仍要验证文件和执行隔离恢复。
1-- 源实例执行;新文件名先确认未被使用2BACKUP DATABASE SalesDB3TO DISK = N'D:\SqlBackup\SalesDB_full_20260916.bak'4WITH CHECKSUM, COMPRESSION, STATS = 5;这份普通全备会成为后续差异备份的候选基。不要把它与第 84 条 copy-only 文件混为一谈;真实生产作业还需按存储、条带和备份保留策略配置。
1-- 第 85 条普通全备已成功,期间没有其他普通全备重置差异基2BACKUP DATABASE SalesDB3TO DISK = N'D:\SqlBackup\SalesDB_diff_20260916.bak'4WITH DIFFERENTIAL, CHECKSUM, COMPRESSION, STATS = 5;差异备份始终以它真正的差异基为准,不以文件名里的日期为准。若第 85 条后又有其他普通全备完成,就不能假设此差异还指向示例那一份;用 backupset 和实际备份头回读。
1-- 源库为 FULL,日志链已开始;本地独立文件2BACKUP LOG SalesDB3TO DISK = N'D:\SqlBackup\SalesDB_log_20260916_1200.trn'4WITH CHECKSUM, COMPRESSION, STATS = 5;日志备份链独立于数据备份,不能从“做了新全备”推断中间旧日志可以丢。仍要按第 6、7 条从选定数据备份接到恢复目标,确认每份日志都在场。
1-- 在实际执行备份的源实例 msdb2SELECT backup_set_id, type, is_copy_only,3 backup_start_date, backup_finish_date,4 has_backup_checksums, is_damaged5FROM msdb.dbo.backupset6WHERE database_name = N'SalesDB'7 AND backup_start_date >= '2026-09-16T00:00:00'8ORDER BY backup_start_date, backup_set_id;预期看到 copy-only 全备、普通全备、差异和日志四类记录。少一类就回看作业或命令错误;记录齐全还要核对实际路径、介质头与恢复结果。
1SELECT bs.backup_set_id, bs.type,2 bs.checkpoint_lsn, bs.differential_base_lsn,3 bs.differential_base_guid, bs.is_copy_only4FROM msdb.dbo.backupset AS bs5WHERE bs.database_name = N'SalesDB'6 AND bs.type IN ('D', 'I')7 AND bs.backup_start_date >= '2026-09-16T00:00:00'8ORDER BY bs.backup_start_date;先排除 copy-only 全备,再按普通全备与差异基的 LSN/GUID 关系核对。若差异基字段为空,可能是多文件差异基,回到第 25 条按文件检查;不能凭同一天生成就认定可接。
1SELECT TOP (20) backup_set_id, backup_start_date,2 backup_finish_date, first_lsn, last_lsn,3 first_recovery_fork_guid,4 last_recovery_fork_guid5FROM msdb.dbo.backupset6WHERE database_name = N'SalesDB'7 AND type = 'L'8ORDER BY backup_start_date DESC, backup_set_id DESC;新文件的 first_lsn、last_lsn 要和之前的日志、恢复分叉放在一条连续的路径里看。切换过恢复分叉或备份分散在其他节点时,这台实例的 20 行历史未必完整,还得合并实际介质头。
1-- 隔离测试实例;逻辑文件名来自实际 FILELISTONLY2RESTORE VERIFYONLY3FROM DISK = N'D:\SqlBackup\SalesDB_full_20260916.bak'4WITH FILE = 1, CHECKSUM,5 MOVE N'SalesDB' TO N'E:\Rehearsal\SalesDB_Rehearsal.mdf',6 MOVE N'SalesDB_log' TO N'E:\Rehearsal\SalesDB_Rehearsal.ldf';第 10 条只查介质能不能读;这里还用计划中的 MOVE 检查目标路径和文件布局。验证仍不真正恢复,且源库如果有更多数据文件,所有逻辑文件都应按实际清单安排目标路径。
1-- 源实例;两个新文件都必须位于可靠存储2BACKUP DATABASE SalesDB3TO DISK = N'D:\SqlBackup\SalesDB_stripe_1.bak',4 DISK = N'D:\SqlBackup\SalesDB_stripe_2.bak'5WITH COPY_ONLY, CHECKSUM, COMPRESSION, STATS = 5;这是一次备份,分在两个媒体家族中,并非两份独立全备。COPY_ONLY 使它不改变差异基;存档和恢复时必须把两个文件作为同一套介质保存。
1-- 隔离测试实例;两份文件来自第 92 条同一次备份2RESTORE HEADERONLY3FROM DISK = N'D:\SqlBackup\SalesDB_stripe_1.bak',4 DISK = N'D:\SqlBackup\SalesDB_stripe_2.bak';先确认库名、类型、媒体集及备份集的 Position。如果只有第一个文件而第二个文件找不到,不能把现有文件单独当成可恢复备份。
1-- FILE 位置按第 93 条输出核对2RESTORE VERIFYONLY3FROM DISK = N'D:\SqlBackup\SalesDB_stripe_1.bak',4 DISK = N'D:\SqlBackup\SalesDB_stripe_2.bak'5WITH FILE = 1, CHECKSUM;验证整套条带介质,能比单独验证一个文件更早发现缺失或损坏。通过后还须做实际隔离恢复;VERIFYONLY 不检查恢复出的数据库结构。
1-- 仅隔离实例;SalesDB_StripedTest 与目标文件均确认不存在2RESTORE DATABASE SalesDB_StripedTest3FROM DISK = N'D:\SqlBackup\SalesDB_stripe_1.bak',4 DISK = N'D:\SqlBackup\SalesDB_stripe_2.bak'5WITH FILE = 1, NORECOVERY,6 MOVE N'SalesDB' TO N'E:\Rehearsal\SalesDB_StripedTest.mdf',7 MOVE N'SalesDB_log' TO N'E:\Rehearsal\SalesDB_StripedTest.ldf',8 STATS = 5;这个例子把两条媒体家族一起恢复,NORECOVERY 留下继续追日志的入口。逻辑文件名和所有 MOVE 路径按实际 FILELISTONLY 替换;不要在已有业务库上用 REPLACE 图省事。
1-- 仅在第 95 条恢复确实中断、介质与目标文件未改动时2RESTORE DATABASE SalesDB_StripedTest3FROM DISK = N'D:\SqlBackup\SalesDB_stripe_1.bak',4 DISK = N'D:\SqlBackup\SalesDB_stripe_2.bak'5WITH FILE = 1, NORECOVERY,6 MOVE N'SalesDB' TO N'E:\Rehearsal\SalesDB_StripedTest.mdf',7 MOVE N'SalesDB_log' TO N'E:\Rehearsal\SalesDB_StripedTest.ldf',8 RESTART, STATS = 5;RESTART 要重复原恢复语句及参数,不能改成另一个备份集或路径。它会重新启动恢复,并非从已复制百分比处续传;原本操作若没有中断,就不该执行这条。
1-- 独立隔离测试库;选 9 月 10 日全备,目标是 9 月 11 日2RESTORE DATABASE SalesDB_PointTest3FROM DISK = N'D:\SqlBackup\SalesDB_full_20260910.bak'4WITH FILE = 1, NORECOVERY,5 STOPAT = '2026-09-11T10:15:00',6 MOVE N'SalesDB' TO N'E:\Rehearsal\SalesDB_PointTest.mdf',7 MOVE N'SalesDB_log' TO N'E:\Rehearsal\SalesDB_PointTest.ldf';这里选的全备早于目标时间,后面还要接匹配的差异和日志。若误把 9 月 16 日全备换进来,STOPAT 能提醒数据备份选得过晚;它本身不会把库恢复到目标点,后续每份日志仍要使用相同 STOPAT。
1-- 源实例 msdb;仅已有标记时才考虑标记恢复2SELECT database_name, mark_name, mark_time,3 lsn, description4FROM msdb.dbo.logmarkhistory5WHERE database_name = N'SalesDB'6ORDER BY mark_time DESC;事务标记必须在业务写入时事先建立并进入日志备份,恢复时临时起一个名字没有用。相关数据库要恢复到共同的标记时,每个库的日志中都应有同一标记;还要核对各自所需的全备和日志链。
1-- 另一条独立的隔离恢复分支;前序全备、日志已用 NORECOVERY2RESTORE LOG SalesDB_MarkTest3FROM DISK = N'D:\SqlBackup\SalesDB_mark_20260916.trn'4WITH FILE = 1, STOPATMARK = N'BillingCutover',5 RECOVERY;这条只适用于确有 BillingCutover 标记、且目标日志文件包含它的情况。标记与目标库名、介质都要现场替换;前面的日志要按顺序恢复,不能只拿含标记的最后一个 .trn 直接打开库。
1-- 独立隔离分支;目标库已有匹配全备,尚未最终 RECOVERY2RESTORE LOG SalesDB_StandbyTest3FROM DISK = N'D:\SqlBackup\SalesDB_log_20260916_1200.trn'4WITH FILE = 1,5 STANDBY = N'E:\Rehearsal\SalesDB_StandbyTest.undo',6 STATS = 5;STANDBY 用撤销文件保存恢复所需信息,让库暂时可读,还能继续应用后续日志。撤销文件必须保留且有足够空间;这是只读检查状态,不是最终业务上线状态。后续日志与最终 RECOVERY 仍须按选定恢复链完成。
SQL Server 恢复依赖完整的 Full、Differential 和 Log 备份链。只看到备份作业成功还不够,还要确认 LSN 关系、备份文件可读、恢复顺序正确,并定期在隔离实例完成演练。
建议把数据库恢复目标、备份位置、加密证书和验证脚本写进同一份恢复手册。事故发生时,先选对备份链,再开始 RESTORE。
更多数据库运维内容可在 ORA100 · DBA100 查看:
微信里搜索小程序 「三笠的百令册」,也可以继续查看这个系列。
ORA100 DBA100