讲解SQL Server 2005灾难恢复步骤

来源:百度文库 编辑:神马文学网 时间:2024/04/28 03:24:03

讲解SQL Server 2005灾难恢复步骤

作者: bianceng,  出处:IT专家网论坛, 责任编辑: 陈子琪, 2010-04-27 07:00
  SQL Server 2005在实现高可用性和灾难恢复方面给我们提供了很多种选择。比技术本身更重要的是拿出适当的程序,这是因为我们要管理不同的灾难恢复方案。

  问题

  SQL Server 2005在实现高可用性和灾难恢复方面给我们提供了很多种选择。比技术本身更重要的是拿出适当的程序,这是因为我们要管理不同的灾难恢复方案。我们应该如何拿出程序来管理多种多样的灾难恢复场景呢?

  专家解答

  这一系列文章将探讨不同的灾难恢复场景和涉及你的恢复计划的程序。考虑到你的SQL Server 2005数据库中的灾难恢复选项,你应该包含尽可能多的技术,因为如果灾难发生时你将会有很多种选择来解决这个问题。尽管拥有这些技术被证实是很重要的,但是它也是一个伴随着使整个程序更加有效的过程。在这篇文章里,让我们来看看一个简单的场景,那就是在发生数据库备份的5个小时后,一个用户不小心删除了一张表。从一个数据库备份中恢复意味着失去5小时的数据。而对于大多数的公司来说,比起失去数据,他们更愿意选择失去时间。并且,如果这是一个非常大的数据库,那么将花很长一段时间来恢复和使它联机。在将数据损失控制到最小的前提下,我们将考虑这种情况来建立一个程序方法,以此来尽快地恢复数据库。我们将使用Northwind数据库来显示这个过程。记住在进行以下步骤之前先把Northwind中的数据库恢复模式改成FULL。

  1) 确保你有一个好的备份

  针对上面的场景,我们假设你有一个每天早上6:00运行的备份,并且没有一个定期创建的数据库快照。你的数据库被配置成使用一个单一的MDF和LDF文件,这个文件对灾难恢复不太适用。让我们在Northwind数据库中创建一个全数据库备份,它将是数据库恢复的起始点。
