支持提示:在 DPM 2007、2010 和 2012 上,计划磁带备份在错误日期运行
各位 DPM 管理员,大家好,我是 DPM 支持团队的 Wilson Souza。大家知道,Data Protection Manager 提供了很多保护 Exchange、SQL、SharePoint、Hyper-V、系统状态、裸机、文件、共享等服务器工作负荷的方式。它可通过以下方式提供保护:
磁盘到磁盘 (D-D) – 受保护数据从受保护服务器上的磁盘转移到位于 DPM 服务器上的卷。
磁盘到磁带 (D-T)– 受保护数据从受保护服务器上的磁盘转移到由 DPM 服务器管理的磁带设备。
磁盘到磁盘到磁带 (D-D-T) – 结合使用以上两个选项,受保护数据从受保护服务器上的磁盘转移到位于 DPM 服务器上的卷,然后我们再将数据从 DPM 卷复制到磁带设备。
使用磁带时,对于何时将数据备份转移到磁带,我们有一系列可用选项。可以是每天、每周、每月、每季度和每年,或者您认为适合 SLA 的任意组合。
DPM 将计划备份控制委托给 SQL 代理,到了运行磁带备份时,SQL 代理会触发 DPM 引擎作业以启动备份。
设想一下,有一天上午您来到办公室,查看创建备份的情况,发现原本应在两个月之后运行的备份在昨晚已经完成,但您却没有收到任何警告,提醒 DPM 将在这个出人意料的日期运行备份。本篇博文旨在解释这个问题并提供解决方法。
注意:此问题不会影响每天、每周或每月的磁带备份计划,而主要影响几个月的磁带备份,例如每季度、每半年、每年的备份等。此问题会在 DPM 2007、2010 和 2012 上出现,但本文的解决方法不适用于 DPM 2007。
解释问题
假定今天是 2011 年 10 月 7 日,我们创建了一个新的保护组并设置了长期保护(每周、每季度和每年)。在新保护组向导的最后,DPM 将创建必要的计划作业,并将它们发送到 SQL 代理。
如下图所示,从 SQL 代理看来,这是每季度备份。
注意:每季度备份仅应在 1 月/4 月/7 月/10 月运行
SQL 代理显示,此作业应在作业创建之后两天后运行
2011 年 10 月 9 日,磁带备份作业按预期运行。根据季度计划定义,我们现在预期此备份将在 2012 年 1 月 9 日运行。
对保护组进行的几乎所有操作(手动:添加/删除受保护成员、修改磁盘分配或在没有进行任何更改的情况下完成修改保护组向导,或者自动:SQL 和 SharePoint 自动保护、磁盘自动增长)都将导致所有计划作业从该组删除和重新创建。这正是计划作业可能在错误日期运行的潜在原因。
在删除/创建新计划之后,DPM 将使用原始 XML 来生成新的计划作业。ScheduleXml 将使用原始的开始日期,该日期现在可能已经过去。以下是 ScheduleXML 的片段。
<?xmlversion="1.0"encoding="utf-16"?>
<Schedulexmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"xmlns:xsd="https://www.w3.org/2001/XMLSchema"ScheduleID="9b0c036b-5c2d-49b8-a374-3842ba6cfb96"JobDefID="c5241cb4-8dc2-4574-b758-2e7b7db0ca70"xmlns="https://schemas.microsoft.com/2003/dls/Scheduler.xsd">
<Recurrence>
<MonthlyStartDt="2011-10-09"EndDt="9999-12-31"Interval="3"MonthDayList="9" />
<TimeStartTm="20:00:00"EndTm="20:00:00" />
</Recurrence>
</Schedule>
现在,我们快进到 11 月 6 日,保护组已被修改,添加了新数据源。原始计划作业将被删除,并创建新的作业。
请注意,以下的开始日期没有更改。
突出显示的是由修改保护组操作创建的新计划(每周、每季度和每年的计划)。季度的新计划作业是倒数第二行的作业(下一次运行 = 11/09/2011 8:00:00 PM)
SQL 代理认为此计划设置在 2011 年 10 月 9 日第一次运行。现在已经过去了将近一个月,“上一次运行”列显示此作业从未运行。为了解决这个问题,SQL 代理将这个新作业的下一次运行时间设置为第一个可用日期。由于现在是 6 日,距离 9 日有三天时间,SQL 代理将计划在 9 日运行此作业。
因此,它不会按预期在 1 月运行每季度备份,而是现在立即运行备份,提早了 2 个月。此外,此作业不会在 DPM 用户界面中显示为已计划(我们将在另一篇博客中对这个问题进行解释)。您只会在此作业正在运行、完成或失败时看到对此作业的引用。
解决方法
为了解决这个问题,请将以下脚本复制到 SQL Server Management Studio 并执行该脚本。这个新存储的程序将检查开始日期是否已过,如果是,它将计算下一次运行时间,并进行相应设置。从 SQL 代理的角度来看,错误计划只会在计划开始日期设置为过去日期时发生,一旦超过原始计划日期,这种情况始终都会发生。
脚本:
=====
USE [DPMDB]
GO
/****** Object: StoredProcedure [dbo].[prc_IM_UserSchedule_Update] Script Date: 11/10/2011 19:11:01 ******
******* Edited by........: Wilson Souza
******* Version..........: 2.2
******* Date Created.....: 11/10/11
******* Date Last Change.: 04/04/11
******* THIS SCRIPT IS FOR DPM 2010/2012 RTM
******* Change Log for V 2.2
******* Addressed issue if selected DAY instead any day of the week.
******* Change log for V 2.0
******* Now using XML variable to retrieve data instead of searching string on schedule variable
******* Now addresses issues for Weekly schedules. Not only Months
******* Now addresses issues when user select First, Second, Third, Four or Last day of the month.
*/
SETANSI_NULLSON
GO
SETQUOTED_IDENTIFIERON
GO
--
-- Update one row in UserSchedule table by ScheduleID.
-- If this ScheduleID doesn't exist,
-- add a new row with this ScheduleID.
--
ALTERPROCEDURE [dbo].[prc_IM_UserSchedule_Update]
(
@ScheduleID GUID,
@ProtectedGroupID GUID,
@JobType tinyint,
--------------- Change Start ---------------
-- @Schedule ntext,
@Schedule nvarchar(max),
--------------- Change end ---------------
@Immediacy bit,
@TimeOffset int,
@MaxDuration bigint,
@ScheduleListId GUID
)
AS
DECLARE @error int,
@rowcount int,
--------------- Change Start ---------------
@xml xml,
@CurrentDate date,
@ForLOfTheMonth date,-- First or Last Day of the Month
@count int,
@count1 int,
@Monthly_StartDt date,
@Monthly_Interval int,
@Monthly_MonthDayList int,-- This might not be needed
@MonthlyRelative_StartDt date,
@MonthlyRelative_Interval int,
@MonthlyRelative_RelativeWeekDay nvarchar(3),
@MonthlyRelative_RelativeInterval nvarchar(6),
@Weekly_StartDt date,
@Weekly_Interval int,
@Weekly_WeekDayList nvarchar(20)-- This might not be needed
set @xml =CONVERT(xml,SUBSTRING(@schedule,42,LEN(@schedule)-41))
set @CurrentDate =GETDATE()
select @Weekly_StartDt = @xml.value('(//*[local-name()="Weekly"]/@StartDt)[1]','date')
select @Weekly_Interval = @xml.value('(//*[local-name()="Weekly"]/@Interval)[1]','int')
select @Weekly_WeekDayList = @xml.value('(//*[local-name()="Weekly"]/@WeekDayList)[1]','nvarchar(20)')-- This might not be needed
select @Monthly_StartDt = @xml.value('(//*[local-name()="Monthly"]/@StartDt)[1]','date')
select @Monthly_Interval = @xml.value('(//*[local-name()="Monthly"]/@Interval)[1]','int')
select @Monthly_MonthDayList = @xml.value('(//*[local-name()="Monthly"]/@MonthDayList)[1]','int')-- This might not be needed
select @MonthlyRelative_StartDt = @xml.value('(//*[local-name()="MonthlyRelative"]/@StartDt)[1]','date')
select @MonthlyRelative_Interval = @xml.value('(//*[local-name()="MonthlyRelative"]/@Interval)[1]','int')
select @MonthlyRelative_RelativeWeekDay = @xml.value('(//*[local-name()="MonthlyRelative"]/@RelativeWeekDay)[1]','nvarchar(3)')
select @MonthlyRelative_RelativeInterval = @xml.value('(//*[local-name()="MonthlyRelative"]/@RelativeInterval)[1]','nvarchar(6)')
If @Monthly_StartDt isNOTNULL
while @Monthly_StartDt < @Currentdate
Set @Monthly_StartDt =DATEADD(MONTH,@Monthly_Interval,@Monthly_StartDt)
if @Weekly_StartDt isNOTNULL
if @Weekly_Interval > 1
while @Weekly_StartDt < @CurrentDate
set @Weekly_StartDt =DATEADD(DAY,@Weekly_Interval * 7,@Weekly_StartDt)
If @MonthlyRelative_StartDt isNOTNULL
Begin
set @ForLOfTheMonth =DATEADD(dd,-(DAY(DATEADD(mm,1,@Currentdate))-1)-(DAY(@Currentdate)-DAY(DATEADD(mm,1,@Currentdate))),@Currentdate)
if @MonthlyRelative_RelativeInterval ='Last'
begin
set @ForLOfTheMonth =DATEADD(Month,1,@ForLOfTheMonth)
set @ForLOfTheMonth =DATEADD(dd,-(DAY(DATEADD(mm,1,@ForLOfTheMonth))-1)-(DAY(@ForLOfTheMonth)-DAY(DATEADD(mm,1,@ForLOfTheMonth))),@ForLOfTheMonth)
set @ForLOfTheMonth =DATEADD(day,-1,@ForLOfTheMonth)
end
while @MonthlyRelative_StartDt < @CurrentDate
begin
while @MonthlyRelative_StartDt < @ForLOfTheMonth
Set @MonthlyRelative_StartDt =DATEADD(MONTH,@MonthlyRelative_Interval,@MonthlyRelative_StartDt)
if @MonthlyRelative_RelativeInterval ='Last'
Begin
set @MonthlyRelative_StartDt =DATEADD(Month,1,@ForLOfTheMonth)
set @MonthlyRelative_StartDt =DATEADD(dd,-(DAY(DATEADD(mm,1,@MonthlyRelative_StartDt))-1)-(DAY(@MonthlyRelative_StartDt)-DAY(DATEADD(mm,1,@MonthlyRelative_StartDt))),@MonthlyRelative_StartDt)
set @MonthlyRelative_StartDt =DATEADD(day,-1,@MonthlyRelative_StartDt)
End
else
set @MonthlyRelative_StartDt =DATEADD(dd,-(DAY(DATEADD(mm,1,@MonthlyRelative_StartDt))-1)-(DAY(@MonthlyRelative_StartDt)-DAY(DATEADD(mm,1,@MonthlyRelative_StartDt))),@MonthlyRelative_StartDt)
if @MonthlyRelative_RelativeInterval ='First'or @MonthlyRelative_RelativeInterval ='Last'
set @count = 1
if @MonthlyRelative_RelativeInterval ='Second'
set @count = 2
if @MonthlyRelative_RelativeInterval ='Third'
set @count = 3
if @MonthlyRelative_RelativeInterval ='Fourth'
set @count = 4
set @count1 = @count
if @MonthlyRelative_RelativeWeekDay ='Day'
if @count <> 1
Begin
set @MonthlyRelative_StartDt =DATEADD(dd,@count-1,@MonthlyRelative_StartDt)
set @count = 0
End
Else
set @count = 0
while @count <> 0
begin
ifsubstring(DATENAME(dw,@MonthlyRelative_StartDt),1,2)= @MonthlyRelative_RelativeWeekDay
set @count = @count - 1
if @count <> 0
if @MonthlyRelative_RelativeInterval <>'Last'
set @MonthlyRelative_StartDt =DATEADD(day,1,@MonthlyRelative_StartDt)
else
set @MonthlyRelative_StartDt =DATEADD(day,-1,@MonthlyRelative_StartDt)
end
if @MonthlyRelative_StartDt < @CurrentDate
begin
set @MonthlyRelative_StartDt =DATEADD(MONTH,@MonthlyRelative_Interval,@MonthlyRelative_StartDt)
set @MonthlyRelative_StartDt =DATEADD(dd,-(DAY(DATEADD(mm,1,@MonthlyRelative_StartDt))-1)-(DAY(@MonthlyRelative_StartDt)-DAY(DATEADD(mm,1,@MonthlyRelative_StartDt))),@MonthlyRelative_StartDt)
if @MonthlyRelative_RelativeInterval ='Last'
begin
set @MonthlyRelative_StartDt =DATEADD(Month,1,@MonthlyRelative_StartDt)
set @MonthlyRelative_StartDt =DATEADD(dd,-(DAY(DATEADD(mm,1,@MonthlyRelative_StartDt))-1)-(DAY(@MonthlyRelative_StartDt)-DAY(DATEADD(mm,1,@MonthlyRelative_StartDt))),@MonthlyRelative_StartDt)
set @MonthlyRelative_StartDt =DATEADD(day,-1,@MonthlyRelative_StartDt)
end
set @count = @count1
if @MonthlyRelative_RelativeWeekDay ='Day'
if @count <> 1
Begin
set @MonthlyRelative_StartDt =DATEADD(dd,@count-1,@MonthlyRelative_StartDt)
set @count = 0
End
Else
set @count = 0
while @count <> 0
begin
ifsubstring(DATENAME(dw,@MonthlyRelative_StartDt),1,2)= @MonthlyRelative_RelativeWeekDay
set @count = @count - 1
if @count <> 0
if @MonthlyRelative_RelativeInterval <>'Last'
set @MonthlyRelative_StartDt =DATEADD(day,1,@MonthlyRelative_StartDt)
else
set @MonthlyRelative_StartDt =DATEADD(day,-1,@MonthlyRelative_StartDt)
end
end
end
End
if @Monthly_StartDt isNOTNULL
set @xml.modify ('replace value of (//*[local-name()="Monthly"]/@StartDt)[1] with sql:variable("@Monthly_StartDt")')
If @MonthlyRelative_StartDt isNOTNULL
set @xml.modify ('replace value of (//*[local-name()="MonthlyRelative"]/@StartDt)[1] with sql:variable("@MonthlyRelative_StartDt")')
if @Weekly_StartDt isNOTNULL
set @xml.modify ('replace value of (//*[local-name()="Weekly"]/@StartDt)[1] with sql:variable("@Weekly_StartDt")')
set @Schedule ='<?xml version="1.0" encoding="utf-16"?> '+CONVERT(nvarchar(max),@xml)
--------------- Change end ---------------
SET @rowcount = 0
SET @error = 0
SETNOCOUNTON
UPDATE dbo.tbl_IM_UserSchedule
SET ProtectedGroupID = @ProtectedGroupID,
JobType = @JobType,
Schedule = @Schedule,
Immediacy = @Immediacy,
TimeOffset = @TimeOffset,
MaxDuration = @MaxDuration,
ScheduleListId = @ScheduleListId
WHERE ScheduleID = @ScheduleID
SELECT @error =@@ERROR, @rowcount =@@ROWCOUNT
IF (@error = 0 AND @rowcount = 0)
BEGIN
INSERTINTO dbo.tbl_IM_UserSchedule
(
ScheduleID,
ProtectedGroupID,
JobType,
Schedule,
Immediacy,
TimeOffset,
MaxDuration,
ScheduleListId
)
values
(
@ScheduleID,
@ProtectedGroupID,
@JobType,
@Schedule,
@Immediacy,
@TimeOffset,
@MaxDuration,
@ScheduleListId
)
SET @error =@@ERROR
END
SETNOCOUNTOFF
RETURN @error
=====
Wilson Souza | 高级支持呈报工程师 | 管理和安全部