SQL Agent如何以及何时更新next_run_date / next_run_time值?


13

我一直在研究T-SQL中的代码,以便使用msdb数据库中的sp_add_jobschedule proc将新日程表添加到SQL Agent作业中。当我添加新计划(通常在特定日期/时间运行一次)并立即查看sysjobschedules和sysschedules中的值时,我可以看到已添加新计划并将其绑定到SQL Agent的job_id工作。但是,next_run_date和next_run_time的值中有0。当我回来在2或3分钟内再次查看它们时,它们中仍然显示0。但是,当我在5或10分钟后再回来时,它现在可以正确显示与下一次计划运行相对应的日期和时间值。

所以我的问题是:

  • 这些值多久更新一次?
  • 更新这些值的过程是什么?
  • 如果我要添加一个计划,例如未来1分钟,那么这是否意味着作业尚未运行,因为next_run_date / time还没有更新?

我用于添加新时间表的代码示例:

exec msdb.dbo.sp_add_jobschedule @job_id = @jobID
                    , @name = @JobName
                    , @enabled = 1
                    , @freq_type = 1
                    , @freq_interval = 0
                    , @freq_subday_type = 0
                    , @freq_subday_interval = 0
                    , @freq_relative_interval = 0
                    , @freq_recurrence_factor = 0
                    , @active_start_date = @ScheduleRunDate
                    , @active_end_date = 99991231
                    , @active_start_time = @ScheduleRunTime
                    , @active_end_time = 235959

其中@jobID是二进制(16),其中保存有问题的作业的job_id,@ ScheduleRunDate和@ScheduleRunTime分别是带有日期和时间的INT。


3
通过SQL Agent作业进行更新。通过SQL Agent作业进行更新。递归者团结起来!
亚伦·伯特兰

1
道歉。有一阵子没有机会回来。
BBlake 2012年

Answers:


16

简短答案

看起来其中的数据msdb.dbo.sysjobschedules是由SQL Agent中的后台线程SQLAgent - Schedule Saver每20分钟更新一次的,标识为,每20分钟一次(或者,如果xp_sqlagent_notify未调用且在此期间未运行任何作业,则更不频繁)。

有关更准确的信息,请参阅next_scheduled_run_date中的msdb.dbo.sysjobactivity。每当更改作业或运行作业时,都会实时更新。另外,以sysjobactivity正确的方式(作为日期时间列)存储数据,比起那些愚蠢的INT而言,使用起来要容易得多。

这是简短的答案:

sysjobschedules最多可能需要20分钟才能反映出事实。但是,sysjobactivity将始终是最新的。如果您想了解更多有关此事的详细信息,或者我想如何解决...


长答案

如果您想跟踪兔子一会儿,请在调用时sp_add_jobschedule,将以下一系列事件设置为运动:

msdb.dbo.sp_add_jobschedule == calls ==> msdb.dbo.sp_add_schedule
                                         msdb.dbo.sp_attach_schedule

msdb.dbo.sp_attach_schedule == calls ==> msdb.dbo.sp_sqlagent_notify

msdb.dbo.sp_sqlagent_notify == calls ==> msdb.dbo.xp_sqlagent_notify

现在,我们无法再追赶兔子了,因为我们无法真正窥视出什么xp_sqlagent_notify。但是我认为我们可以假定此扩展过程与代理服务交互,并告诉我们此特定作业和计划已发生更改。通过运行服务器端跟踪,我们可以立即看到SQL Agent调用了以下动态SQL:

exec sp_executesql N'DECLARE @nextScheduledRunDate DATETIME 
  SET @nextScheduledRunDate = msdb.dbo.agent_datetime(@P1, @P2) 
  UPDATE msdb.dbo.sysjobactivity 
    SET next_scheduled_run_date = @nextScheduledRunDate 
    WHERE session_id = @P3 AND job_id = @P4',
N'@P1 int,@P2 int,@P3 int,@P4 uniqueidentifier',
20120819,181600,5,'36924B24-9706-4FD7-8B3A-1F9F0BECB52C'

似乎sysjobactivity是立即更新,并且sysjobschedules仅按计划更新。如果我们将新时间表更改为每天一次,例如

@freq_type=4, 
@freq_interval=1, 
@freq_subday_type=1, 
@freq_subday_interval=0, 
@freq_relative_interval=0, 
@freq_recurrence_factor=1, 

我们仍然看到立即更新到sysjobactivity上面,然后在作业完成后又进行了一次更新。各种更新来自SQL Agent的后台和其他线程,例如:

SQLAgent - Job Manager
SQLAgent - Update job activity
SQLAgent - Job invocation engine
SQLAgent - Schedule Saver