以下是代码:
  USEmaster
  GO
  BACKUPDATABASENorthwind
  TODISK=N'D:DBBackupNorthwindBackup.bak'
  WITHNAME=N'FullDatabaseBackup',DESCRIPTION='Startingpointforrecovery',
  INIT,STATS=10
  GO

  从Northwind数据库模式中,你很难删除Products, Orders和Customers表,这是因为有其他表比如Order Details表的外码约束限制。但我敢打赌你可以轻易地删除Order Details表。接下来,我们来模拟在大约上午11:00时删除这张表的灾难情景。


  DROPTABLE[OrderDetails]

  2) 包含发生的问题

  由于这个数据库只有一个单一的MDF和LDF文件,我们不可能做很多操作。我们所能做的就是通过把它设置成有限制的用户模式来使数据库脱机。


  USEmaster
  GO
  ALTERDATABASENorthwind
  SETRESTRICTED_USER
  WITHROLLBACKIMMEDIATE
  GO

  这将很有效地使数据库脱机,停止所有活动的连接。这是分秒必争的时刻,我们需要采取行动。当进行到下面的步骤时,请牢记你的RPO和RTO。

  3) 备份事务日志

  一个好的数据库管理员应该知道当灾难来临时,首先要做的是备份事务日志 – 假设你的数据库被设置成全数据库备份。这是为了确保在上次备份之后你还保存所有活动的事务。在我们的场景中,由于上次的备份 – 全备份,在这个例子中 – 发生于早上6:00。


  BACKUPLOGNorthwind
  TODISK=N'D:DBBackupNorthwindBackupLog.trn'
  WITHNAME=N'TransactionLogBackup'
  ,DESCRIPTION='Gettingeverythingtocurrentpointintime.',
  STATS=10
  GO

  4) 还原数据库到一个已知的良好时间点

  现在,任何用户意外地删除一张表或者在数据库上造成一些破坏后都不会立即告诉你。有时候,你可能需要自己深入挖掘它,但是这需要花很多时间。由于我们想让数据库尽快地联机,所以我们假设一个众所周知的良好时间点,并且让发掘在稍后的时间里进行。在下面的脚本中,我将选择在我的STOPAT参数中将早上10:42设置成已知的良好时间点,一次达到展示的目的。


  RESTOREDATABASENorthwind
  FROMDISK=N'D:DBBackupNorthwindBackup.bak'
  WITHNORECOVERY,RESTRICTED_USER
  GO
  RESTORELOGNorthwind
  FROMDISK=N'D:DBBackupNorthwindBackupLog.trn'
  WITHRESTRICTED_USER,
  STOPAT='2008-09-2310:42:44.00',RECOVERY
  --usea"knowngood"pointintime
  GO

  虽然我们已经把数据库恢复到一个已知的良好时间点,但是我们并没有知道我们实际上到底失去了多少数据。我们需要找出在执行DROP TABLE语句之前执行最后的一个INSERT语句的准确时间,这样我们才能恢复尽可能多的数据。但是由于我们需要尽可能快地使数据库联机,所以我们不想在数据库上直接做这些。这正证明了接下来的步骤是有价值的。

  你可以通过执行一个针对它的查询来验证删除的表是否已经恢复。


  SELECT*
  FROMNorthwind.dbo.[OrderDetails]
  GO

  5) 创建一个恢复点的快照

  我们将创建一个还原数据库的数据库快照,以此来做进一步的处理。这个数据库快照将是把数据恢复到执行DROP TABLE语句之前的准确时间的参考。


  USEmaster
  GO
  CREATEDATABASENorthwind_RestorePointSnapshot
  ON
  (NAME=N'Northwind',
  FILENAME=N'D:DBBackupNorthwindData_RestorePontSnapshot.snap')
  ASSNAPSHOTOF[Northwind]
  GO

  依靠这个表模式,我们可以选择让它保持原状,或者像我们对Order Details 表所做的那样,或者多做一些操作。如果这张表有一个现有IDENTITY栏,我们需要在IDENTITY栏的最大值和需要恢复的行的假设数量之间创建一个间隙。这当然取决于在服务器上发生的事务数目。要确定IDENTITY栏的最大值,你可以执行如下显示的DBCC CHECKIDENT命令:


  DBCCCHECKIDENT('tableName')
  --Displaysthenumberofrowsyouhavefortherestoredtable
  GO

  这将返回IDENTITY列的最大值。让我们假设这张表每天的事务数目大约是4000条记录,我们可以在最大值和下一个值之间创建一个间隙。如果IDENTITY列的最大值是25000,我们需要将4000加到这个值中,并且再次执行带有RESEED参数的DBCC CHECKIDENT命令(我们简单假设你在一天之内可以恢复失去的数据,那就是值为400):


  DBCCCHECKIDENT('tableName',RESEED,29000)
  --CreatesagapoffortheIDENTITYcolumntostartthenextvalueat29000
  GO

  6) 使数据库联机

  一旦你已经成功做到上面的步骤,那么就更改数据库选项使它联机并且允许用户连接到数据库和运行他们的应用程序。


  USEmaster
  GO
  ALTERDATABASENorthwind
  SETMULTI_USER
  GO

  现在数据库已经重新联机并且删除的表已经恢复。虽然现在每个人都很开心,但是作为数据库管理员,我们的工作不能就此止步。记住我们仍然需要恢复从已知良好时间点到执行DROP TABLE命令之前的缺失数据。这是我们能够恢复尽可能多的数据的唯一途径。尽管我们可以用一些第三方工具来读取事务日志和通过重放这些事务来恢复数据,但是绝大多数人没有使用这些工具的奢侈权利。所以我们下一个最好的方法是使用带有STOPAT选项的RESTORE从句。这可能有些繁琐,有时也很让人有压力,因为一个错误会让你陷入重复地执行整个过程的状态。我们所需要做的就是找出我们做备份的时间,直到事务日志备份结束。在我们的场景中,上次的备份在早上6:00,而你的已知良好时间点在早上10:42.44。因此,你可以从早上10:42.44开始执行带有STOPAT选项的RESTORE命令,并且把STOPAT时间值改成可能一秒。如果你不是很确定上次备份发生的时间,你可以一直查询MSDB数据库。


  SELECT*
  FROMmsdb.dbo.backupsetASa
  INNERJOINmsdb.dbo.backupmediafamilyASb
  ONa.media_set_id=b.media_set_id
  WHEREdatabase_name='Northwind'
  ORDERBYbackup_finish_date

  请注意backup_finish_date和type栏,因为这些栏将会让你知道什么时候你需要考虑RESTORE命令中的STOPAT值。

  7) 用不同的名称恢复另一个损坏数据库的副本以便于研究

  用不同的名称来还愿损坏数据库的另一个副本可以让我们致力于数据的还原,而不用担心可用性,这是因为我们已经成功建立一个生产数据库。你只要确定你为恢复数据库选择了不同的名称和数据库文件位置,或者你可以结束损坏已经建立的数据库。我们将重复步骤四所做的,但是在这里使用不同的名称和数据文件位置。


  RESTOREDATABASENorthwind_recover
  FROMDISK=N'D:DBBackupNorthwindBackup.bak'
  WITHMOVEN'Northwind'TON'D:DBBackupNorthwindData_recover.mdf',
  MOVEN'Northwind_Log'TON'D:DBBackupNorthwindLog_recover.ldf',
  STANDBY=N'D:DBBackupNorthwind_UNDO.bak',
  STATS=10
  GO
  RESTORELOGNorthwind_recover
  FROMDISK=N'D:DBBackupNorthwindBackupLog.trn'
  WITHSTANDBY=N'D:DBBackupNorthwind_UNDO.bak',
  STATS=10,STOPAT='2008-09-2310:42:44.00'
  GO

  记录你的STOPAT参数值,因为这将是你在这个过程中使用到的最关键的参数。由于我们只是重复了步骤四的过程,我们肯定知道这个时候还没有执行DROP TABLE命令。

  8) 通过提高STOPAT参数值来还原事务日志

  我们执行RESTORE LOG命令,通过一分钟 – 从10:42:44.00到 10:43:44.00来增加STOPAT参数值。


  RESTORELOGNorthwind_recover
  FROMDISK=N'D:DBBackupNorthwindBackupLog.trn'
  WITHSTANDBY=N'D:DBBackupNorthwind_UNDO.bak',
  STATS=10,STOPAT='2008-09-2310:43:44.00'
  GO

  这是它重复的部分。不要因为它确实繁琐而感到烦恼。你可以通过1分钟,5分钟,10分钟和记录这些时间来增加这个值。记住在执行RESTORE LOG命令之后在删除的对象上运行一个测试查询。我建议在这个活动上创建一张表,这张表看起来像这样:


  TIMEOBJECTEXISTED?
  10:43:44.00YES
  10:48:44.00YES
  10:58:44.00YES
  11:03:44.00NO

  有了这个信息,你就可以很肯定地知道这张表在10:58:44.00与 11:03:44.00之间被删除。你可以重复步骤8,并且因为你已经在一个更小的时间间隔内操作,你可以在一分钟甚至一秒内增加STOPAT参数值,如果你觉得自己过度关注STOPAT参数的时间值,那么请返回带有你在步骤八中记录的列表信息的步骤七,使恢复过程更快进行。你只需要记住在上次的RESTORE LOG语句中使用WITH RECOVERY选项,例如: 


  RESTORELOGNorthwind_recover
  FROMDISK=N'D:DBBackupNorthwindBackupLog.trn'
  WITHSTATS=10,STOPAT='2008-09-2311:01:44.00',RECOVERY
  GO

  一旦你设法把数据库恢复到执行DROP TABLE命令之前,你就可以在生产数据库上所做的还原和恢复所做的作一个比较。你可以用很多种方式实现这一点。由于我们已经有一个之前创建好的数据库快照,所以我们也用它来做灾难恢复。先前关于SQL Server 2005 tablediff命令行工具的方法可以让你可以了解如何使用这个工具但是只强调你的源数据库将是你恢复的那个数据库,并且目的数据库将是你的数据库快照。这是为什么你的数据库快照被证实是很重要的,尤其是如果你处理不只是一个平常的对象。如果你不习惯用命令行工具,那么可以参考一个由来自SQLTeam.com的成员创建的GUI版本。你可能也想确定这些并且把它放在你的DBA工具栏中。

  你也可以执行一个INSERT/SELECT操作,在基于恢复数据库的一个查询的生产数据库上插入纪录。因为我们的Order Details表没有IDENTITY栏,所以可以通过在一张临时表中插入纪录和使用ROW_NUMBER()函数来自己创建这一栏。


  --Thisinsertsrecordsinatemporarytableandassignsadummyidentityvalueforreference
  SELECTROW_NUMBER()OVER(ORDERBYOrderID)ASROWID,*
  INTONorthwind_recover.dbo.OrderDetailsRecover
  FROM[OrderDetails]
  --Thisinsertsrecoveredrecordsfromtherecovereddatabaseintotheproductiondatabasebasedon
  --thedummyidentityvaluewehaveassignedforreference
  INSERTINTONorthwind.dbo.[OrderDetails](OrderID,ProductId,UnitPrice,Quantity,Discount)
  SELECTOrderID,ProductId,UnitPrice,Quantity,Discount
  FROMNorthwind_recover.dbo.OrderDetailsRecover
  WHEREROWID>
  (
  SELECTCOUNT(*)
  FROMNorthwind_RestorePointSnapShot.dbo.[OrderDetails]
  )

  请注意,我们使用数据库快照来识别我们所设法还原的和我们所恢复的区别。