使用标记的事务(完整恢复模式)
本主题仅适用于使用完整或大容量日志恢复模式的 SQL Server 数据库。
在对两个或更多数据库(“相关数据库”)进行相关更新时,可以使用事务标记将它们恢复到逻辑上一致的点。但是,此恢复将丢失在作为恢复点的标记之后提交的所有事务。只有您在测试相关数据库或不介意丢失近期提交的事务时,标记事务才适用。
在每个相关数据库中定期标记相关事务将在数据库中建立一系列公用恢复点。事务标记将记录在事务日志中并包括在日志备份中。发生灾难时,可以将各数据库还原到相同的事务标记,从而将它们恢复到一致的点。
注意 |
---|
可以对不同的数据库独立创建各日志备份,而无需同时创建。 |
对于以下情况,若要恢复相关数据库,则必须先对各相关数据库中的事务进行标记:
一个或多个事务日志已破坏。需要将数据库集还原到上次进行日志备份时的一致状态。
需要将整个数据库集还原到某个更早时间点的相互一致状态。
重要提示 |
---|
您仅可以将相关数据库恢复为标记事务,而不是特定时间点。 |
有关如何创建标记事务的信息,请参阅本主题后面的“创建标记事务”。
使用标记事务的典型方案
使用标记事务的典型方案包括以下步骤:
创建每个相关数据库的完整数据库备份或差异数据库备份。
在所有数据库中标记事务块。
备份所有数据库的事务日志。
用 WITH NORECOVERY 还原数据库备份。
用 WITH STOPATMARK 还原日志。
使用标记事务的注意事项
在将已命名的标记插入事务日志之前,请注意以下事项:
由于事务标记会消耗日志空间,应只对在数据库恢复策略中起重要作用的事务使用标记。
标记的事务提交之后,在 msdb 的 logmarkhistory 表中插入一行。
如果标记事务跨同一数据库服务器或不同服务器上的多个数据库,则必须在所有受影响数据库的日志中记录标记。
创建标记事务
若要创建标记事务,请使用 BEGIN TRANSACTION 语句和 WITH MARK [description] 子句。可选内容 description 是关于标记的文字说明。必须为事务指定标记名称。标记名称可以重复使用。事务日志记录标记名称、描述、数据库、用户、日期时间信息以及日志序列号 (LSN)。日期时间信息与标记名称一起使用,以便对标记进行唯一标识。
在数据库集中创建标记事务:
用 BEGIN TRAN 语句命名事务,并使用 WITH MARK 子句。
您可以在现有事务中嵌套 BEGIN TRAN new_mark_name WITH MARK 语句。即使事务拥有事务名称,new_mark_name 的值也是事务的标记名称。
注意 如果执行第二个嵌套 BEGIN TRAN...WITH MARK,那么将跳过该语句,但会导致出现警告消息。
对数据库集中的所有数据库进行更新。
将仅在执行 BEGIN TRAN...WITH MARK 语句的服务器实例上的事务日志中插入特定事务的标记。事务标记放置在由该服务器实例上的标记事务更新过的各数据库的事务日志中。如果数据库位于不同的服务器实例上,则必须在各服务器实例上创建相同的标记。
示例
以下示例将事务日志还原到名为 ListPriceUpdate 的标记事务中的标记处。
USE AdventureWorks2008R2;
GO
BEGIN TRANSACTION ListPriceUpdate
WITH MARK 'UPDATE Product list prices';
GO
UPDATE Production.Product
SET ListPrice = ListPrice * 1.10
WHERE ProductNumber LIKE 'BK-%';
GO
COMMIT TRANSACTION ListPriceUpdate;
GO
-- Time passes. Regular database
-- and log backups are taken.
-- An error occurs in the database.
USE master
GO
RESTORE DATABASE AdventureWorks2008R2
FROM AdventureWorksBackups
WITH FILE = 3, NORECOVERY;
GO
RESTORE LOG AdventureWorks2008R2
FROM AdventureWorksBackups
WITH FILE = 4,
RECOVERY,
STOPATMARK = 'ListPriceUpdate';
强制将标记分布到其他服务器
事务分散到其他服务器时,事务标记名称不会自动分布到其他服务器。若要强制标记分散到其他服务器,必须编写包含 BEGIN TRAN 名称 WITH MARK 语句的存储过程。然后,必须在发起服务器上的事务作用域下,在远程服务器上执行该存储过程。
例如,假设多个 SQL Server 实例上存在分区数据库。每个实例上都有一个名为 coyote 的数据库。首先,在每个数据库中创建一个存储过程,例如,sp_SetMark。
CREATE PROCEDURE sp_SetMark
@name nvarchar (128)
AS
BEGIN TRANSACTION @name WITH MARK
UPDATE coyote.dbo.Marks SET one = 1
COMMIT TRANSACTION;
GO
然后,创建包含在每个数据库中放置标记的事务的存储过程 sp_MarkAll。sp_MarkAll 可以从任意实例中运行。
CREATE PROCEDURE sp_MarkAll
@name nvarchar (128)
AS
BEGIN TRANSACTION
EXEC instance0.coyote.dbo.sp_SetMark @name
EXEC instance1.coyote.dbo.sp_SetMark @name
EXEC instance2.coyote.dbo.sp_SetMark @name
COMMIT TRANSACTION;
GO
两阶段提交
提交分布式事务分为两个阶段:准备和提交。提交标记事务时,标记事务中每个数据库的提交日志记录都放在日志的某一点处,以使任何日志中不存在有疑问的事务。此点保证不存在这样的事务:在一个日志中显示为已提交,在另一个日志中显示为未提交。
为此,可在提交标记事务的过程中执行下列操作:
标记事务的准备阶段停止所有新的准备和提交。
仅允许继续提交准备好的事务。
标记事务将等待耗尽所有准备好的事务(带超时设置)。
准备并提交标记事务。
取消停止新的准备和提交。
由跨多个数据库的标记事务产生的停止会降低服务器的事务处理性能。
建议不要运行并发标记事务。虽然很少见,但分布式标记事务有可能与其他同时提交的分布式标记事务发生死锁。出现这种情况时,正在标记的事务将被选作死锁牺牲品并回滚。出现此错误时,应用程序会重试标记事务。多个标记事务尝试同时提交时,发生死锁的可能性较大。