web-dev-qa-db-ja.com

SQLエージェントは、いつどのようにしてnext_run_date / next_run_time値を更新しますか?

Msdbデータベースのsp_add_jobschedule procを使用して、SQLエージェントジョブに新しいスケジュールを追加するためにT-SQLのコードに取り組んでいます。新しいスケジュール(通常は特定の日時に1回実行)を追加し、すぐにsysjobschedulesおよびsysschedulesの値を確認すると、新しいスケジュールが追加され、SQLエージェントの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は問題のジョブのjob_idを保持するbinary(16)で、@ ScheduleRunDateと@ScheduleRunTimeはそれぞれ日付と時刻を含むINTです。

13
BBlake

短い答え

msdb.dbo.sysjobschedulesのデータは、SQLエージェントのバックグラウンドスレッドによって20分ごとに更新されているようです(SQLAgent - Schedule Saverが20分ごと(またはxp_sqlagent_notifyが呼び出されておらず、ジョブもない場合はそれほど頻繁ではありません)。その間に実行されました)。

より正確な情報については、next_scheduled_run_datemsdb.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が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は定期的に更新されるようです。新しいスケジュールを1日に1回に変更するとします。

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

上記のようにsysjobactivityがすぐに更新され、ジョブが完了した後、別の更新が表示されます。さまざまな更新は、SQLエージェント内のバックグラウンドおよびその他のスレッドから行われます。

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

バックグラウンドスレッド(「スケジュールセーバー」スレッド)が最終的に発生し、sysjobschedulesを更新します。私の最初の調査から、これは20分ごとであるように見え、最後に実行してからジョブに変更が加えられたためにxp_sqlagent_notifyが呼び出された場合にのみ発生します(何が起こるかを確認するためのさらなるテストは実行しませんでした) 1つのジョブが変更され、別のジョブが実行された場合、「スケジュールセーバー」スレッドが両方を更新した場合-私はそれを行う必要があると思いますが、読者への演習として残します)。

20分サイクルがSQLエージェントの開始時から、または真夜中から、またはマシン固有の何かからオフセットされているかどうかはわかりません。同じ物理サーバー上の2つの異なるインスタンスで、「スケジュールセーバー」スレッドが両方のインスタンスでsysjobschedulesをほぼ正確に同時に更新しました-1つで18:31:37と18:51:37。 18:31:39と18:51:39。これらのサーバーで同時にSQL Serverエージェントを起動しませんでしたが、起動時間が20分ずれる可能性があります。疑わしいですが、今のところ、そのうちの1つでエージェントを再起動して更新が行われるのを待つことで確認する時間はありません。

トレースでそれが見つからなかった場合、または誤ってそれをフィルターで除外した場合に備えて、トリガーをそこに配置してキャプチャしたため、誰がそれを実行したか、いつ発生したかを知っています。

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エージェントの再起動を通じて永続化)、appname = 'SQLAgent - Schedule Saver'...のトレースを実行できます。

したがって、次の実行時間をすぐに知りたい場合は、sysjobactivityではなくsysjobschedulesを見てください。このテーブルは、アクティビティが発生するか、xp_sqlagent_notifyから通知されると、エージェントまたはそのバックグラウンドスレッド(「ジョブ更新アクティビティ」、「ジョブマネージャ」、「ジョブ呼び出しエンジン」)によって直接更新されます。

ただし、これらのテーブルからデータを削除することに対する保護機能がないため、どちらのテーブルも簡単にマックアップできることに注意してください。 (たとえば、クリーンアップすることを決定した場合は、そのジョブのすべての行をアクティビティテーブルから簡単に削除できます。)この場合、SQL Serverエージェントが次の実行日を取得または保存する方法が正確にわかりません。おそらく、私が自由時間があるときに、後でさらに調査する価値があります...

16
Aaron Bertrand

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を使用します。

0