外观
SQLServer 页级恢复
页级恢复概述
什么是页级恢复
页级恢复(Page-Level Recovery)是指只恢复数据库中损坏的页,而不是整个数据库或文件组的恢复方式。SQLServer数据库的基本存储单位是页(Page),每个页大小为8KB。当数据库中的个别页损坏时,可以使用页级恢复只恢复损坏的页,从而减少恢复时间和资源消耗。
页级恢复的适用场景
- 个别页损坏:当数据库中只有少数页损坏时,页级恢复比全库恢复更高效
- 减少恢复时间:页级恢复只恢复损坏的页,减少了恢复时间和系统停机时间
- 降低资源消耗:页级恢复只需要恢复少量数据,降低了CPU、内存和I/O资源消耗
- 在线恢复:在SQL Server 2005及以上版本中,页级恢复可以在数据库在线状态下执行
- 大型数据库恢复:对于大型数据库,页级恢复可以避免长时间的全库恢复
页级恢复前准备
识别损坏的页
通过错误日志识别:
sql-- 查看错误日志中的页损坏信息 EXEC xp_readerrorlog 0, 1, '823', NULL, NULL, NULL, 'DESC'; EXEC xp_readerrorlog 0, 1, '824', NULL, NULL, NULL, 'DESC'; EXEC xp_readerrorlog 0, 1, '825', NULL, NULL, NULL, 'DESC';通过DBCC CHECKDB识别:
sql-- 执行DBCC CHECKDB检查页损坏 DBCC CHECKDB ([AdventureWorks2022]) WITH NO_INFOMSGS, ALL_ERRORMSGS; -- 查找包含 "error 823"、"error 824" 或 "error 825" 的错误信息,记录损坏的页ID通过sys.dm_db_missing_index_details视图识别:
sql-- 查看缺失的索引和损坏的页 SELECT * FROM sys.dm_db_missing_index_details;
恢复环境检查
备份文件检查:
sql-- 验证完整备份文件完整性 RESTORE VERIFYONLY FROM DISK = N'D:\Backup\AdventureWorks2022_Full_20251227.bak' WITH CHECKSUM; -- 验证事务日志备份文件完整性 RESTORE VERIFYONLY FROM DISK = N'D:\Backup\AdventureWorks2022_Log_20251227_1215.trn' WITH CHECKSUM;数据库状态检查:
sql-- 检查数据库状态 SELECT name AS DatabaseName, state_desc, recovery_model_desc FROM sys.databases WHERE name = 'AdventureWorks2022';恢复模式检查:
- 页级恢复要求数据库使用完整恢复模式或大容量日志恢复模式
- 如果数据库使用简单恢复模式,需要先切换到完整恢复模式并创建完整备份
sql-- 切换到完整恢复模式 ALTER DATABASE [AdventureWorks2022] SET RECOVERY FULL; -- 创建完整备份 BACKUP DATABASE [AdventureWorks2022] TO DISK = N'D:\Backup\AdventureWorks2022_Full_Before_Page_Recovery.bak' WITH NOFORMAT, NOINIT, NAME = N'AdventureWorks2022-Full Database Backup Before Page Recovery', SKIP, NOREWIND, NOUNLOAD, STATS = 10;
页级恢复实施步骤
使用SSMS进行页级恢复
- 打开SQL Server Management Studio,连接到SQL Server实例
- 展开"数据库"节点
- 右键单击要恢复的数据库,选择"任务" > "还原" > "页..."
- 在"还原页"对话框中:
- 在"还原页"区域,输入损坏的页ID,格式为"文件ID:页ID"(例如:1:256)
- 在"源"区域,选择"源设备",点击"..."按钮,选择完整备份和事务日志备份文件
- 在"恢复选项"区域,配置恢复选项:
- 恢复状态:选择"RESTORE WITH RECOVERY"(如果只恢复损坏的页)
- 覆盖现有数据库:不需要勾选,因为只恢复损坏的页
- 点击"确定"执行恢复
使用T-SQL进行页级恢复
基本页级恢复
sql
-- 1. 识别损坏的页
-- 假设DBCC CHECKDB检测到页1:256损坏
-- 2. 恢复损坏的页
RESTORE DATABASE [AdventureWorks2022]
PAGE = '1:256'
FROM DISK = N'D:\Backup\AdventureWorks2022_Full_20251227.bak'
WITH NORECOVERY,
NOUNLOAD, STATS = 10;
-- 3. 恢复所有后续的事务日志备份
RESTORE LOG [AdventureWorks2022]
FROM DISK = N'D:\Backup\AdventureWorks2022_Log_20251227_1215.trn'
WITH NORECOVERY,
NOUNLOAD, STATS = 10;
RESTORE LOG [AdventureWorks2022]
FROM DISK = N'D:\Backup\AdventureWorks2022_Log_20251227_1300.trn'
WITH RECOVERY,
NOUNLOAD, STATS = 10;恢复多个损坏的页
sql
-- 恢复多个损坏的页
RESTORE DATABASE [AdventureWorks2022]
PAGE = '1:256', '1:512', '2:1024'
FROM DISK = N'D:\Backup\AdventureWorks2022_Full_20251227.bak'
WITH NORECOVERY,
NOUNLOAD, STATS = 10;
-- 恢复所有后续的事务日志备份
RESTORE LOG [AdventureWorks2022]
FROM DISK = N'D:\Backup\AdventureWorks2022_Log_20251227_1215.trn'
WITH NORECOVERY,
NOUNLOAD, STATS = 10;
RESTORE LOG [AdventureWorks2022]
FROM DISK = N'D:\Backup\AdventureWorks2022_Log_20251227_1300.trn'
WITH RECOVERY,
NOUNLOAD, STATS = 10;使用差异备份加速页级恢复
sql
-- 1. 恢复损坏的页
RESTORE DATABASE [AdventureWorks2022]
PAGE = '1:256'
FROM DISK = N'D:\Backup\AdventureWorks2022_Full_20251227.bak'
WITH NORECOVERY,
NOUNLOAD, STATS = 10;
-- 2. 恢复差异备份(可选,加速恢复)
RESTORE DATABASE [AdventureWorks2022]
FROM DISK = N'D:\Backup\AdventureWorks2022_Diff_20251227_1200.bak'
WITH NORECOVERY,
NOUNLOAD, STATS = 10;
-- 3. 恢复所有后续的事务日志备份
RESTORE LOG [AdventureWorks2022]
FROM DISK = N'D:\Backup\AdventureWorks2022_Log_20251227_1215.trn'
WITH NORECOVERY,
NOUNLOAD, STATS = 10;
RESTORE LOG [AdventureWorks2022]
FROM DISK = N'D:\Backup\AdventureWorks2022_Log_20251227_1300.trn'
WITH RECOVERY,
NOUNLOAD, STATS = 10;页级恢复场景
场景1:个别页损坏恢复
恢复步骤
识别损坏的页:
sql-- 执行DBCC CHECKDB检测损坏的页 DBCC CHECKDB ([AdventureWorks2022]) WITH NO_INFOMSGS, ALL_ERRORMSGS; -- 假设检测到页1:256和1:512损坏恢复损坏的页:
sql-- 恢复损坏的页 RESTORE DATABASE [AdventureWorks2022] PAGE = '1:256', '1:512' FROM DISK = N'D:\Backup\AdventureWorks2022_Full_20251227.bak' WITH NORECOVERY, NOUNLOAD, STATS = 10;恢复事务日志备份:
sql-- 恢复所有后续的事务日志备份 RESTORE LOG [AdventureWorks2022] FROM DISK = N'D:\Backup\AdventureWorks2022_Log_20251227_1215.trn' WITH NORECOVERY, NOUNLOAD, STATS = 10; RESTORE LOG [AdventureWorks2022] FROM DISK = N'D:\Backup\AdventureWorks2022_Log_20251227_1300.trn' WITH RECOVERY, NOUNLOAD, STATS = 10;验证恢复结果:
sql-- 验证页恢复结果 DBCC CHECKDB ([AdventureWorks2022]) WITH NO_INFOMSGS, ALL_ERRORMSGS; -- 确认损坏的页已恢复
场景2:大型数据库页损坏恢复
恢复步骤
识别损坏的页:
sql-- 对于大型数据库,使用更高效的方式检测损坏的页 DBCC CHECKDB ([AdventureWorks2022]) WITH PHYSICAL_ONLY, NO_INFOMSGS; -- 假设检测到页1:1024损坏恢复损坏的页:
sql-- 恢复损坏的页 RESTORE DATABASE [AdventureWorks2022] PAGE = '1:1024' FROM DISK = N'D:\Backup\AdventureWorks2022_Full_20251227.bak' WITH NORECOVERY, NOUNLOAD, STATS = 10;恢复差异备份:
sql-- 恢复差异备份,加速恢复过程 RESTORE DATABASE [AdventureWorks2022] FROM DISK = N'D:\Backup\AdventureWorks2022_Diff_20251227_1200.bak' WITH NORECOVERY, NOUNLOAD, STATS = 10;恢复事务日志备份:
sql-- 恢复所有后续的事务日志备份 DECLARE @LogBackupPath NVARCHAR(255) = N'D:\Backup\AdventureWorks2022_Log_'; DECLARE @LogBackupFiles TABLE (FileName NVARCHAR(255)); -- 假设我们有多个事务日志备份文件 INSERT INTO @LogBackupFiles VALUES (@LogBackupPath + '20251227_1215.trn'), (@LogBackupPath + '20251227_1230.trn'), (@LogBackupPath + '20251227_1245.trn'), (@LogBackupPath + '20251227_1300.trn'); DECLARE @CurrentFile NVARCHAR(255); DECLARE @IsLastFile BIT = 0; DECLARE FileCursor CURSOR FOR SELECT FileName, CASE WHEN ROW_NUMBER() OVER (ORDER BY FileName DESC) = 1 THEN 1 ELSE 0 END AS IsLastFile FROM @LogBackupFiles; OPEN FileCursor; FETCH NEXT FROM FileCursor INTO @CurrentFile, @IsLastFile; WHILE @@FETCH_STATUS = 0 BEGIN IF @IsLastFile = 1 BEGIN -- 最后一个事务日志备份,使用RECOVERY RESTORE LOG [AdventureWorks2022] FROM DISK = @CurrentFile WITH RECOVERY, NOUNLOAD, STATS = 10; END ELSE BEGIN -- 中间的事务日志备份,使用NORECOVERY RESTORE LOG [AdventureWorks2022] FROM DISK = @CurrentFile WITH NORECOVERY, NOUNLOAD, STATS = 10; END FETCH NEXT FROM FileCursor INTO @CurrentFile, @IsLastFile; END; CLOSE FileCursor; DEALLOCATE FileCursor;验证恢复结果:
sql-- 验证页恢复结果 DBCC CHECKDB ([AdventureWorks2022]) WITH NO_INFOMSGS, ALL_ERRORMSGS; -- 确认损坏的页已恢复
页级恢复最佳实践
恢复前准备
及时识别损坏的页:
- 定期执行DBCC CHECKDB检查数据库完整性
- 监控SQL Server错误日志,及时发现页损坏错误
- 配置数据库邮件告警,当发生页损坏时及时通知DBA
确保备份文件完整:
- 定期验证备份文件的完整性
- 确保有足够的备份文件覆盖损坏的页
- 对于大型数据库,考虑使用文件组备份,便于页级恢复
准备恢复环境:
- 确保有足够的磁盘空间
- 确保系统有足够的CPU和内存资源
- 关闭不必要的应用程序,减少系统负载
恢复过程中的最佳实践
正确使用NORECOVERY和RECOVERY选项:
- 页级恢复和中间的事务日志恢复使用NORECOVERY
- 最后一个事务日志恢复使用RECOVERY,完成恢复
- 避免在中间步骤使用RECOVERY,导致恢复链中断
使用差异备份加速恢复:
- 对于大型数据库,使用差异备份可以减少需要恢复的事务日志备份数量
- 差异备份只包含自上次完整备份以来更改的数据,恢复速度更快
监控恢复进度:
- 使用STATS选项监控恢复进度
- 定期检查恢复状态
- 记录恢复开始时间和完成时间
恢复后的最佳实践
验证恢复结果:
- 执行DBCC CHECKDB验证数据完整性
- 验证损坏的页已恢复
- 运行应用程序测试,确保数据库正常工作
分析页损坏原因:
- 检查存储设备的健康状态
- 检查磁盘控制器和电缆
- 检查系统事件日志,查找硬件故障信息
- 考虑更换有问题的存储设备
更新备份策略:
- 根据页损坏的原因调整备份策略
- 增加备份频率,减少数据丢失风险
- 考虑使用更可靠的存储设备
页级恢复常见问题处理
问题1:页级恢复失败,提示找不到指定的页
错误信息:
Msg 3157, Level 16, State 1, Line 1
The backup set does not contain the requested database page. The page number is (1:256).
Msg 3013, Level 16, State 1, Line 1
RESTORE DATABASE is terminating abnormally.解决方案:
检查备份文件是否包含损坏的页:
sql-- 查看备份文件中的页范围 RESTORE FILELISTONLY FROM DISK = N'D:\Backup\AdventureWorks2022_Full_20251227.bak';使用包含损坏页的备份文件:
- 查找包含损坏页的完整备份或文件组备份
- 使用正确的备份文件进行页级恢复
如果没有包含损坏页的备份文件,考虑使用其他恢复方式:
- 全库恢复
- 文件组恢复
- 从其他备份恢复数据
问题2:页级恢复失败,提示数据库处于只读状态
错误信息:
Msg 3127, Level 16, State 1, Line 1
The database is read-only. It cannot be recovered. Use RESTORE WITH RECOVERY to bring the database online.解决方案:
检查数据库状态:
sqlSELECT name AS DatabaseName, state_desc, is_read_only FROM sys.databases WHERE name = 'AdventureWorks2022';如果数据库处于只读状态,修改为读写状态:
sql-- 将数据库修改为读写状态 ALTER DATABASE [AdventureWorks2022] SET READ_WRITE;重新执行页级恢复:
sql-- 重新执行页级恢复 RESTORE DATABASE [AdventureWorks2022] PAGE = '1:256' FROM DISK = N'D:\Backup\AdventureWorks2022_Full_20251227.bak' WITH NORECOVERY, NOUNLOAD, STATS = 10; -- 恢复事务日志备份 RESTORE LOG [AdventureWorks2022] FROM DISK = N'D:\Backup\AdventureWorks2022_Log_20251227_1215.trn' WITH RECOVERY, NOUNLOAD, STATS = 10;
问题3:页级恢复后数据库无法访问
错误信息:
Msg 927, Level 14, State 2, Line 1
Database 'AdventureWorks2022' cannot be opened. It is in the middle of a restore.解决方案:
检查数据库状态:
sqlSELECT name AS DatabaseName, state_desc, is_in_standby FROM sys.databases WHERE name = 'AdventureWorks2022';如果数据库处于RESTORING状态,完成恢复:
sql-- 使用RECOVERY选项完成恢复 RESTORE DATABASE [AdventureWorks2022] WITH RECOVERY;
页级恢复性能优化
优化备份策略
使用文件组备份:
- 对于大型数据库,使用文件组备份可以减少页级恢复需要的备份文件大小
- 文件组备份只包含特定文件组的数据,恢复速度更快
sql-- 创建文件组备份 BACKUP DATABASE [AdventureWorks2022] FILEGROUP = N'PRIMARY' TO DISK = N'D:\Backup\AdventureWorks2022_Primary_FG.bak' WITH NOFORMAT, NOINIT, NAME = N'AdventureWorks2022-Primary Filegroup Backup', SKIP, NOREWIND, NOUNLOAD, STATS = 10;使用备份压缩:
- 启用备份压缩可以减少备份文件大小,提高恢复速度
- 对于页级恢复,压缩的备份文件需要解压,可能会增加CPU消耗,但减少了I/O消耗
sql-- 创建压缩的完整备份 BACKUP DATABASE [AdventureWorks2022] TO DISK = N'D:\Backup\AdventureWorks2022_Full_Compressed.bak' WITH COMPRESSION, NOFORMAT, NOINIT, NAME = N'AdventureWorks2022-Compressed Full Database Backup', SKIP, NOREWIND, NOUNLOAD, STATS = 10;
优化恢复过程
使用更快的存储设备:
- 将备份文件存储在高性能存储上(如SSD)
- 将数据库文件恢复到高性能存储上
增加恢复并行度:
- 使用多个备份设备并行恢复
- 对于大型数据库,考虑使用文件组备份和并行恢复
使用加速数据库恢复(ADR):
- SQL Server 2019及以上版本支持加速数据库恢复
- ADR可以显著减少恢复时间,尤其是对于大型数据库
- 启用ADR:sql
ALTER DATABASE [AdventureWorks2022] SET ACCELERATED_DATABASE_RECOVERY = ON;
常见问题(FAQ)
Q1: 页级恢复和全库恢复有什么区别?
A: 页级恢复只恢复数据库中损坏的页,而全库恢复恢复整个数据库。页级恢复的恢复时间更短,资源消耗更少,适用于只有少数页损坏的情况。全库恢复适用于数据库完全损坏或大部分页损坏的情况。
Q2: 页级恢复需要什么条件?
A: 页级恢复需要满足以下条件:
- 数据库必须使用完整恢复模式或大容量日志恢复模式
- 必须有包含损坏页的完整备份或文件组备份
- 必须有从完整备份到当前时间的所有事务日志备份
- 损坏的页必须可以通过DBCC CHECKDB或其他方式识别
Q3: 如何确定损坏的页ID?
A: 可以通过以下方式确定损坏的页ID:
- 执行DBCC CHECKDB命令,查看错误信息中的页ID
- 查看SQL Server错误日志,查找包含页损坏信息的错误
- 使用sys.dm_db_missing_index_details视图查看缺失的页
Q4: 页级恢复可以恢复到只读数据库吗?
A: 页级恢复不能直接恢复到只读数据库,需要先将数据库修改为读写状态,然后执行页级恢复,最后将数据库修改回只读状态。
Q5: 如何预防页损坏?
A: 可以通过以下方式预防页损坏:
- 使用可靠的存储设备
- 定期检查存储设备的健康状态
- 定期执行DBCC CHECKDB检查数据库完整性
- 配置数据库邮件告警,及时发现页损坏
- 实施3-2-1备份原则,确保有足够的备份
Q6: 页级恢复失败后如何处理?
A: 页级恢复失败后,可以采取以下措施:
- 查看错误信息,确定失败原因
- 根据错误信息采取相应措施,如更换备份文件、修改数据库状态等
- 如果页级恢复无法成功,可以考虑使用其他恢复方式,如全库恢复或文件组恢复
- 必要时联系Microsoft支持获取帮助
版本差异
| SQLServer版本 | 页级恢复特性差异 |
|---|---|
| 2008 R2 | 基本页级恢复功能 |
| 2012 | 改进页级恢复性能 |
| 2014 | 支持使用差异备份加速页级恢复 |
| 2016 | 支持压缩备份的页级恢复 |
| 2017 | 支持直接从Azure Blob Storage恢复页 |
| 2019 | 引入加速数据库恢复(ADR),提高页级恢复速度 |
| 2022 | 改进ADR性能,支持从S3兼容存储恢复页 |
| 2024 | 增强页级恢复可靠性,支持更多恢复场景 |
总结
页级恢复是SQLServer恢复操作中一种高效的恢复方式,它只恢复数据库中损坏的页,而不是整个数据库或文件组。页级恢复适用于个别页损坏的情况,可以减少恢复时间和资源消耗。在实施页级恢复时,需要遵循最佳实践,包括恢复前准备、恢复过程中的监控和恢复后的验证。通过合理的恢复策略和优化措施,可以提高页级恢复的速度和可靠性,确保在发生页损坏时,能够快速、可靠地恢复损坏的页,满足业务的RTO和RPO要求。