后台线程(“ Schedule Saver”线程)最终出现并进行了更新sysjobschedules;从我最初的调查来看,这似乎是每20分钟一次,并且仅在xp_sqlagent_notify自上次运行以来由于对作业进行了更改而被调用时才发生(我没有执行任何进一步的测试来查看如果一项作业已经发生,将会发生什么情况)如果“ Schedule Saver”线程同时更新了这两个变量,并且又运行了另一个线程-我怀疑是必须的,但将其留给读者练习。

我不确定20分钟的周期是否与SQL Agent启动时,午夜或与计算机有关的时间有所偏移。在同一台物理服务器上的两个不同实例上,“ Schedule Saver”线程sysjobschedules几乎在同一时间更新了两个实例上的“时间表保护程序”线程-分别在18:31:37&18:51:37和18:31:39& 18:51:39在另一边 我没有在这些服务器上同时启动SQL Server代理,但是极有可能启动时间恰好偏移了20分钟。我对此表示怀疑,但是我现在没有时间通过​​重新启动其中一个代理程序并等待更多更新来确认。

我知道是谁做的,以及它发生的时间,因为我在其中放置了一个触发器并捕获了它,以防万一在跟踪中找不到它,或者我无意中将其过滤掉了。

CREATE TABLE dbo.JobAudit
(
  [action] CHAR(1),
  [table] CHAR(1), 
  hostname SYSNAME NOT NULL DEFAULT HOST_NAME(), 
  appname SYSNAME  NOT NULL DEFAULT PROGRAM_NAME(),
  dt DATETIME2     NOT NULL DEFAULT SYSDATETIME()
);

CREATE TRIGGER dbo.schedule1 ON dbo.sysjobactivity FOR INSERT
AS
  INSERT dbo.JobAudit([action], [table] SELECT 'I', 'A';
GO
CREATE TRIGGER dbo.schedule2 ON dbo.sysjobactivity FOR UPDATE
AS
  INSERT dbo.JobAudit([action], [table] SELECT 'U', 'A';
GO
CREATE TRIGGER dbo.schedule3 ON dbo.sysjobschedules FOR INSERT
AS
  INSERT dbo.JobAudit([action], [table] SELECT 'I', 'S';
GO
CREATE TRIGGER dbo.schedule4 ON dbo.sysjobschedules FOR UPDATE
AS
  INSERT dbo.JobAudit([action], [table] SELECT 'U', 'S';
GO

就是说,不难理解标准的痕迹,这甚至是作为非动态DML实现的:

UPDATE msdb.dbo.sysjobschedules 
  SET next_run_date = 20120817, 
      next_run_time = 20000 
 WHERE (job_id = 0xB87B329BFBF7BA40B30D9B27E0B120DE 
 and schedule_id = 8)

如果您想运行更多经过过滤的跟踪以跟踪一段时间内的这种行为(例如,通过SQL Agent重新启动而不是按需持久化),则可以运行appname = 'SQLAgent - Schedule Saver'...

因此,我认为,如果您想立即知道下一个运行时间,请查看sysjobactivity,而不是sysjobschedules。当活动发生时或由通知时,此表由Agent或其后台线程(“更新作业活动”,“作业管理器”和“作业调用引擎”)直接更新xp_sqlagent_notify

但是请注意,将两个表中的任何一个都变得非常简单-因为没有防止从这些表中删除数据的保护措施。(因此,例如,如果您决定清理,则可以轻松地从活动表中删除该作业的所有行。)在这种情况下,我不确定SQL Server代理如何获取或保存下一个运行日期。也许以后我有空的时候值得进一步调查...


如果作业尚未第一次运行,则sysjobschedules(最终)将显示next_run_date和next_run_time的正确值,而sysjobactivity.next_scheduled_run_date保持为空,直到第一次执行。从sysjobactivity获取值时,您需要在按job_id分组并获得MAX(next_scheduled_run_date)的子查询中执行此操作。
马克·弗里曼2014年

0

msdb.dbo.sp_help_job总是看起来会返回正确的实际next_run_date/ next_run_time

它使用sp_get_composite_job_info,执行以下调用以实际检索next_run_date/time

      IF ((@@microsoftversion / 0x01000000) >= 8) -- SQL Server 8.0 or greater
        INSERT INTO @xp_results
        EXECUTE master.dbo.xp_sqlagent_enum_jobs @can_see_all_running_jobs, @job_owner, @job_id
      ELSE
        INSERT INTO @xp_results
        EXECUTE master.dbo.xp_sqlagent_enum_jobs @can_see_all_running_jobs, @job_owner

由于sysjobschedule似乎不可靠,因此我将使用sp_help_job

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.